12

Suppose I want to construct something like

Array[#1^#2 == 3 &, {3, 3}] 

And now I want to replace the "3" with a variable. I can do, for example:

f[x_] := Array[#1^#2 == x &, {x, x}]  

The question is: Is there a way using only slots and & as the functional notation?

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
  • 4
    This got me looking at the docs for `Slot` (+1). The recursive, pure function definition for the factorial: `f = If[#1 == 1, 1, #1 #0[#1 - 1]]&` is pretty slick! – Simon Feb 07 '11 at 11:51
  • @Simon Yep. That #0 worried me too :D – Dr. belisarius Feb 07 '11 at 11:57
  • 1
    @Simon, I've never read the Slot documentation that closely before. The `#0` is definitely bothersome, and potentially very useful. – rcollyer Feb 07 '11 at 13:21
  • @belisarius does a solution have to use `Array`? I think the function `Table` could meet your current requirements? – dbjohn Feb 07 '11 at 13:41
  • @dbjohn I want to come up with a solution that works for Select[], Sort[], etc. So I think a `Table[k^j == #, {k, #}, {j, #}] &[3]` is not useful _unless you are thinking of another way!_ – Dr. belisarius Feb 07 '11 at 13:47
  • +1 for `#0`, now I have some experimenting to do. – Timo Feb 07 '11 at 14:02
  • @belisarius, I might have another way, could you give an example usage or output of this function maybe by editing your question, just so I am sure I know what you want. Suppose we use the Function `Select` on a list like {0,1,2,3} with the criteria `Array[#1^#2 == x &, {x, x}]` where x is each element in the list. For 3 this produces `{{False, False, False}, {False, False, False}, {True, False, False}}`. Is it correct you expecting the element to be selected when all cases are true like `{True, True, True etc }`? – dbjohn Feb 07 '11 at 19:02
  • 2
    @dbjohn For example the equivalent to `f[x_] := Sort[x, #1 > #2 Last@x &]` Such as `f[{7, 3, 2}]={7,2,3}` – Dr. belisarius Feb 07 '11 at 19:09

5 Answers5

9

Not really the answer to the original question, but I noticed that many people got interested in #0 stuff, so here I put a couple of non-trivial examples, hope they will be useful.

Regarding the statement that for nested functions one should use functions with named arguments: while this is generally true, one should always keep in mind that lexical scoping for pure functions (and generally) is emulated in Mathematica, and can be broken. Example:

In[71]:= 
Clear[f,g];
f[fun_,val_]:=val/.x_:>fun[x];
g[fn_,val_]:=f[Function[{x},fn[#1^#2==x&,{x,x}]],val];
g[Array,3]

During evaluation of In[71]:= Function::flpar: Parameter specification {3} in  
   Function[{3},Array[#1^#2==3&,{3,3}]] should be a symbol or a list of symbols. >>
During evaluation of In[71]:= Function::flpar: Parameter specification {3} in 
   Function[{3},Array[#1^#2==3&,{3,3}]] should be a symbol or a list of symbols. >>

Out[74]= Function[{3},Array[#1^#2==3&,{3,3}]][3]

This behavior has to do with the intrusive nature of rule substitutions - that is, with the fact that Rule and RuleDelayed don't care about possible name collisions between names in scoping constructs which may be present in expressions subject to rule applications, and names of pattern variables in rules. What makes things worse is that g and f work completely fine when taken separately. It is when they are mixed together, that this entanglement happens, and only because we were unlucky to use the same pattern variable x in the body of f, as in a pure function. This makes such bugs very hard to catch, while such situations do happen sometimes in practice, so I'd recommend against passing pure functions with named arguments as parameters into higher-order functions defined through patterns.

Edit:

Expanding a bit on emulation of the lexical scoping. What I mean is that, for example, when I create a pure function (which is a lexical scoping construct that binds the variable names in its body to the values of passed parameters), I expect that I should not be able to alter this binding after I have created a function. This means that, no matter where I use Function[x,body-that-depends-on-x], I should be able to treat it as a black box with input parameters and resulting outputs. But, in Mathematica, Function[x,x^2] (for instance) is also an expression, and as such, can be modified like any other expression. For example:

In[75]:= 
x = 5;
Function[Evaluate[x],x^2]

During evaluation of In[75]:= Function::flpar: Parameter specification 5 in Function[5,x^2] should 
  be a symbol or a list of symbols. >>
Out[76]= Function[5,x^2]

or, even simpler (the essence of my previous warning):

In[79]:= 1/.x_:>Function[x,x^2]

During evaluation of In[79]:= Function::flpar: Parameter specification 1 in Function[1,1^2] should 
  be a symbol or a list of symbols. >>

Out[79]= Function[1,1^2]

I was bitten by this last behavior a few times pretty painfully. This behavior was also noted by @WReach at the bottom of his post on this page - obviously he had similar experiences. There are other ways of breaking the scope, based on exact knowledge of how Mathematica renames variables during the conflicts, but those are comparatively less harmful in practice. Generally, I don't think these sorts of things can be avoided if one insists on the level of transparency represented by Mathematica expressions. It just seems to be "over-transparent" for pure functions (and lexical scoping constructs generally), but on the other hand this has its uses as well, for example we can forge a pure function at run-time like this:

In[82]:= Block[{x},Function@@{x,Integrate[HermiteH[10,y],{y,0,x}]}]

Out[82]= Function[x,-30240 x+100800 x^3-80640 x^5+23040 x^7-2560 x^9+(1024 x^11)/11]

Where the integral is computed only once, at definition-time (could use Evaluate as well). So, this looks like a tradeoff. In this way, the functional abstraction is better integrated into Mathematica, but is leaky, as @WReach noted. Alternatively, it could have been "waterproof", but perhaps for the price of being less exposed. This was clearly a design decision.

Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • @Leonid Thanks for the nice explanation and examples. I think this http://stackoverflow.com/questions/4198961/what-is-in-your-mathematica-tool-bag is a good place to post it, and perhaps you may contribute there with other ideas too. – Dr. belisarius Feb 07 '11 at 15:32
  • @belisarius Thanks, I was not sure where to put things like this. Do you think it is a good idea to relocate this post there and link to it from here? And if yes, how do I relocate the post - should I just delete it here and post there? – Leonid Shifrin Feb 07 '11 at 15:40
  • @Leonid I think you may post it there and leave here an answer basically with the first paragraph and a pointer to the other answer. I (hopefully "We") would like to collect in the _toolbag_ post useful recipes and clarifying concepts. I'm sure you may help a lot with that! Also note that the in the question we are collecting an index, if you already have edit rights, feel free to maintain the index updated, or we'll do that for you if you still can't. – Dr. belisarius Feb 07 '11 at 15:48
  • @belisarius Thanks for the explanation - done for the post. As for the index - you mean the list of books etc at the top of the page, right? - I will contribute a few entries soon. – Leonid Shifrin Feb 07 '11 at 16:16
  • @Leonid 1) Great! 2) Yes, I mean that index. 3) Could you explain further **lexical scoping is emulated**? – Dr. belisarius Feb 07 '11 at 16:27
  • @belisarius I have just edited my answer, expanding on this topic. One probably needs more examples to get a better picture here, but this at least illustrates some aspects of the problem. – Leonid Shifrin Feb 07 '11 at 17:02
  • @Leonid Thanks a lot for your time and effort. The Mma (functional + rewrite rules) mixed approach is practical but difficult to tame sometimes. – Dr. belisarius Feb 07 '11 at 17:18
  • @belisarius I was lucky to have Mma as a hobby for a few years before I started to heavily use it at work. Otherwise, there would be no way for me to know this sort of details - when you need it for work, you have no time to dig into it deep enough, unless you know precisely what to look for. But this doesn't have to be like that - I think that the bits of information we accumulate here will make it easier for others. And SO seems to be a great medium for it. – Leonid Shifrin Feb 07 '11 at 17:45
4

How about

Map[Last, #] & /@ Array[#1^#2 == #3 &, {#, #, #}] &[3]

Horrendously ugly element extraction, and very interestingly Map[Last, #]& gives me a different result than Last /@. Is this due to the fact that Map has different attributes than &?

Timo
  • 4,246
  • 6
  • 29
  • 42
3

How about With[{x = #1}, Array[#1^#2 == x &, {x, x}]] &?

Life
  • 449
  • 3
  • 9
  • 1
    +1. Yes, this is one of the possibilities. Actually, I use this construct quite often when I need to nest slot-based functions, and I know that some other folks use this too. – Leonid Shifrin Jun 22 '13 at 16:56
  • Named argument is somehow necessary. Well, anonymous function has limitations when facing nest construct. Good lesson for me newbie! – Life Jun 22 '13 at 17:05
  • `Sequence[x, Array[#1^#2 == x &, {x, x}]] &` is also a skill. – Life Jun 22 '13 at 17:12
3

I guess you know what the documentation says about nested pure functions.

Use explicit names to set up nested pure functions (for example):

Function[u, Function[v, f[u, v]]][x]

Anyway, here's the best I could come up with without following the above advice:

f[x_] := Array[#1^#2 == x &, {x, x}]
g = Array[With[{x = #}, #1^#2 == x &], {#, #}] &

g is functionally identical to your original f, but is not really better than the recommended

h = Function[x, Array[#1^#2 == x &, {x, x}]]
Simon
  • 14,631
  • 4
  • 41
  • 101
  • Yes, I'm aware of the docs :D. Nevertheless this case, where I want to use the same variable value for the inner and outer parms seems prone to some hacking. – Dr. belisarius Feb 07 '11 at 11:51
  • 1
    @belisarius: Actually, I didn't notice it was you who asked the question. Sorry! – Simon Feb 07 '11 at 11:53
  • next time I'll include a [`nice portrait`](http://www.life.com/image/53368173) to ease recognition :D – Dr. belisarius Feb 07 '11 at 12:07
2

Perhaps

Array[#1^#2 &, {#, #}] /. i_Integer :> i == # &[3]

Or

Thread /@ Thread[# == Array[#1^#2 &, {#, #}]] &[3]
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125