52
$\begingroup$

I cannot understand the difference between Apply, Scan, and Map.

I have tried to play with these functions using the codes below:

f[x_] := x^2

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

Output:

{{a^2, b^2}, c^2}

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

Output:

f[{a, b}, c]

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

There is no output for this one.

The function of Map is understandable. But I cannot get the purpose of Apply and Scan. What are the differences between these three functions?

$\endgroup$
2
  • 11
    $\begingroup$ Do not define f when you do this, then it'll be much easier to understand what's going on. ...^2 threads over lists so your results don't reflect only the effect of Map, etc. but also this auto-threading. Take a look here and in the documentation page of each function. There are plenty of examples. For example the documentation clearly explains that Scan is the same as Map except it doesn't return anything. If is useful when f has a side-effect, e.g. f=Print. $\endgroup$
    – Szabolcs
    Commented Apr 16, 2014 at 16:11
  • 10
    $\begingroup$ In addition to the references given by @Szabolcs: I covered these functions in considerable detail here. $\endgroup$ Commented Apr 16, 2014 at 16:28

2 Answers 2

57
$\begingroup$

These three functions are similar (speaking commonly), and in some applications any of them could be used, yet they have very different special applications.

Rudimentarily:

  • Map wraps (sub)expressions in a given Head, and returns the modified input
  • Apply replaces Heads in (sub)expressions, and returns the modified input
  • Scan "visits" (sub)expressions, evaluates each of them, and returns Null

(For each example below f should be undefined; start with ClearAll[f].)

Map

Most basically Map works like this:

f /@ head[1, 2, 3]      (* shorthand for Map[f, head[1, 2, 3], {1}] *)
head[f[1], f[2], f[3]]

As with each of the functions under discussion it can operate at different levels:

Map[f, {{1, 2}, {3, 4}}, {0}]
Map[f, {{1, 2}, {3, 4}}, {1}]
Map[f, {{1, 2}, {3, 4}}, {2}]
Map[f, {{1, 2}, {3, 4}}, {0, 2}]
f[{{1, 2}, {3, 4}}]

{f[{1, 2}], f[{3, 4}]}

{{f[1], f[2]}, {f[3], f[4]}}

f[{f[{f[1], f[2]}], f[{f[3], f[4]}]}]

Map is an additive process; it inserts additional heads into an expression at the specified levelspec and then evaluates it.

If the expression is held nothing evaluates. (Held meaning the outermost Head has an Attribute such as HoldAll. Hold has this attribute. Do not confuse the Head with the Attribute.)

Print /@ Hold[a[1, 2], b[3, 4]]
Hold[Print[a[1, 2]], Print[b[3, 4]]]

Apply

Apply works like Map, except that instead of adding a Head it replaces an existing one, if a head exists at the specified level. Unlike Map it is frequently used at levelspec {0} with the short form @@, which replaces the outermost head:

f @@ head[1, 2, 3]   (* short form of Apply[f, head[1, 2, 3], {0}] *)
f[1, 2, 3]

A second short form (@@@) exists for levelspec {1}:

f @@@ {{1, 2}, {3, 4}}
{f[1, 2], f[3, 4]}

If a sub-expression is atomic (has no operable Head) Apply does not modify it:

f @@@ head[1, 2, x + y]
head[1, 2, f[x, y]]

Note that 1 and 2 are unmodified, while x + y (which has a FullForm of Plus[x, y]) had its Head replaced.

As with Map, Apply transforms an expression and then evaluates it in entirety; if it is held evaluation does not continue:

Print @@@ Hold[a[1, 2], b[3, 4]]
Hold[Print[1, 2], Print[3, 4]]

One way to "release" this expression for evaluation is to replace Hold with something else, like List:

List @@ Print @@@ Hold[a[1, 2], b[3, 4]]

12

34

{Null, Null}

Note here that the elements 1, 2, and 3, 4 are printed by Print, but since Print[. . .] itself evaluates to Null the output of the line is {Null, Null}.

Scan

Scan is also like Map, except that each individual expression which is wrapped in the specified head is evaluated outside of the main expression, and the main expression is never returned. If we Scan a function without side-effects over an expression we get nothing:

Scan[f, head[1, 2, 3]]

(This could also be written f ~Scan~ head[1, 2, 3].) We do not even get an output line because the output is Null and Mathematica does not print a lone Null output line (by convention). If we use a function with side-effects, such as Print, we get a different result:

Scan[Print, head[1, 2, 3]]

1

2

3

Crucially Scan will carry out its evaluation even when an expression is held because it only looks at the subexpressions:

Print ~Scan~ Hold[a[1, 2], b[3, 4]]

a[1,2]

b[3,4]

Its evaluation can also be interrupted because, unlike Map and Apply which transform the entire expression, then evaluate it, Scan works incrementally:

