Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Suppose that I define

f = Function[#1 + #2];

And I want to define a new function, argsNum, so that it returns a number of arguments of input function:

argsNum[f]
2

since f has 2 slots, #1 and #2.

I found the good Q&A, Function that counts the number of arguments of other functions but it does not work on functions of form like Function[#1 + #2] or etc.

It works on Function[{x, y}, x + y]. But I need a function that works on slots.

How can I modify the function in above link or define a new function which also works on slots?

share|improve this question
1  
Count[f, _Slot, ∞] –  m_goldberg Aug 5 at 16:13
2  
Can you explain why you need this? For reasons that Mr. Wizard mentions in his answer, I think that such a function wouldn't be needed most of the time. But of course I might be wrong. If you explain why you need this, we might be able to give a better answer (and people would also be more interested in the question). As MrW. said, pure functions can take any number of arguments that is greater than the largest slot number. The new named slots introduced in v10 complicate this further (e.g. #one + #two &) –  Szabolcs Aug 5 at 17:04
    
@m_goldberg What about repeated use of the same Slot? Even in the most narrow application that won't work. –  Mr.Wizard Aug 5 at 17:13
    
@Szabolcs Actually I am making a function with one argument and that argument would be another function that I want to check. To make this explanation be simple, GoodFuncQ[func] returns true if func has exactly 2 arguments, otherwise, false. –  Analysis Aug 5 at 19:41
1  
@Analysis Thanks for the Accept. Considering your application I think it would be better to Check for error messages during application, as I suspect such tests cannot be written robustly for all possible input, at least not without unreasonable overhead. Perhaps one of the argument testing frameworks I presented here will be useful to you. –  Mr.Wizard Aug 5 at 20:23

4 Answers 4

up vote 13 down vote accepted

Your question is not well specified for several reasons:

  1. Pure functions accept a flexible number of arguments

    #1 + #2 &[a, b, c, d]
    
    a + b
    
  2. It is common for some arguments to not be used:

    #1 + #3 & @@ {a, b, c, d}
    
    a + c
    
  3. SlotSequence includes all arguments after the given position:

    +##3 & @@ {a, b, c, d}
    
    c + d
    

Without clarifying how each of these cases is to be counted it is impossible to give a general answer.

share|improve this answer
2  
+##&, seriously? Not that it doesn't amuse me, it's not something I would consider writing. (+1) –  rcollyer Aug 5 at 17:56
    
@rcollyer It's a long-time favorite of mine. :D Actually I like it so much I wish there were more short-form operators that would work this way. (Monadic.) –  Mr.Wizard Aug 5 at 18:00
    
Mr.Wizard, can you explain how the evaluation of (3) works? Why is + special in this respect and are there other operators which work in the same way? –  sebhofer Aug 6 at 14:31
    
@sebhofer +x parses as Plus[x]. + is special because most Mathematica operators do not have a monadic form; I cannot write *x for Times[x] for example. I can write 1*## which parses as Times[1, ##] however. I wrote some about this here; please take a look at that before asking follow-up questions. –  Mr.Wizard Aug 6 at 17:02
    
That makes sense. Don't know why I didn't think of that. Sorry. I only realized that *## doesn't work (and -## neither for that matter). Thanks for the link, I wasn't aware of that post. I looked for "monad" but came up empty handed. –  sebhofer Aug 6 at 18:32

Try

Length@Union@Cases[f, _Slot, Infinity]
2

The Union eliminates duplicates

share|improve this answer
3  
This doesn't work for cases like f = MapIndexed[#1^#2 &, #] &, which only has one argument. –  Teake Nutma Aug 5 at 16:29

As @Mr.Wizard has pointed out, your question isn't well specified. However, a pure function always needs a minimum number of arguments, otherwise an error message is thrown:

#1 + #2 &[a]

Function::slotn: Slot number 2 in #1+#2& cannot be filled from (#1+#2&)[a] >>

a + #2

So finding the minimum number of required arguments of a pure function is a well-posed problem. We can solve it by simply detecting if the above error message is thrown for a given number of arguments; if so, we increase the number of arguments and try again:

EnoughArgumentsQ[f_Function, numargs_Integer] := 
  Quiet[
    Check[
      f @@ ConstantArray[Null, numargs]; True, 
      False, 
      Function::slotn
    ],
    Function::slotn
  ];

MinNumberOfArguments[f_Function] := 
  NestWhile[# + 1 &, 0, ! EnoughArgumentsQ[f, #] &]

This is not a very efficient approach, but it gets the job done in a pretty robustly fashion:

MinNumberOfArguments /@ {
  (* 0 *)
  Function[{}, Null],
  (* 1 *)
  MapIndexed[#1^#2 &, #] &,
  (* 2 *)
  #1 + #2 &,
  (* 3 *)
  {#1,#2,#3,##} &,
  (* 4 *)
  #1 + #4 &
}
{0,1,2,3,4}

Mathematica is bound to have a cleverer internal way of determining the minimum number of arguments before it throws the Function::slotn message. If anybody knows how this is done, and if it's accessible, that'd be great :).

share|improve this answer
    
Interesting, because you used Check it even gets the pathological Map[Function[{x, y}, {#1, #2, x, y}] &, #] &. –  rcollyer Aug 5 at 18:06
    
I'd even consider reducing possible run-time of this by first extracting Slot/SlotSequence, i.e. Cases[f, (Slot|SlotSequence)[n_]:> n, -1], and running down from n. Also, I'd use NestWhile as it returns an expression. –  rcollyer Aug 5 at 18:12
    
@rcollyer I'm not sure what you mean with "it returns an expression", but I've changed MinNumberOfArguments to use NestWhile. Thanks for the suggestion! –  Teake Nutma Aug 5 at 18:22
    
While, For, Do, and Scan do not have return values. NestWhile, Table, etc. do. So, your prior code where you had to write While[...]; i is not needed as you can write NestWhile[...] directly. –  rcollyer Aug 5 at 19:00

Here's my approach to determine the number of slots in the pure function without counting those in any sub-functions. That means MapIndexed[#1^#2 &, #1] & (per @TeakeNutma's comment in @JohnMcGee's answer) will only return one slot. I do this by deleting all inner Functions before counting the slots:

Clear[slotCount]
slotCount[f_Function]:=Module[{deleteInner},
deleteInner=Replace[f,_Function:>Sequence[],{1,Infinity}];
DeleteDuplicates[Cases[deleteInner,_Slot,{1,Infinity},Heads->True]]//Length
]

Notes: Using Heads->True for Cases will count slots correctly for cases like #1[#2].


Testing on the example functions in @TeakeNutma's answer

{#,slotCount[#]}&/@{Function[{},Null],MapIndexed[#1^#2&,#]&,#1+#2&,{#1,#2,#3,##}&,#1+#4&}//Grid[#,Frame->All]&

Mathematica graphics

I'm not quite sure #1 + #4 & would have 4 slots (instead of just 2). Below is a modified version of my slotCount function that will return 4:

Clear[slotCountPositionAware]
slotCountPositionAware[f_Function]:=Module[{deleteInner,slotList,maxSlot},
deleteInner=Replace[f,_Function:>Sequence[],{1,Infinity}];
slotList=DeleteDuplicates[Cases[deleteInner,_Slot,{1,Infinity},Heads->True]];
maxSlot[l_List]:=If[l=={},0,l/.Slot[x_]:>x//Max];
maxSlot[slotList]
]

{#,slotCountPositionAware[#]}&/@{Function[{},Null],MapIndexed[#1^#2&,#]&,#1+#2&,{#1,#2,#3,##}&,#1+#4&}//Grid[#,Frame->All]&

Mathematica graphics

share|improve this answer
1  
This method does not like this function for input: Function[{x, y}, {#1, #2, x, y}] &. (That's a function that returns a Function.) –  Mr.Wizard Aug 6 at 20:10
    
I've never seen mixing of named slots and hashtag(?) slots in a Function before. Isn't #1 the same thing as x and #2, y? I'm working on my function to see if it could accommodate such cases, hopefully with some success. –  seismatica Aug 6 at 21:02
    
@Mr.Wizard I've defined another instance of slotCount to handle special functions like the one above. Let me know what you think and where I can improve: Clear[slotCount] slotCount[f:Function[Function[__]]]:= Module[{deleteInner}, deleteInner=Replace[f,_Function:>Sequence[],{2,Infinity}]//First; Cases[deleteInner,_Slot,{1,Infinity},Heads->True]~Join~First@deleteInner//Length ]; slotCount[Function[{x,y},{#1,#2,x,y}]&] –  seismatica Aug 6 at 21:12
    
That example was borrowed from a comment by rcollyer. It is not mixing Slot and named parameters in the same Function, but rather one function nested inside another like this. The outer function using & and # contains the inner function using Function and x/y. Using the entire thing as fn, fn[1, 2] yields Function[{x, y}, {1, 2, x, y}] which is itself a complete function. –  Mr.Wizard Aug 6 at 21:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.