Math 236  •  30 March 2001

Functional programming using Map and Apply

incomplete!

See also the page "Pure Functions" in the "Mathematica Q & A" part of the Notes section of the course web site.

Pure functions

We start with a toy problem: to square a number, say 5.  The direct way to do it is, of course:

(5^2)

The expression (5^2)is an abbreviation for Power[5, 2], so we just used the function Power of two arguments.  Instead, let's square by introducing a function of one variable:

square[x_]  :=  (x^2)

square[5]

An alternative to using such a named function is to use an anonymous, unnamed function -- a function that, in Mathematica terminology, is called a pure function:

	\(TraditionalForm\`#\^2\)&

The symbol # stands for the argument, and the ampersand & at the end indicates that this is a pure function.  Such a function is used as if it had been given a name, in other words, the same way we used the named function square.

\(\(#\^2 &\)[5]\)

If you are more comfortable doing so, go ahead and enclose the pure function in parentheses—it doesn't hurt (and, in more complicated situations will avoid ambiguity or errors).

\(\((#\^2 &)\)[5]\)

The pure function notation using # and & is just a shorthand for the function Function, which is used like this:

Function[x,  (x^2)][5]

The expression Function[x, (x^2)] stands for a function that has at an arbitrary input x the value (x^2).  The symbol x here is a "dummy variable" that gets no global value:

x

Moreover, the symbol x may be replaced by others, for example:

Function[y,  (y^2)][5]

You can see the actual, full meaning of the pure function abbreviation ((#)^2)& like this:

\(FullForm[#\^2 &]\)

Here Slot[1] is what we abbreviated by #.  Actually, # is  an abbreviation for #1, and #1 is in turn an abbreviation for Slot[1].   Instead of using #, you could use #1:

#(1^2) &[5]

Is there such a thing as #2 and Slot[2], etc.?  Yes—when we want to use a pure function of several arguments, for example:

\(\(#1\^#2 &\)[5, \ 3]\)

Here again there was no real reason to use a pure function—you could simply  have evaluated (5^3)—but we did so for purposes of introducing pure functions.  All the preceding was a warmup for a genuine application of pure functions that will follow below.

Map

Suppose we have a list of lists, such as the following (which, by the way, is not a matrix, since its constituent lists do not have the same lengths):

numbers  =  {{1, 2, 3},  {2, 0},  {8, 1, 4, 4}};

The problem we pose is to find the lengths of this list's constituent lists.  The following does not do what we want—it just gives the length of the overall list.

Length[numbers]

We can find the lengths of the constituent lists and then assemble those lengths into a single list:

{Length[numbers[[1]]],  
      Length[numbers[[2]]],  
      Length[numbers[[3]]]}

But that tactic would not work if we did not know ahead of time how many consituent lists there were.  A method that works in general is to use the Map function like this:

Map[Length,  numbers]

Whenever you want to apply the same function to each element of a list, you can do so implicitly by using Map.  For example:

Map[square,  {5,  4}]

Here's the same thing, but using a pure function:

\(Map[#\^2 &, \ {5, \ 4}]\)

Of course, that example, too, is just for purposes of illustration.  Since the power function has attribute Listable, a much simpler way of squaring each number in the list {5, 4} is:

{5,  4}^2

Another example of using Map appears in the section Strings, below.

Strings

A string is just a sequence of symbols enclosed in double quotes:

greeting  =  "hello"

Of course, you don't see the quotes unless you look at the FullForm:

FullForm[greeting]

You can check that something is a string by evaluating at it the logical function StringQ ...

StringQ[greeting]

…or by checking its Head:

Head[greeting]

A string can consist of a single letter...

StringQ["h"]

... or any other single character:

StringQ["3"]

A string can consist entirely of non-letter characters:

"3 + 4"
StringQ["3 + 4"]

But when the characters constituting a string are numbers and other characters denoting mathematical operations, you cannot always tell just by looking at the output what you have:

"3"  +  4

FullForm[%]

Don't confuse strings with symbols.  Here's a symbol:

abc
StringQ[abc]
Head[abc]

Look again at greeting.

greeting

Suppose we wish to extract its first letter.  The following approach, using indexing, does not work:

greeting[[1]]

The reason such indexing does not work is the same reason it doesn't work in the following cell: a multi-character string, like a multi-digit number, is an atomic object.

234[[1]]

Because a string such as greeting is an atomic object, lots of our familiar functions for manipulating lists do not work with strings, or do not work as expected.  For example:

Length[greeting]

Using a corresponding StringSomething function often does for strings what the Something function does for lists.  For example:

StringLength[greeting]

StringTake[greeting,  4]

StringTake[greeting,  {2}]

StringDrop[greeting,  -3]

Recall that, for lists, Position tells us where an item lies in the list:

Position[{99,  3,  4,  6,  6},  6]

To do the corresponding thing with a string, use StringPosition:

StringPosition[greeting,  "l"]

The form of the result may not be quite what you expected: why the double 3 and the double 4?  The next example may help clarify things:

StringPosition[greeting,  "lo"]

Thus StringPosition[string, substring] tells where in string the substring starts and where it ends.  So the earlier result {{3,3},{4,4}} for StringPosition[greeting, "l"] means that the one-character substring "l" appears twice in "hello", once starting and ending at position 3 and then again starting and ending at position 4.

To convert a string to the list of its characters, use the function Characters:

spelling  =  Characters[greeting]

Now we can index into this list, find the position of a character in the list, etc., without using the special StringSomething family of functions.  For example:

spelling[[1]]
Position[spelling,  "l"]

Let's form a list of two strings:

more  =  "world"; 
words  =  {greeting,  more}

To convert the characters constituting each string in the list into a list of individual characters, we may use Map:

Map[Characters,  words]

To find the number of characters in each string in the list, you may do this:

Map[StringLength,  words]

(Can you think of another way to do it -- without, of course, explicitly forming a list?)

Our next example of working with strings is to determine the positions in the alphabet of the characters in a string.  Here's our alphabet:

letters  =  Characters["abcdefghijklmnopqrstuvwxyz"]

Here are the positions:

Position[letters,  Characters[greeting]]

Oops, that didn't work!  Do you understand why?  Here's a way that does work:

Map[Position[letters,  #] &,  Characters[greeting]]

Flatten[%]

Can you think of a simpler way to get the same result about the positions of the letters of greeting in our alphabet?

Next, let's try joining the two "words" greeting and more together:

Join[greeting, more]

Once again, we must use a StringSomething function instead:

StringJoin[greeting,  more]

StringJoin can, like ordinary Join, be used with infix notation:

greeting~StringJoin~more

The notation <> (that's < followed by >) can be used as an abbreviation instead:

greeting <> more

To insert the missing space, join the one-character blank string between the two others:

greeting <> " " <> more

That's again an abbreviation, for the following:

StringJoin[greeting,  " ",  more]