Haskell noob在这里:我仍然试图理解语言的机制,所以如果我的问题很简单,请原谅我并指出一些我可以学习的链接(我已经搜索过了)在stackoverflow上有类似主题的一段时间,但我仍然无法得到这个。)
我出来了这个功能:
chunks :: Int -> [a] -> [[a]]
chunks n xs
| length xs <= n = [xs]
| otherwise = let (ch, rest) = splitAt n xs in ch:chunks n rest
这样
ghci> chunks 4 "abracadabra"
["abra","cada","bra"]
ghci>
ghci> chunks 3 [1..6]
[[1,2,3],[4,5,6]]
我对此很满意,然后我想&#34;懒惰的评价!我甚至可以在无限的序列中使用它!&#34;。所以我尝试了take 4 $ chunks 3 [1..]
。我希望懒惰的haskell魔法会产生[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
,相反,似乎这个时候懒惰无法帮助我:它无法达到计算的终点(它是否全部走了方式到[1..]
结尾?)
我认为问题出在&#34;长度xs&#34; part:ghci似乎也被困在一个简单的length [1..]
上。所以我问:长度实际上是在迭代整个列表来给出响应吗?如果是这样,我想每次我尝试使用懒惰评估实现一些正常工作时,应该避免长度,所以有一些替代方案吗?
(例如,如何改进我的示例以使用无限列表?)
答案 0 :(得分:5)
是实际迭代整个列表的长度以给出响应吗?
是
我想每次尝试使用延迟评估实现一些正常的工作时,应避免长度
不仅如此,当懒惰不是因素时(如果O(1)检查通常足够 1 的情况下为O(n)),它也会给你带来不良的运行时间,所以你一般来说,大部分时间都应该避免使用它。
如何改进我的示例以使用无限列表?
您不需要检查列表的长度是否小于n
,您只需要检查它是否为零。而且您可以使用简单的模式匹配。
1 例如f xs | length xs >= 2 = ...
之类的东西(O(n))可以替换为f (x1 : x2 : xs) = ...
,即O(1)。
答案 1 :(得分:4)
你可以做的另一个技巧(我在Data.Text
看过,但我很惊讶的不是一般列表的前奏)是通过返回使length
尽快短路Ordering
而不是Bool
。
compareLength :: [a] -> Int -> Ordering
compareLength [] n = compare 0 n
compareLength _ 0 = GT
compareLength (x : xs) n = compareLength xs (n - 1)
然后您可以在chunks
中使用它。
chunks :: Int -> [a] -> [[a]]
chunks n xs = case compareLength xs n of
LT -> [xs]
_ -> let (ch, rest) = splitAt n xs in ch:chunks n rest
这很好用。
*Main> take 4 $ chunks 3 [1..]
[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
对于这种特殊情况,其他实现可能更惯用,但希望这是一个很好的技巧。
答案 2 :(得分:3)
是实际迭代整个列表的长度以给出响应吗?
是的,绝对的。
每次我尝试使用延迟评估实现一些良好的工作时,应该避免长度
是的,绝对。
所以还有其他选择吗?
是:在不引用length
的情况下解决问题。没有解决问题的一般方法,因此您需要解决每个特定情况。
你是一名铁路工人。如果汽车从您站立的地方开始并延伸到地平线上,那么这是一辆巨大的火车。你不知道它在哪里结束,如果有的话。你的工作是把它分成三列车的小火车。你怎么办?如何改进我的示例以使用无限列表