I'm trying to make a relatively simple function that's almost concat but with a little twist. It's supposed to binary or together the last and first elements of each list and combine them in the process. I'm working to learn to write code that can take advantage of the Stream Fusion capabilities in Data.List.Stream
I checked that the concat in base does what is needed and builds the list lazily, however, this version I created doesn't. in the base, concat is specified as follows:
concat :: [[a]] -> [a]
concat = foldr (++) []
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys
Here's my code:
bconcat :: [[Word8]] -> [Word8]
bconcat = foldr1 bappend
bappend :: [Word8] -> [Word8] -> [Word8]
bappend as bs = init as ++ (last as .|. head bs) : tail bs
The question I have is, how do I write this so the list is built lazily? I even tried writing bappend by mimicing the (++) definition in base but it didn't make a difference.
At the moment, I use the following code, it works the way I want but the performance falls behind concat. Also, it uses explicit recursion which I'd like to avoid.
bconcat :: [[Word8]] -> [Word8]
bconcat (a:b:xs) = init a ++ bconcat ((bappend (last a) b):xs)
bconcat (a:[]) = a
bconcat [] = []
bappend :: Word8 -> [Word8] -> [Word8]
bappend !a bs = (a .|. head bs) : tail bs
So, the question I have is, how do I write this code so it builds the list lazily and without explicit recursion?
Thank you for your time.
Edit:
My primary interest, for the moment, is making clean, concise and understable code with the standard combinators. I'm still very much a beginner with functional thinking and would love to see a reasonably efficient way of putting them to use here.
Your definition looks strict to me. For instance, try evaluating
length $ bconcat [[1,2,3],[4,undefined,6]]
But you could be building up thunks for the .|. expression. Perhaps you want to force that.
You could always fuse things yourself if they don't fuse well automatically:
bconcat [] = error "bconcat: empty outer list"
bconcat (xs:xss) = loop xss xs
where loop [] ys = ys
loop ((x:xs):xss) [y] = let z = x .|. y in seq z $ z : loop xss xs
loop ([]:_) _ = error "bconcat: empty inner list"
loop xss (y:ys) = y : loop xss ys
I can change the answer by augustuss to more closely match the original problem by writing:
bconcat2 [] = []
bconcat2 (xs:xss) = loop xss xs
where loop [] ys = ys
loop xss (y:[]) = gather xss y
loop xss (y:ys) = y : loop xss ys
loop (xs:xss) [] = loop xss xs
gather [] z = [z]
gather ([]:xss) z = gather xss z
gather ((x:[]):xss) z = gather xss $! z .|. x
gather ((x:xs):xss) z = let z' = z .|. x in z' : loop xss xs
The above ignores any empty internal lists.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With