13

I hope this is not a silly question, since there are plenty of questions on Thread and MapThread. However, I still cannot quite understand the relationship between them. It seems MapThread can take a level argument, but Thread cannot. Thread can do something like Thread[f[{a, b, c}, x]], MapThread cannot. Why we have two of them?

Following two commands give the same results:

MapThread[f, {{a, b, c}, {x, y, z}}]
Thread[f[{a, b, c}, {x, y, z}]]
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Kattern
  • 2,561
  • 19
  • 35
  • "MapThread works like Thread, but takes the function and arguments separately..." - straight from the documentation. They can be used to get the same results, and each has some giblets the other lacks (e.g., specifying the head to thread over in Thread). Read, understand and experiment with the examples in the documentation, everything you ever wanted to know is there. – ciao Jun 02 '15 at 04:12
  • @ciao does it means actually these two function are almost the same? It is a redundant design for convince. – Kattern Jun 02 '15 at 04:27
  • 3
    Thread has been here since V1. MapThread was added in V2, since the need to map something over sets of equal length lists is common (effectively mapping the threading over sets made up from the Nth elements of each list), made sense to add it. In addition, there are differences in evaluation - MapThread evaluates on each set as you progress, Thread attempts to evaluate the whole expression before attempting to thread it. Room (and need) for both... – ciao Jun 02 '15 at 05:16
  • 1
    @ciao An example of this is Thread[Plus[{{1, 2, 3}, {4, 5, 6}}]] vs MapThread[Plus, {{1, 2, 3}, {4, 5, 6}}]. – Greg Hurst Jun 02 '15 at 14:32
  • @ChipHurst, Shouldn't the example be "Thread[Plus[{1, 2, 3}, {4, 5, 6}]] vs MapThread[Plus, {{1, 2, 3}, {4, 5, 6}}]", which doesn't quite show what you wanted to. Perhaps Thread[Equal[{1, 2, 3}, {4, 2, 6}]] vs MapThread[Equal, {{1, 2, 3}, {4, 2, 6}}]? – Michael E2 Jun 03 '15 at 16:55
  • @MichaelE2 yes, thank you for finding this mistake! – Greg Hurst Jun 05 '15 at 16:35
  • Obvious, but see also the sections Properties and relations and Possible issues sections of MapThread. – Jacob Akkerboom Jun 15 '15 at 10:44

1 Answers1

14

With one addition the difference is as you describe in your own question:

  • MapThread takes the head separately
  • MapThread only operates upon List expressions
  • MapThread does not distribute singletons
  • MapThread accepts a level specification

The first characteristic simplifies threading functions that are eager to evaluate:

f = Print;
one = {a, b, c};
two = {x, y, z};

MapThread[f, {one, two}];

Thread[f[one, two]];

ax

by

cz

{a,b,c}{x,y,z}

In the second form Print evaluates to Null giving Thread nothing to do. To make Thread work one would need to keep Print unevaluated while allowing one and two to resolve:

Block[{Print},
  Thread[f[one, two]]
];

That MapThread does not distribute singletons is something I have no explanation for. I have wished that it would work that way but I may have failed to comprehend the ramifications of that behavior. Perhaps it was determined that the interaction with level specification would be too confusing or arbitrary.

Exploration

Here is an attempt at implementing a MapThread variant with both level and head parameters that threads over singletons, based on my earlier raggedMapThread; perhaps experimentation with it will demonstrate limitations or implementation difficulties that illuminate the reason for the existing design.

distMapThread[f_, expr_, level_Integer: 1, head_: List] := 
 Apply[f, 
   Map[Thread[#, head] &, 
     Flatten[expr, List /@ Range[2, level]],
   {level - 1}],
 {level}]

An example:

distMapThread[
  foo,
  {ReplacePart[Array[a, {3, 2, 3}], {{1, 2} -> x, {3, 2} -> z}], 
   ReplacePart[Array[b, {3, 2, 3}], {2, 1} -> y]},
  3
] // MatrixForm

enter image description here

A smaller example with a different head:

distMapThread[
 foo, 
 bar[Array[a, {3, 2}, 1, bar], ReplacePart[Array[b, {3, 2}, 1, bar], 2 -> x]],
 2,
 bar
]
bar[
 bar[foo[a[1, 1], b[1, 1]], foo[a[1, 2], b[1, 2]]], 
 bar[foo[a[2, 1], x], foo[a[2, 2], x]],
 bar[foo[a[3, 1], b[3, 1]], foo[a[3, 2], b[3, 2]]]
]
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371