In Lisp there are two common mapping functions for iterating over lists,
mapcar and
maplist. The
mapcar function corresponds to the Scala
map function, but there does not seem to be an equivalent of
maplist in Scala, although it is not difficult to create one for lists as follows.
def maplist[T,U](seq:List[T],client:(List[T]=>U)):List[U] = {
def loop(rest:List[T], acc:List[U]):List[U] = rest match {
case Nil => acc
case _::tail => loop(tail,client(rest)::acc)
}
loop(seq,Nil).reverse
}
maplist does not iterate over the elements as such, but rather iterates over the cons cells. Thus at each iteration you can look t the head and tail to see the current element and the elements to come.
My question concerns the marriage of for expressions map. And expression such as
for { x <- List(1,2,3) ; y <- List(10,20,30) } yield List(x,y)
will collect all paris of one element from the first list and one element from the second list. But if the two given lists are the same
there will be duplicates making the loop do twice as much work as necessary and allocate twice as much memory as necessary. This duplication is because iteration is done via map rather than maplist.
Can I somehow extend for so that I can use my own syntax, e.g., <::- to indicate that I want to use maplist rather than map? I.e., first iteration x=List(1,2,3), second x=List(2,3), finally x=List(3).
for { x <::- List(1,2,3) ; y <- x } yield List(x.head,y)
And if I want to eliminate the elements List(1,1), List(2,2), and List(3.3) I could do the following:
for { x <::- List(1,2,3) ; y <- x.tail } yield List(x.head,y)
Of course I'd also have to apply a flatMap analog to maplist, which is not so difficult to do. Albeit perhaps a challenge to do efficiently, admittedly the following has the problem that lists are reversed twice in order to make it purely functional.
def mapcon[T,U](seq:List[T],client:(List[T]=>List[U])):List[U] = {
def loop(rest:List[T], acc:List[U]):List[U] = rest match {
case Nil => acc
case _::tail => loop(tail,client(rest).reverse++acc)
}
loop(seq,Nil).reverse
}
The other way to do this might be to define a subtype of List[T] called ListPrime[T], and therein override map and maplist. Perhaps it's a more Scala-ish way to do it, but I'm not sure what the hidden consequences would be of overriding map and flatMap especially if my class inherits from List[T].
I'd love to hear anyone's comments on this. It it a problem that for does not let you describe how iteration should occur? And is it possible/safe to override map and flatMap on subclasses of List?