Haskell 无限列表、惰性计算和长度

Haskell 无限列表、惰性计算和长度,haskell,lazy-evaluation,infinite,Haskell,Lazy Evaluation,Infinite,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

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
我对此非常满意,然后我想“有懒惰的评估!我可以在无限序列上使用它!”。所以我试着
拿4块$chunks 3[1..]
。我本希望懒惰的哈斯克尔魔法能产生
[[1,2,3],[4,5,6],[7,8,9],[10,11,12]
,但这一次似乎懒散帮不了我:它无法到达计算的终点(它会一直走到
[1..]
的终点吗?)

我认为问题出在“长度xs”部分:ghci似乎也在一个简单的
长度[1..]
上卡住了。所以我在问:length实际上是在迭代整个列表以给出响应吗?如果是这样的话,我想每次我尝试实现一些使用惰性评估的好方法时,长度都是要避免的,所以有其他选择吗? (例如,如何改进示例以处理无限列表?)

length实际上是在迭代整个列表以给出响应吗

是的,绝对是

每次我尝试使用惰性评估实现一些工作良好的东西时,都要避免使用长度

是的,绝对是

那么还有其他选择吗

是:在不参考
长度的情况下解决问题。没有解决问题的通用方法,因此您需要解决每个具体案例

如何改进示例以处理无限列表

你是铁路工人。一列巨大的火车,如果汽车从你站的地方开始,延伸到地平线上。你不知道它在哪里结束,如果有的话。你的工作是把它分成三节车厢的小火车。你如何进行

长度是否真的迭代整个列表以给出响应

是的,绝对是

每次我尝试使用惰性评估实现一些工作良好的东西时,都要避免使用长度

是的,绝对是

那么还有其他选择吗

是:在不参考
长度的情况下解决问题。没有解决问题的通用方法,因此您需要解决每个具体案例

如何改进示例以处理无限列表

你是铁路工人。一列巨大的火车,如果汽车从你站的地方开始,延伸到地平线上。你不知道它在哪里结束,如果有的话。你的工作是把它分成三节车厢的小火车。你如何进行

length实际上是在迭代整个列表以给出响应吗

我想每次我试图实现一些可以很好地使用惰性评估的东西时,都应该避免使用长度

不仅如此,当惰性不是一个因素时(在O(1)检查通常足够1的情况下为O(n)),它也会给您带来糟糕的运行时,因此您通常应该在大多数情况下避免它

如何改进示例以处理无限列表

您不需要检查列表的长度是否小于
n
,只需要检查它是否为零。你可以用一个简单的模式匹配


1例如,像
fxs | length xs>=2=…
,它是O(n),可以替换为
f(x1:x2:xs)=…
,它是O(1)

length实际上是在迭代整个列表以给出响应吗

我想每次我试图实现一些可以很好地使用惰性评估的东西时,都应该避免使用长度

不仅如此,当惰性不是一个因素时(在O(1)检查通常足够1的情况下为O(n)),它也会给您带来糟糕的运行时,因此您通常应该在大多数情况下避免它

如何改进示例以处理无限列表

您不需要检查列表的长度是否小于
n
,只需要检查它是否为零。你可以用一个简单的模式匹配


例如,像
fxs | length xs>=2=…
,也就是O(n),可以替换为
f(x1:x2:xs)=…
,也就是O(1)。

你可以做的另一个技巧(我在
Data.Text
中看到过,但令人惊讶的是,它并不是一般列表的前奏)是通过返回
顺序而不是
Bool
使
length
短路

compareLength :: [a] -> Int -> Ordering
compareLength [] n = compare 0 n
compareLength _ 0 = GT
compareLength (x : xs) n = compareLength xs (n - 1)
然后您可以在
块中使用它

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]]
对于这种特殊情况,其他实现可能更惯用,但希望这是一个很好的技巧。

您可以做的另一个技巧(我在
Data.Text
中看到过,但令人惊讶的是,它并不是列表的前奏)是通过返回
顺序而不是
Bool
使
length
短路

compareLength :: [a] -> Int -> Ordering
compareLength [] n = compare 0 n
compareLength _ 0 = GT
compareLength (x : xs) n = compareLength xs (n - 1)
然后您可以在
块中使用它

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]]

对于这种特殊情况,其他实现可能更惯用,但希望这是一个很好的技巧。

当然,回答您自己问题的方法是跟踪
长度的实现(请参阅haskell标记信息部分以获取参考资料),或者最好自己定义它,让自己相信它只能以一种方式表现。如果你觉得有冒险精神,尝试重新实现
splitAt
length
而不是
Int
,看看你是否能让你的
chunk
在无限列表中表现良好你可能想看的和懒惰类型——只是在
chunk
类型中将
Int
改为
Natural
,然后改变
长度
一般长度
将使