Daniel Fischer <daniel.is.fischer <at> web.de> writes: > > Am Samstag 09 Januar 2010 08:04:20 schrieb Will Ness: > > Daniel Fischer <daniel.is.fischer <at> web.de> writes: > > > Am Freitag 08 Januar 2010 19:45:47 schrieb Will Ness: > > > > Daniel Fischer <daniel.is.fischer <at> web.de> writes: > > > > > > It's not tail-recursive, the recursive call is inside a celebrate. > > > > It is (spMerge that is). > > No. > "In computer science, tail recursion (or tail-end recursion) is a special > case of recursion in which the last operation of the function, the tail > call, is a recursive call."
As far as I understand it, when a function makes a tail call to a tail recursive function (be it itself or some other function) it is itself tail recursive. I.e. that call may be replaced with a direct jump, with no new context to be created. That is what your version accomplishes, too. Mine really held to that pair ~(c,d) when it wanted to call (merge _ d) _after_ the call to spMerge. By passing the pre-decomposed part of a pair, 'd', into the process environment of spMerge, you've made it tail recursive - it carried along all the context it needed. That's what've eliminated the space leak, so I'd say tail recursion does play a role under lazy evaluation - when a compiler isn't smart enough to do _that_ for us by itself. _Were_ it reliably smart, even non- recursive functions like my initial variant would work just as well. > The last operation of spMerge is a call to celebrate or the pair > constructor (be that P or (,)). Doesn't matter, though, as for lazy > languages, tail recursion isn't very important. > > > It calls tail-recursive celebrate in a tail > > position. What you've done, is to eliminate the outstanding context, by > > moving it inward. Your detailed explanation is more clear than that. :) > > > > BTW when I run VIP code it is consistently slower than using just pairs, > > I can't reproduce that. Ceteris paribus, I get the exact same allocation > and GC figures whether I use People or (,), running times identical enough > (difference between People and (,) is smaller than the difference between > runs of the same; the difference between the fastest and the slowest run of > the two is less than 0.5%). I think it must be the other changes you made. I just take the VIP code as it is on a web page, and my intial variant without the wheel, and compare. Then I add the wheel in the same fashion, and then feeder, and compare again. When I tested that Monid instance code I too compared it to the straight pairs, and it was slower. Don't know why. > > modified with wheel and feeder and all. So what's needed is to > > re-implement your approach for pairs: > > > > mergeSP (a,b) ~(c,d) = let (bc,bd) = spMerge b c d > > in (a ++ bc, bd) > > where > > spMerge b [] d = ([], merge b d) > > spMerge b@(x:xs) c@(y:ys) d = case compare x y of > > LT -> consSP x $ spMerge xs c d > > EQ -> consSP x $ spMerge xs ys d > > GT -> consSP y $ spMerge b ys d > > > > consSP x ~(bc,bd) = (x:bc,bd) -- don't forget that magic `~` !!! > > I called that (<:). > > > > > BTW I'm able to eliminate sharing without a compiler switch by using > > > > Yes, I can too. But it's easy to make a false step and trigger sharing. yes indeed. It's seems unpredictable. Fortunately GHC couldn't tell that (12-1) was 11 by the looks of it. :) Your idea certainly seems right, that there ought to be some control over sharing on a per-function basis somehow without these ridiculous code tricks. > I can get a nice speedup (~15%, mostly due to much less garbage collecting) by doing the final merge in a function without unnecessarily wrapping the result in a pair Will have to wrap my head around _that_. But that would be fighting with the compiler again. I don't like that, I much rather fight with the problem at hand. There shouldn't be any pairs in the compiled code in the first place. They just guide the staging of (++) and (merge) intertwined between the producer streams. At each finite step, when the second part of a pair comes into play, it is only after its first part was completely consumed. I guess the next thing to try would be to actually create a data type MergeNode and arrange _those_ in a tree and see if that helps. That would be the next half-step towards the PQ itself. > This uses a different folding structure again, which I am yet to decipher. :) > > How about them wheels? :) > > Well, what about them? I dunno, it makes for a real easy wheel derivation, and coming out of our discussion of euler's sieve it's a nice cross-pollination. :) Having yet another list representation suddenly cleared up the whole issue (two of them). I'll repost it one last time as I've made some corrections to it: > euler ks@(p:rs) = p : euler (rs `minus` map (*p) ks) > primes = euler [2..] > primes = euler $ listFrom [2] 1 > = 2:euler ( listFrom [3] 1 `minus` map(2*) (listFrom [2] 1)) ) > listFrom [3,4] 2 `minus` listFrom [4] 2 > == listFrom [3] 2 == > = 2:3:euler (listFrom [5] 2 `minus` map(3*) (listFrom [3] 2)) > listFrom [5,7,9] 6 `minus` listFrom [9] 6 > == listFrom [5,7] 6 == listFrom xs by = concat $ iterate (map (+ by)) xs rolls = unfoldr (Just . nextRoll) ([2],1) nextRoll r@(xs@(p:xt),b) = ( (p,r') , r') where r' = (xs',p*b) xs' = (concat $ take p $ iterate (map (+ b)) $ xt ++ [p+b]) `minus` map (p*) xs nthWheel n = let (ps,rs) = unzip $ take n rolls (x:xs,b) = last rs in ((ps, x), zipWith (-) (xs++[x+b]) (x:xs)) eulerPrimes n = let (ps,rs) = unzip $ take n rolls (qs@(q:_),b) = last rs in ps ++ takeWhile (< q^2) qs (BTW I've noticed that when I reply in Gmane, all the text below double hyphen, if present in the post to which I'm replying, just disappears. This may be an artefact of some specific browser.) _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe