Stefan Kahrs <[EMAIL PROTECTED]> writes:
> I liked Mark's version, but let me add a related challenge.
>
> I defined a translation of //-list comprehensions
> that is analogous to the foldr-translation of list comprehensions
> (which optimises the map-concat approach) but works in the infinite case
> as well.
>
> This requires an operator foldrinf, of type:
>
> foldrinf :: (a -> [b] -> [b]) -> [a] -> [b] -> [b]
>
This is actually
foldrinf :: (a -> [b] -> [b]) -> [b] -> [a] -> [b]
> The idea is that the [a] list is our input, and the
> [b] list is an already-built result which we have to interleave
> with putting further elements into this using the input list
> and our operation (the first paramater).
>
> A translation using this operator would be something like this:
> start with
>
> pairdiag = [(x,y) // x<-[1..], y<-[2..]]
>
> and the translation gives
> pairdiag = foldrinf newop1 [] [1..]
> newop1 x y = foldrinf (newop2 x) y [2..]
> newop2 x y z = (x,y) : z
>
> And diag itself can be written as:
> diag xs = [ x // y<-xs, x<-y ]
> which translates into:
> diag xs = foldrinf op [] xs
> op x y = foldrinf (:) y x
>
> The operator foldrinf can be defined as follows:
>
> foldrinf op ys xs =
> folly ys xs 0
> where
> folly ys (x:xs) n = op x (aux n ys)
> where aux 0 bs = folly bs xs (n+1)
> aux n (b:bs) = b : aux (n-1) bs
> aux n [] = foldr op [] xs
> folly ys [] n = ys
>
> But, of course, this is ugly, with numbers and stuff...
> So the challenge is to clean up this version so that it
> looks just as nice as Mark's diag operator.
>
What the hell is going on here? ;-)
Trying to understand, I first strived to make aux more self-contained:
DiagSK1:
> diag xs = foldrinf op [] xs
> op x y = foldrinf (:) y x
> foldrinf :: (a -> [b] -> [b]) -> [b] -> [a] -> [b]
> foldrinf op ys xs =
> folly ys xs 0
> where
> folly ys [] n = ys
> folly ys (x:xs) n = op x (aux n n ys xs)
> where aux m 0 bs [] = bs
> aux m 0 bs (x:xs) = op x (aux (m+1) (m+1) bs xs)
> aux m n (b:bs) xs = b : aux m (n-1) bs xs
> aux m n [] xs = foldr op [] xs
Then eliminate folly and make aux fully independent:
DiagSK2:
> diag xs = foldrinf op [] xs
> op x y = foldrinf (:) y x
> foldrinf :: (a -> [b] -> [b]) -> [b] -> [a] -> [b]
> foldrinf op ys [] = ys
> foldrinf op ys (x:xs) = op x (aux op 0 0 ys xs)
> aux :: (b -> [c] -> [c]) -> Integer -> Integer -> [c] -> [b] -> [c]
> aux op m 0 bs [] = bs
> aux op m 0 bs (x:xs) = op x (aux op (m+1) (m+1) bs xs)
> aux op m n [] xs = foldr op [] xs
> aux op m n (b:bs) xs = b : aux op m (n-1) bs xs
This allows to play around with aux, understand what it does,
and reprogram it:
DiagSK3:
> diag xs = foldrinf op [] xs
> op x y = foldrinf (:) y x
> foldrinf :: (a -> [b] -> [b]) -> [b] -> [a] -> [b]
> foldrinf op ys [] = ys
> foldrinf op ys (x:xs) = op x (aux op split0 split1 ys xs)
> type Split a = [a] -> ([a] -> [a] , [a])
> split0, split1 :: Split a
> split0 l = (id,l)
> split1 = sshift split0
> sshift :: Split a -> Split a
> sshift split l = let p@(h,t) = split l
> in case t of
> [] -> p
> (x:xs) -> ((h . (x :)) , xs)
> aux :: (a -> [b] -> [b]) -> Split b -> Split b -> [b] -> [a] -> [b]
> aux op init follow bs xs =
> let (bh,bt) = init bs
> in bh (if null bt
> then foldr op [] xs
> else case xs of
> [] -> bt
> (x : xs) -> op x (aux op follow (sshift follow) bt xs))
Now we can observe that the to split arguments always follow each other,
so we can eliminate one:
DiagSK4:
> diag xs = foldrinf op [] xs
> op x y = foldrinf (:) y x
> foldrinf :: (a -> [b] -> [b]) -> [b] -> [a] -> [b]
> foldrinf op ys [] = ys
> foldrinf op ys (x:xs) = op x (aux op split0 ys xs)
> type Split a = [a] -> ([a] -> [a] , [a])
> split0 :: Split a
> split0 l = (id,l)
> sshift :: Split a -> Split a
> sshift split l = let p@(h,t) = split l
> in case t of
> [] -> p
> (x:xs) -> ((h . (x :)) , xs)
> aux :: (a -> [b] -> [b]) -> Split b -> [b] -> [a] -> [b]
> aux op split bs xs =
> let (bh,bt) = split bs
> in bh (if null bt
> then foldr op [] xs
> else case xs of
> [] -> bt
> (x : xs) -> op x (aux op (sshift split) bt xs))
Unfortunately, however, this time it wasn't really worth all the effort:
20000 200000 2000000
DiagMPJ 0:00.16 0:02.32 0:37.55
DiagMPJ1 0:00.12 0:01.50 0:23.83
DiagMPJ2 0:00.12 0:01.50 0:24.35
DiagWK1 0:00.12 0:01.34 0:19.02
DiagWK2 0:00.12 0:01.35 0:19.09
DiagWK3 0:00.12 0:01.34 0:18.82
DiagSK 0:01.01 0:45.25
DiagSK3 0:01.13 1:35.33
DiagSK4 0:01.17 1:35.77
We can claw back a little bit by using a more sophisticated split type:
DiagSK5 0:00.69 0:47.19
DiagSK5:
> diag xs = foldrinf op [] xs
> op x y = foldrinf (:) y x
> foldrinf :: (a -> [b] -> [b]) -> [b] -> [a] -> [b]
> foldrinf op ys [] = ys
> foldrinf op ys (x:xs) = op x (aux op split0 ys xs)
> type Split a = ([a] -> [a]) -> ([a] -> [a]) -> ([a] -> [a])
> split0 :: Split a
> split0 pt ph l = ph (pt l)
> sshift :: Split a -> Split a
> sshift split pt ph [] = ph (pt [])
> sshift split pt ph (x : xs) = split pt (ph . (x:)) xs
> aux :: (a -> [b] -> [b]) -> Split b -> [b] -> [a] -> [b]
> aux op split bs xs =
> let pt bt = if null bt
> then foldr op [] xs
> else case xs of
> [] -> bt
> (x : xs) -> op x (aux op (sshift split) bt xs)
> in split pt id bs
This should now be even more cryptic than the original version ;-)
Have fun!
Wolfram