为什么我的函数不适用于无限列表?
Why does my function not work with an infinite list?
我正在尝试学习 haskell 并实现了一个函数 conseq
,它将 return 大小为 n 的连续元素列表。
conseq :: Int -> [Int] -> [[Int]]
conseq n x
| n == length(x) = [x]
| n > length(x) = [x]
| otherwise = [take n x] ++ (conseq n (drop 1 x))
这工作正常。
> take 5 $ conseq 2 [1..10]
[[1,2],[2,3],[3,4],[4,5],[5,6]]
但是,如果我传递 [1..]
而不是 [1..10]
,程序就会陷入无限循环。
据我了解,haskell 有惰性评估,所以我应该仍然能够得到相同的结果,对吗?是length
吗?一旦长度大于 n
,前两个条件是否应该评估为 false?
我误会了什么?
您的函数做的第一件事是计算 length(x)
,因此它知道它应该 return [x]
、[x]
还是 [take n x] ++ (conseq n (drop 1 x))
length
计算列表中元素的数量 - 所有元素。如果你问一个无限列表的长度,它永远不会计数完。
使用 length
不是一个好主意的主要原因之一是当它必须在无限列表上计算时,它会陷入无限循环。
好消息是,我们不需要 length
。这也会使时间复杂度变得更糟。我们可以使用两个枚举器,一个在另一个前面 n-1 个位置。如果这个枚举器到达列表的末尾,那么我们知道第一个枚举器仍然有 n-1 个元素,因此我们可以停止产生值:
conseq :: Int -> [a] -> [[a]]
conseq n ys = go (<b>drop (n-1)</b> ys) ys
where go [] _ = []
go (_:as) ba@(~(_:bs)) = take n ba : go as bs
这给了我们:
Prelude> conseq 3 [1 ..]
[[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8],[7,8,9],[8,9,10],[9,10,11],[10,11,12],[11,12,13],[12,13,14],[13,14,15],[14,15,16],[15,16,17],[16,17,18],[17,18,19],[18,19,20],[19,20,21],[20,21,22],[21,22,23],[22,23,24],[23,24,25],[24,25,26],[25,26,27],…
Prelude> conseq 3 [1 .. 4]
[[1,2,3],[2,3,4]]
我正在尝试学习 haskell 并实现了一个函数 conseq
,它将 return 大小为 n 的连续元素列表。
conseq :: Int -> [Int] -> [[Int]]
conseq n x
| n == length(x) = [x]
| n > length(x) = [x]
| otherwise = [take n x] ++ (conseq n (drop 1 x))
这工作正常。
> take 5 $ conseq 2 [1..10]
[[1,2],[2,3],[3,4],[4,5],[5,6]]
但是,如果我传递 [1..]
而不是 [1..10]
,程序就会陷入无限循环。
据我了解,haskell 有惰性评估,所以我应该仍然能够得到相同的结果,对吗?是length
吗?一旦长度大于 n
,前两个条件是否应该评估为 false?
我误会了什么?
您的函数做的第一件事是计算 length(x)
,因此它知道它应该 return [x]
、[x]
还是 [take n x] ++ (conseq n (drop 1 x))
length
计算列表中元素的数量 - 所有元素。如果你问一个无限列表的长度,它永远不会计数完。
使用 length
不是一个好主意的主要原因之一是当它必须在无限列表上计算时,它会陷入无限循环。
好消息是,我们不需要 length
。这也会使时间复杂度变得更糟。我们可以使用两个枚举器,一个在另一个前面 n-1 个位置。如果这个枚举器到达列表的末尾,那么我们知道第一个枚举器仍然有 n-1 个元素,因此我们可以停止产生值:
conseq :: Int -> [a] -> [[a]]
conseq n ys = go (<b>drop (n-1)</b> ys) ys
where go [] _ = []
go (_:as) ba@(~(_:bs)) = take n ba : go as bs
这给了我们:
Prelude> conseq 3 [1 ..]
[[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8],[7,8,9],[8,9,10],[9,10,11],[10,11,12],[11,12,13],[12,13,14],[13,14,15],[14,15,16],[15,16,17],[16,17,18],[17,18,19],[18,19,20],[19,20,21],[20,21,22],[21,22,23],[22,23,24],[23,24,25],[24,25,26],[25,26,27],…
Prelude> conseq 3 [1 .. 4]
[[1,2,3],[2,3,4]]