12
$\begingroup$

So say I've got a function g defined as follows:

g[x_Integer] := x + 1
g[s_String] := s <> "!!!"

How would I write a function GetHeads such that GetHeads[g] == {{Integer}, {String}} (i.e. the function returns the heads of all groups of arguments g is defined on)?

$\endgroup$
  • 3
    $\begingroup$ You may start by (DownValues@g)[[All, 1, 1, 1]] $\endgroup$ – Dr. belisarius Sep 17 '14 at 18:11
11
$\begingroup$

If your definitions are exactly like you show, every time, you can use belisarius's method, slightly refined:

g[x_Integer] := x + 1
g[s_String] := s <> "!!!"

(DownValues@g)[[All, 1, 1, 1, 2, 1]]
{Integer, String}

However this is fragile in that it will fail if your definitions are different, e.g.:

g[r_ /; Head[r] === Real] := r + Pi

g[a_List?MatrixQ] := foo[a]

(DownValues@g)[[All, 1, 1, 1, 2, 1]]

Part::partd: Part specification {HoldPattern[g[x_Integer]]:>x+1,HoldPattern[g[s_String]]:>s<>!!!,HoldPattern[g[r_/;Head[<<1>>]===Real]]:>r+[Pi],HoldPattern[g[(a:Blank[<<1>>])?MatrixQ]]:>foo[a]}[[All,1,1,1,2,1]] is longer than depth of object. >>

If your intent is a truly robust method you may need to actually evaluate the function to see if it "accepts" the argument, meaning that it evaluates to something other than itself. If it is costly to run a full evaluation you can interrupt the process as soon as a rule match is found by using my step function from:

Please load that function then try:

step @ g[#] & /@ {1, "a", 2.5, 1/2, I + 2, {}} // InputForm
{HoldForm[1 + 1], HoldForm[StringJoin["a", "!!!"]], 
 HoldForm[2.5 + Pi], g[1/2], g[2 + I], g[{}]}

Note that the types that match partially evaluate and are held by HoldForm, whereas the ones that do not match return with head g. You could therefore use something like:

SetAttributes[test, HoldFirst]

test[f_, expr_List] := If[MatchQ[step @ f[#], _HoldForm], Head@#, ## &[]] & /@ expr

Example:

test[g, {1, "a", 2.5, 1/2, I + 2, {}}]
{Integer, String, Real}

This returns the heads of the arbitrary expressions for which a match is found.

$\endgroup$
  • $\begingroup$ Very nice and always thorough. +1 $\endgroup$ – RunnyKine Sep 17 '14 at 19:32
7
$\begingroup$

This also works:

getHeads[g_] := DownValues[g][[All, 1, 1, 1]] /. Verbatim[Pattern][_, k_] :> k[[1]]

Then:

getHeads[g]
{Integer, String}
$\endgroup$
5
$\begingroup$
f[g_] := Cases[First[#], Verbatim[Blank][x_] :> x, ∞] & /@ DownValues[g]
$\endgroup$
  • 2
    $\begingroup$ @kocho do not accept so fast :) it is good habit to wait a day or two. don't discourage others. p.s. This is not very general answer, you may want to consider BlankSequence e.g. Depeds what you need at the end :) $\endgroup$ – Kuba Sep 17 '14 at 18:20
  • $\begingroup$ How would I extend this to work with anonymous functions? Say I wanted to run f[Function[x_Integer,x+1]] and get {{Integer}}, is there any way to do that? $\endgroup$ – gallabytes Sep 17 '14 at 18:25
  • 2
    $\begingroup$ @kocho your use of Function is malformed. Function does not accept parameter patterns. If you need that look at (9759) and the example (58341) $\endgroup$ – Mr.Wizard Sep 17 '14 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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