11.3.7 Using map and curry
Perhaps the easiest way to implement our simple sequence copier is to use the map function. The map function takes the same arguments as does do. However, instead of ignoring the return value of the function that you provide, map gathers into a new collection all the results of calling the provided function. The new collection will be an instance of the type-for-copy of the first collection argument to map.
define method my-copy-sequence
(old-sequence :: <sequence>) => (new-sequence :: <sequence>)
map(identity, old-sequence);
end method my-copy-sequence;
The identity function simply returns its argument without making any changes. A more interesting example is to define a method that multiplies a number by each element of a vector, yielding a new vector with the products. Here is a sample call to scalar-multiply, which we define next:
? scalar-multiply(3, #[4, 5, 6]); #[12, 15, 18]
Here is our definition of scalar-multiply, using map:
define method scalar-multiply
(scalar :: <number>, old-vector :: <vector>) => (result :: <vector>)
map(method (vector-element) scalar * vector-element end,
old-vector);
end method scalar-multiply;
We use the method statement to create a kind of function (a closure) that multiplies scalar by an element of the vector provided by map. The map iterator then calls that function on each element of old-vector, collecting the results in a new sequence. A variant of map, called map-into, replaces elements in an existing collection, rather than creating a new collection for the results. See Section 16.1.2, page 247, for an example of the use of map-into.
We can define this method more succinctly using curry, which is a function that generates a function:
define method scalar-multiply
(scalar :: <number>, old-vector :: <vector>) => (result :: <vector>)
map(curry(\*, scalar), old-vector);
end method scalar-multiply;
The curry function in this example creates exactly the same method as the one that we created in the previous definition of scalar-multiply. That is, curry(\*, scalar) builds a function that multiplies its argument by scalar. This generated function is then used by map to compute the value of each element of the new sequence.
Mapping functions such as do and map work well when you want to operate over the entire collection. The map function works well only if there is a one-to-one correspondence between input-collection sizes and output-collection size. However, the other techniques that we have presented, such as using for and while, can work better when you want to operate on only part of a sequence. In Section 11.3.8, we take another look at how a for loop can help us to solve the problem of iterating over only part of a collection.




