5

Why are the following not equivalent:

Map[Line, Map[(Print[#]; #) &, {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}]]

which produces

{{2,1},{1,1}}
{{-2,1},{3,1}}
{Line[{{2, 1}, {1, 1}}], Line[{{-2, 1}, {3, 1}}]}

and:

Line /@ (Print[#]; #) & /@ {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}

which produces

{{2,1},{1,1}}
{{-2,1},{3,1}}
{{Line[{2, 1}], Line[{1, 1}]}, {Line[{-2, 1}], Line[{3, 1}]}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
d125q
  • 437
  • 3
  • 9

2 Answers2

10

The behaviour we see is due to the precedence of &, which is much lower than the precedence of /@. As a consequence, the expression Line /@ (Print[#]; #) is bound tightly together by the high precedence /@ infix operator, yielding the single argument to the low precedence & postfix operator. This means that the second expression is interpreted as (note the added parentheses prior to the second /@):

(Line /@ (Print[#]; #) &) /@ {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}

which is equivalent to:

Map[Line /@ (Print[#]; #) &, {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}]

or

Map[Map[Line, (Print[#]; #) &], {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}]

This is manifestly different from the first expression in the question.

One way to see this in the front-end is to place the cursor somewhere within Print and then extend the selection using CTRL+. repeatedly. With each key press, the selection grows outward to show how subexpressions group together due to precedence. Repeatedly double-, triple-, quadruple-clicking, etc. on a selection point will also extend the selection to show precedence.

Another way to see how the expression is interpreted is to inspect its full-form:

Line /@ (Print[#];#)& /@ {{{2,1},{1,1}},{{-2,1},{3,1}}} // FullForm // HoldForm

(*
  Map[
    Function[Map[Line, CompoundExpression[Print[Slot[1]],Slot[1]]]],
    List[List[List[2,1],List[1,1]],List[List[-2,1],List[3,1]]]]
*)

The Wolfram Language documentation has a section that details operator precedence.

WReach
  • 68,832
  • 4
  • 164
  • 269
1

Actually,

Map[Line, Map[(Print[#]; #) &, {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}]]

is equivalent to

Line /@ ((Print[#]; #) & /@ {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}})

with both producing

{{2,1},{1,1}}
{{-2,1},{3,1}}
{Line[{{2, 1}, {1, 1}}], Line[{{-2, 1}, {3, 1}}]}

Note the extra pair of parentheses that I added. They are necessary so that Line in the expression using /@ is mapped to all of ((Print[#]; #) & /@ {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}), as it is in the expression using Map explicitly.

bbgodfrey
  • 61,439
  • 17
  • 89
  • 156
  • Okay, but how is Line mapped without the parentheses? Are the two not supposed to be equivalent? How is the result produced in the case of Line /@ (Print[#]; #) & /@ {{{2, 1}, {1, 1}}, {{-2, 1}, {3, 1}}}? I would really appreciate an explanation. – d125q Jan 03 '15 at 21:04
  • Run Trace on the three expressions to see the differences. – bbgodfrey Jan 03 '15 at 21:06