Haskell 无限列表、惰性计算和长度
Haskell noob:我仍在努力理解语言的机制,所以如果我的问题很愚蠢,请原谅我,并给我指出一些我可以从中学习的链接(我在stackoverflow上搜索了一段时间类似的主题,但仍然找不到) 我提出了这个功能: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
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
,然后改变长度
到一般长度
将使