6

How would a function FunctionQ look like, maybe in a way I can even specify the number of arguments allowed?

Karsten W.
  • 17,826
  • 11
  • 69
  • 103
  • 1
    I don't quite understand your question. Do you mean that you want to write a function FunctionQ[input_] which returns True if input is a function, false otherwise ? What form does input take ? – High Performance Mark Sep 17 '10 at 16:20
  • 1
    @Mark: The first question I'd answer with Yes. The form the input would take is a Mathematica expression... – Karsten W. Sep 17 '10 at 20:18

3 Answers3

12

I really feel bad posting after Simon and Daniel, but their codes fail on non-functions which are not symbols. Checking for that and adding a check for builtins via NumericFunction, as suggested by Simon, we arrive at something like

FunctionQ[_Function | _InterpolatingFunction | _CompiledFunction] = True;
FunctionQ[f_Symbol] := Or[
  DownValues[f] =!= {}, 
  MemberQ[ Attributes[f], NumericFunction ]]
FunctionQ[_] = False;

which should work in some (sigh) real-world cases

In[17]:= 
FunctionQ/@{Sin,Function[x,3x], Compile[x,3 x],Interpolation[Range[5]],FunctionQ,3x,"a string", 5}
Out[17]= {True,True,True,True,True,False,False,False}

If you know the signature of the function you are looking for (i.e. how many arguments and of what type), I would agree with Simon that the way to go is duck typing: Apply the function to typical arguments, and look for valid output. Caching might be worthwhile:

AlternativeFunctionQ[f_]:=AlternativeFunctionQ[f]=
  With[{TypicalArgs={1.0}},NumericQ[Apply[f,TypicalArgs]]];

In[33]= AlternativeFunctionQ/@{Sin,Function[x,3x], Compile[x, 3x],Interpolation[Range[5]],FunctionQ,3x,"a string", 5}
Out[34]= {True,True,True,True,False,False,False,False} 
Janus
  • 5,421
  • 2
  • 26
  • 37
  • Definitely don't feel bad! That's SO's MO: build on each other to come up with the best possible answer. Great work on this, btw! – dreeves Sep 23 '10 at 12:43
  • I was just looking over this again, and I noticed that `FunctionQ` is not found to be a 1 var function by `AlternativeFunctionQ`. So, while it is effective at finding numerical functions, it cannot find other types. – rcollyer Nov 05 '10 at 19:54
  • @rcollyer: The `AlternativeFunctionQ` was meant as an example: The typical arguments as well as the valid output should be adapted to the case at hand. I guess this is not very elegant from an architecture point of view, but it often fits the bill quite well. – Janus Nov 08 '10 at 02:04
  • I love how I can bounce around SO and continue to find new and interesting answers like this. +1 and cheers for SO. – telefunkenvf14 May 18 '11 at 19:36
4

As Daniel said, his test (which probably should read)

FunctionQ[x_] := Head[x] === Function || DownValues[x] =!= {}

Is quick and dirty. It will fail for built in functions, e.g. FunctionQ[Sin] will return False (Many built-in functions will be caught by checking for the Attribute NumericFunction). It will also fail for things like f[x_][y_] etc... It should probably also test UpValues, SubValues and maybe NValues (see here for their meanings).

This problem was discussed in this thread. Many useful ideas are in this thread - eg ways to find the number of arguments that some functions can take, but there was no real consensus reached in the discussion.

I think that the best approach is a kind of duck typing. You probably know how many and what type of arguments you want your function to take, so test it with ValueQ. Then make sure that you catch errors using Check.

EDIT: Another comp.soft-sys.math.mathematica thread.

Simon
  • 14,631
  • 4
  • 41
  • 101
2

Here's something quick and dirty which may do what you need:

FunctionQ[x_] := Head[x] == Function || DownValues[x] =!= {}
dreeves
  • 26,430
  • 45
  • 154
  • 229