14

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.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 1
    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. – rcollyer Sep 01 '13 at 14:34
  • 1
    @rcollyer / is a listable binary operator so it works out fine since both arguments will have the same shape. – ssch Sep 01 '13 at 15:25
  • @rcollyer I don't see the problem with your example. Perhaps you have another one? – Mr.Wizard Sep 01 '13 at 22:38
  • @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 – rcollyer Sep 03 '13 at 12:45
  • @rcollyer I know that feeling. Too well. – Mr.Wizard Sep 03 '13 at 13:25
  • Do you have a strict definition of what you call inherent listability, or you are looking for something that more or less works in the most common cases? – Rojo Oct 31 '13 at 15:43
  • @Rojo I didn't (and don't) have a strict definition. "... and moreover, that the question itself may be hard to answer in a complete way." What are you thinking? I should revisit this some time but as you may have noticed I'm taking a break from Mathematica. – Mr.Wizard Oct 31 '13 at 21:39
  • Oh, I hadn't noticed. Why is that? – Rojo Oct 31 '13 at 21:46
  • @Rojo I was feeling burned out. I decided to take a break before I started to dislike spending time here. I still stop by at least daily to check for fires (flags). – Mr.Wizard Oct 31 '13 at 21:48
  • How about this? :) Just check the FullForm function in this list Select[Names["*"], MemberQ[Attributes[#], Listable] &] – Nam Nguyen Oct 28 '20 at 18:27
  • @NamNguyen That is what I meant by "In the case of Symbols this is merely a matter of checking Attributes." – Mr.Wizard Oct 30 '20 at 00:23

2 Answers2

6

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}

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 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. – Mr.Wizard Sep 01 '13 at 13:36
  • 1
    Additionally I realize that my ff and f2 functions are poorly conceived; they were hasty examples. I'll revisit this tomorrow. – Mr.Wizard Sep 01 '13 at 14:02
2

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}

ssch
  • 16,590
  • 2
  • 53
  • 88
  • 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. – Mr.Wizard Sep 01 '13 at 13:54
  • @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 :) – ssch Sep 01 '13 at 13:59