14
$\begingroup$

I would like to have a test that determines if a particular function is Listable. In the case of Symbols this is merely a matter of checking Attributes. Function definitions with the Listable attribute are a bit more involved but quite easy.

However I specifically want to test for inherent listability in as many cases as possible.

For example consider the function from Case #4 in Alternatives to procedural loops and iterating over lists in Mathematica:

(3 - #)/(7 * #) &

This function is inherently listable:

fn = (3 - #)/(7*#) &;
Map[fn, {1, 2, 3}]
fn @ {1, 2, 3}
{2/7, 1/14, 0}
{2/7, 1/14, 0}
  • One must consider Functions with multiple arguments, both the Slot and named parameter type.

  • Ideally the test would handle pattern-based (DownValues) functions to the extent that is possible.

$\endgroup$
11
  • 1
    $\begingroup$ What if your list is {3, 4, {1,2}}? Then, listable f, gives {f[3], f[4], {f[1], f[2]}}. So, while (3 - #)/(7 #)& is inherently listable, I don't think it is completely listable. So, ListableQ will have some limitations. $\endgroup$
    – rcollyer
    Commented Sep 1, 2013 at 14:34
  • 1
    $\begingroup$ @rcollyer / is a listable binary operator so it works out fine since both arguments will have the same shape. $\endgroup$
    – ssch
    Commented Sep 1, 2013 at 15:25
  • $\begingroup$ @rcollyer I don't see the problem with your example. Perhaps you have another one? $\endgroup$
    – Mr.Wizard
    Commented Sep 1, 2013 at 22:38
  • $\begingroup$ @Mr.Wizard initially it was very clear to me when I wrote, not so much now, especially with ssch's comment. I need to see if I can recover what I intended, but my mental tape drive is faulty. :P $\endgroup$
    – rcollyer
    Commented Sep 3, 2013 at 12:45
  • $\begingroup$ @rcollyer I know that feeling. Too well. $\endgroup$
    – Mr.Wizard
    Commented Sep 3, 2013 at 13:25

2 Answers 2

6
$\begingroup$

This is what I have so far. It's far from complete, but still a good start I think.

listableQ[s_Symbol] /; MemberQ[Attributes @ s, Listable] := True
listableQ[s_Symbol] := MatchQ[DownValues[s], {__?test2}]

listableQ[Verbatim[Function][_, _, attr_]] /; ! FreeQ[{attr}, Listable] := True
listableQ[Verbatim[Function][vars_, body_, ___]] := test1[vars, body]
listableQ[body_ &] := 
  Cases[Unevaluated[body], _Slot, {0, -1}, Heads -> True] /. x_ :> test1[x, body]

SetAttributes[test1, HoldAll];
test1[vars_, body_] :=
  Cases[Unevaluated[body],
    h_[args___] /; ! FreeQ[Unevaluated[{args}], Alternatives @@@ HoldPattern[vars]] :> h,
    {0, -1}
  ] ~MatchQ~ {__?listableQ}

test2[lhs_ :> rhs_] :=
  Union @@ Cases[Unevaluated[lhs],
    Verbatim[Pattern][p_, _] :> Hold[p],
    {0, -1}, Heads -> True
  ] /. x_ :> test1[x, rhs]

Here is a very basic test; qq represents a generic function that isn't listable.

ff[a_, b__, c : _List] := a^2/Sign[b^2 + c] + qq[z]
f2[a_, b__, c : _List] := qq[a, b^2] + c

listableQ /@ {
  (3 - #)/(7*#) &,
  (3 - #)/(qq[7]*#) &,
  qq[(3 - #), (7*#)] &,
  Function[x, x + qq[5] + x/2],
  Function[x, qq[x + 5, x/2]],
  Function[, qq[(3 - #), (7*#)], Listable],
  ff,
  f2
 }

{True, True, False, True, False, True, True, False}

$\endgroup$
2
  • $\begingroup$ I am aware that there are ways that this fails, and moreover, that the question itself may be hard to answer in a complete way. For example, if qq[7] were to return {1, 2} this would break listability of second function. Nevertheless I am interested in exploring and improving this kind of test. $\endgroup$
    – Mr.Wizard
    Commented Sep 1, 2013 at 13:36
  • 1
    $\begingroup$ Additionally I realize that my ff and f2 functions are poorly conceived; they were hasty examples. I'll revisit this tomorrow. $\endgroup$
    – Mr.Wizard
    Commented Sep 1, 2013 at 14:02
2
$\begingroup$

The naive one-argument test:

naiveListableQ[f_] := f[{1, 2}] === f /@ {1, 2}

Cons:

  • Evaluates the function
  • Can give incorrect result
  • {1,2} has to be in domain of function
  • ...

Results:

naiveListableQ /@ {
  (3 - #)/(7*#) &,
  (3 - #)/(qq[7]*#) &,
  qq[(3 - #), (7*#)] &,
  Function[x, x + qq[5] + x/2],
  Function[x, qq[x + 5, x/2]],
  Function[, qq[(3 - #), (7*#)], Listable],
 }

{True, True, False, True, False, True}

$\endgroup$
2
  • $\begingroup$ I considered this approach and decided to go a different direction. It may be the most rigorous approach for anonymous functions that don't contain pattern matching. However, I don't like the fact that it does evaluate the function (multiple times) which works against the applications I have in mind. $\endgroup$
    – Mr.Wizard
    Commented Sep 1, 2013 at 13:54
  • $\begingroup$ @Mr.Wizard CW was intentional as it was bit too long for a comment. Except for the simplicity of it I don't like it either :) $\endgroup$
    – ssch
    Commented Sep 1, 2013 at 13:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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