On 27/09/11 15:58, Stephen Allen wrote:
-----Original Message-----
From: Andy Seaborne [mailto:[email protected]]
Sent: Tuesday, September 27, 2011 4:56 AM
To: [email protected]
Subject: Iter.mapMany

Stephen,

I took the liberty of rewriting the implementation of Iter.mapMany - it
didn't cope with the case where one of the sub-iterators returned was
zerolength because it created the iterator and immediately called
.next().

The situation occurred in the SPARQL WG test suite  for Update, where a
binding didn't generate any legal quads.(TemplateLib.calcQuads).


Sounds good.  Not enough tests I guess!


Where did the name "mapMany" come from?  It's called "flatMap in scala.


I called it "mapMany", because I was used to the equivalent C# LINQ operator called "SelectMany" [1].  LINQ's 
version of "Select" is called "map" in ARQ, so I went with "mapMany".


The implement of flatmap is shorter :-) ... and recursive (the while
loop in mapMany is removing that tail recursion).

        Andy

scala.collection.Iterator[A]

    def flatMap[B](f: A =>  GenTraversableOnce[B]): Iterator[B]
               = new Iterator[B] {
        private var cur: Iterator[B] = empty
        def hasNext: Boolean =
          cur.hasNext || self.hasNext&&
               { cur = f(self.next).toIterator; hasNext }
        def next(): B = (if (hasNext) cur else empty).next()
    }

Oh, to have optimized tail recursion in Java!  We can hope it arrives soon.

+1

As far as I can see, this tail recursion does not have problems that are stopping it else where (tail jumps and trampolining).


 Incidentally, I find C#'s implementation even more elegant (argument checking 
elided):

public static IEnumerable<TResult>  SelectMany<TSource, TResult>(
    this IEnumerable<TSource>  source,
    Func<TSource, IEnumerable<TResult>>  selector)
{
     foreach (TSource item in source)
     {
         foreach (TResult result in selector(item))
         {
             yield return result;
         }
     }
}

This relies on C#'s yield generator [2], which makes creating iterators so much 
simpler!

yes - it comes from CLU. Some of the web descriptions call it coroutines but you can do that style yield-iterator on the same call stack.

You have to have compiler/VM support though - there are stackframes in front of the active stack front when yield-iterators are active. A call inside the body of the iterator must put it's frame ahead of the iterators ... or else.


In scala, it can be one line:

  iter.map(a => func(a) ).flatten
or even
  iter.map(func(_)).flatten

which I get it down to:

def flatMap2[A,B](iter:Iterator[A],func: A => Iterator[B])
      : Iterator[B] = iter.map(func(_)).flatten

I expect the Iterator.flatMap was written with a very careful eye to efficiency. If you look at Iterator.flatten, is has the same pattern as the scala library Iterator.flatMap.

I think the one liner it's going to evaluate all the input iterator first in the .map.

        Andy


-Steve

[1] http://msdn.microsoft.com/en-us/library/bb534336.aspx
[2] http://msdn.microsoft.com/en-us/library/9k7k7cf0(v=VS.100).aspx





Reply via email to