我正在尝试制作一个相对简单的功能,它几乎是连续的但有点扭曲。它应该是二进制或每个列表的最后和第一个元素,并在过程中组合它们。我正在努力学习编写可以利用Data.List.Stream
中的Stream Fusion功能的代码我检查了base中的concat执行了所需的操作并且懒惰地构建列表,但是,我创建的这个版本没有。在基数中,concat指定如下:
concat :: [[a]] -> [a]
concat = foldr (++) []
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys
这是我的代码:
bconcat :: [[Word8]] -> [Word8]
bconcat = foldr1 bappend
bappend :: [Word8] -> [Word8] -> [Word8]
bappend as bs = init as ++ (last as .|. head bs) : tail bs
我的问题是,如何写这个以便列表是懒惰的?我甚至尝试通过模仿基础中的(++)定义来编写bappend,但它没有什么区别。
目前,我使用以下代码,它按照我想要的方式工作,但性能落后于concat。此外,它使用我想避免的显式递归。
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
所以,我的问题是,我如何编写这段代码,以便它懒惰地构建列表而没有明确的递归?
感谢您的时间。
修改
目前,我的主要兴趣是使用标准组合器制作干净,简洁和可靠的代码。我仍然是一个具有功能性思维的初学者,并且希望看到一种合理有效的方式将它们用于此处。
答案 0 :(得分:4)
你的定义对我来说很严格。例如,尝试评估
length $ bconcat [[1,2,3],[4,undefined,6]]
但是你可能正在为。|建立thunk。表达。也许你想强迫它。
如果他们不能自动融合,你总能自己融合:
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
答案 1 :(得分:2)
你似乎有一个错字,'bapennd'应该是'bappend'在第一个片段中。我认为最后一个片段中的'combine'应该是'bconcat'。
它看起来像第一个(foldr1 bappend)会相当懒惰。你能详细说明缺乏懒惰吗?
至于同时需要“init xs”和“last xs”,你可能需要单次遍历xs:
let un [] = error "no last element"
un (x:[]) = ([],x)
un (x:xs) = let (ys,z) = un xs
in (x:ys,z)
这有不同的空间和时间权衡。如果你总是按顺序强制bconcat的答案那么它可能是一个改进。 “last xs”保存对整个列表的头部的引用,并防止xs被强制收集,直到被强制为止。
答案 2 :(得分:2)
我可以通过写下来改变奥古斯都的答案,以便更接近地匹配原始问题:
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
以上忽略了任何空的内部列表。