# Thread a function over a list and with a non-atomic 2nd parameter?

From the documentation, Thread's behavior on functions where the first parameter is a List and the second is an atomic expression, is this:

Thread[f[{a, b, c}, x]]

(* {f[a, x], f[b, x], f[c, x]} *)


If the second argument of 'f' is not atomic, it is possible to thread over its first argument {a,b,c} while treating the 2nd argument as if it were atomic? For example, consider:

M = Table[i + j, {i, 1, 3}, {j, 1, 2}]

(* {{2, 3}, {3, 4}, {4, 5}} *)


Then,

Thread[f[{a, b, c}, M]]

(* {f[a, {2, 3}], f[b, {3, 4}], f[c, {4, 5}]} *)


This is understandable because M is not atomic so For example:

g[p_, M_ /; Dimensions[M] == {3, 2}] := {p, M}


Threading over g Doesn't work as intended:

Thread[g[{a, b, c}, M]]

(* {{a, {2, 3}}, {b, {3, 4}}, {c, {4, 5}}} *)


Worse still, the result above is due to a coincidence: Length@M==3. If this is not the case, Thread returns an error:

Thread::tdlen: Objects of unequal length in f[{a,b,c},{{2,3,4,5},{3,4,5,6}}] cannot be combined. >>


I've also considered wrapping M in some variation of Hold, but none yield atomic expressions. Is there a way to force Thread to treat the 2nd argument of the function as atomic?

The ultimate goal is to SetAttributes of the function to Listable.

• But ... what is your intended result? ... f[#, M] & /@ {a, b, c} ? – Dr. belisarius Aug 25 '12 at 21:32
• @Verde, it should handle both f[#, M] & /@ {a, b, c} and f[#,M]& @ a. – alancalvitti Aug 25 '12 at 21:40
• I'm really not quite sure what you are trying to achieve but doesn't ReleaseHold@Thread[f[{a, b, c}, Hold@M]] do what you want? – sebhofer Aug 25 '12 at 21:45
• If I understand the question correctly, I gave explicit solutions here and here, where I also explain the solutions in detail. – Leonid Shifrin Aug 25 '12 at 21:55
• @Mr.Wizard The question does not strike me as being asked narrowly about Thread. The goal seems to thread a function over a list in a certain way, and Thread was just used as a seemingly most straightforward way to obtain the desired result. At least, this is how I interpreted it from the start. – Leonid Shifrin Aug 25 '12 at 22:41

I will reproduce two solutions from my book, one using Listable SubValues described here:

listThread[f_, x_, y_] :=
Module[{auxf},
SetAttributes[auxf, Listable];
auxf[t_][z_] := f[t, z];
Through[auxf[x][y]]];


and another one using pure functions with Listable attribute, described here:

halfListable[f_, x_, y_] := Function[t, f[t, y], Listable][x]


Here is an example:

listThread[f, {1, 2}, {3, 4, 5}]
halfListable[f, {1, 2}, {3, 4, 5}]

(*
{f[1, {3, 4, 5}], f[2, {3, 4, 5}]}
{f[1, {3, 4, 5}], f[2, {3, 4, 5}]}
*)


More explanations can be found in the linked sections of the book.

• Thank you Leonid - so far this looks like the best hope, I will likely accept shortly (still looking through your web site) though I was hoping to avoid custom functions. Could WRI have implemented a Level option functionality for Listable to achieve this? – alancalvitti Aug 25 '12 at 22:29
• @alancalvitti The probem is that Listable is an attribute, not a function. As such, it is wired quite deeply into the evaluation sequence. And evaluator does not have any tuning parameters, it is not even clear how you can define such parameters locally (so that they will apply to a specific head in a given place of code). – Leonid Shifrin Aug 25 '12 at 22:35
• @alancalvitti Thanks for the accept. It's been a while :-). – Leonid Shifrin Feb 19 '13 at 19:56

Does this work as desired?

Thread[Unevaluated[f[{a, b, c}, M]]]

{f[a, {{2, 3}, {3, 4}, {4, 5}}],
f[b, {{2, 3}, {3, 4}, {4, 5}}],
f[c, {{2, 3}, {3, 4}, {4, 5}}]}


Since apparently I was just being obstinate regarding the focus of this question I shall give in and address the extension of this behavior to a pseudo-Listable function. I still find the question underspecified in that interpretation.

ClearAll[f, a, b, c, m]

SetAttributes[f, HoldAll]
x : f[___, _List, ___] := Thread@Unevaluated@x
f[other___] := {other}


Now:

m = Table[i + j, {i, 1, 3}, {j, 1, 2}];

f[{a, b, c}, m]

f[m, {a, b, c}]

{{a, {{2, 3}, {3, 4}, {4, 5}}}, {b, {{2, 3}, {3, 4}, {4, 5}}}, {c, {{2, 3}, {3, 4}, {4, 5}}}}

{{{{2, 3}, {3, 4}, {4, 5}}, a}, {{{2, 3}, {3, 4}, {4, 5}}, b}, {{{2, 3}, {3, 4}, {4, 5}}, c}}

• Nicer than my suggestion (actually the correct way to do it) but I don't think it will satisfy the OP – sebhofer Aug 25 '12 at 22:06
• @sebhofer Thanks; why not? – Mr.Wizard Aug 25 '12 at 22:07
• If I understand correctly the OP wants a function with Listable which does exactly this without using Thread or such... That's what I gather from the comment above. Not totally sure though. – sebhofer Aug 25 '12 at 22:09
• @sebhofer Then a new question is required as this one is clearly about Thread. (Final line notwithstanding.) – Mr.Wizard Aug 25 '12 at 22:12
• @alancalvitti It does so if f is Listable, otherwise it returns the desired result. – sebhofer Aug 25 '12 at 22:31

ClearAll[f];
Thread[f[{a, b, c}, M], List, 1]
(* {f[a, {{2, 3}, {3, 4}, {4, 5}}], f[b, {{2, 3}, {3, 4}, {4, 5}}],
f[c, {{2, 3}, {3, 4}, {4, 5}}]}*)

Thread[g[{a, b, c}, M], List, 1]  // Grid More related examples in Scope >> Sequence Specifications in docs >> Thread

• +1 because this is useful information and the question is not well defined, but I interpret that the solution should also handle f[M, {a, b, c}] and this, as written, does not. – Mr.Wizard Aug 25 '12 at 22:25
• I am mistaken, it does! Thread[f[M, {a, b, c}], List, {2}] (For some reason I didn't think that worked.) – Mr.Wizard Aug 25 '12 at 22:27
• Very nice. Is there any way to make Listable work with these options? – alancalvitti Aug 25 '12 at 22:35
• @alancalvitti, in effect you want to change the behavior of the attribute Listable to thread over selected arguments? I would defer to Leonid/Rojo/... on whether this is possible/advisable:) – kglr Aug 25 '12 at 23:06
• alan, btw just noticed that @Leonid's answer and comment already adresses this question. – kglr Aug 25 '12 at 23:18

The following works when f is Listable and has a definition.

M = Table[i + j, {i, 1, 3}, {j, 1, 2}];
SetAttributes[f,Listable]
f[x_,y_]:={x,y}
f[{a,b,c},Unevaluated@M]