(Print[#]; If[# > 2, Return[]]) & ~Scan~ {1, 2, 3, 4, 5}

1

2

3

Because Scan does not first duplicate and then modify the entire expression it may be more memory efficient than the use of Map or Apply, but it will still unpack packed arrays therefore if memory efficiency is a priority other methods may be preferred.


Conclusion

I hope these simple examples illustrate that these three functions, while closely related, have unique characteristics that differentiate their use, and each has powerful applications that the others do not.

Recommended reading regarding performance of these and related functions:


Notes on atomic objects

Not all expressions that have the appearance head[arguments] in FullForm are actually standard expressions in that form that can be manipulated with structural tools such as Apply. Instead they have a special internal format and they are merely displayed in the form head[arguments].

From the documentation on Atomic Objects:

All expressions in Mathematica are ultimately made up from a small number of basic or atomic types of objects.

These objects have heads that are symbols that can be thought of as "tagging" their types. The objects contain "raw data", which can usually be accessed only by functions specific to the particular type of object. You can extract the head of the object using Head, but you cannot directly extract any of its other parts.

Standard objects such as strings and integers are atomic, but so are other expressions that may not appear to be:

list = {1, "test", 1/2, 2 + 3 I};
list // FullForm
AtomQ /@ list
List[1, "test", Rational[1, 2], Complex[2, 3]]

{True, True, True, True}

Also atomic are more complex structures such as SparseArray, Graph, BooleanFunction, and (in recent versions) Image.

Atomic objects (or atoms) are handled differently from standard (compound) expression forms.

  • Rational[1, 2] is atomic even though it appears otherwise.
  • Even though 1 is formatted without an obvious head, Head will return Integer.
    (See Is there a summary of answers Head[] can give? for other examples.)

  • Apply does not work in the "normal" way on atoms:

    new @@@ list
    
    {1, "test", 1/2, 2 + 3 I}
    
  • Replace and kin often work on the apparent FullForm of atoms:

    Replace[list, head_[args__] :> {head, args}, {1}]
    
    {1, "test", {Rational, 1, 2}, {Complex, 2, 3}}
    
  • Functions may be overloaded to handle atoms differently; e.g. many functions treat a SparseArray as they would the Normal form expression:

    new @@ SparseArray[{1, 2, 3}]
    
    new[1, 2, 3]      (* not new[{1, 2, 3}] *)
    
  • Handling may appear somewhat irregular even within functions; Part will "extract" (or return) the head of Rationa[1, 2] but not anything else:

    Part[1/2, 0]
    
    Rational
    
    Part[1/2, 1]
    

    Part::partd: Part specification (1/2)[[1]] is longer than depth of object. >>

    (1/2)[[1]]
    

(Somewhat related, and perhaps of interest: Head and everything except Head?)

$\endgroup$
12
  • 1
    $\begingroup$ It may be worthwhile adding a note about things that are atomic, but not immediately obviously so, like AtomQ@Complex[1, 1] == True vs. AtomQ@Complex[1, a] == False. +1 $\endgroup$
    – rcollyer
    Commented Apr 17, 2014 at 16:08
  • 1
    $\begingroup$ @Sascha It is infix notation. Please see this. a ~op~ b is equivalent to op[a, b]. I use it a lot because I find it more readable than many nested brackets, but I know a lot of people don't agree with me. $\endgroup$
    – Mr.Wizard
    Commented Apr 18, 2014 at 9:22
  • 2
    $\begingroup$ +1, nice answer. I'd perhaps emphasize more strongly that because Scan does not produce an output, it can be significantly more memory-efficient than Map, particularly when such an output is not needed and in discarded in any case. I've seen cases where this made a real difference. $\endgroup$ Commented Apr 22, 2014 at 22:49
  • 1
    $\begingroup$ @Mr.Wizard This is interesting. Somehow, I never realized that Scan unpacks, and this indeed makes a big difference. In practice then, for really large lists, it may make sense to have a chunked version of Scan, that would use Map (or Listability) on chunks. Of course, one then looses the ability to return from Scan at any point (inserting such a condition will break vectorization), but for many use cases that can be just fine. Thanks for this tidbit. So yes, it kind of addresses the issue, although it would be nice to have an example, as well as emphasize that Scan unpacks. $\endgroup$ Commented May 26, 2016 at 16:33
  • 2
    $\begingroup$ @Mr.Wizard Unfortunately, due to the nature of Do, the by-element indexing method is slow to the degree that it defeats the purpose. Of course, in such cases for large numerical (packed) arrays there is always Compile, and for non-packed it doesn't matter that much. The good thing would be to systematically enumerate these approaches with their respective domains of applicability, pros and cons (I mean, Scan, Map, chunked scan based on Map, Do loop, Compile). Otherwise it is hard to keep all these things in mind, even for us. $\endgroup$ Commented May 26, 2016 at 17:13
15
$\begingroup$

For a visual, animated, description of basic behavior of such functions, I recommend:

http://reference.wolfram.com/legacy/flash/

(You may want to turn on your computer's sound.)

$\endgroup$
2
  • $\begingroup$ As a rule we try to avoid link-only answers. (Excepting large collections.) Perhaps you could include a screen capture or something? Also, I don't see an animation for Scan. $\endgroup$
    – Mr.Wizard
    Commented Apr 19, 2014 at 17:08
  • 2
    $\begingroup$ @Mr.Wizard: The whole point is that the link points to animations of how the various operators work! And given that the pages are copyright by Wolfram Research, I'm not sure it's legal to post even a static screenshot (would it be "fair use"?) But the animations are very useful ways to understand what Map, Apply, etc., do -- using a different modality than the usual static examples. $\endgroup$
    – murray
    Commented Apr 19, 2014 at 21:04

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.