为什么循环功能不能用于空列表?

时间:2014-06-14 18:56:10

标签: haskell

我在一些项目中使用了函数cycle,今天我发现它不是一个完整的函数,如GHCI示例所示:

λ> Data.List.cycle []
*** Exception: Prelude.cycle: empty list

我知道Haskells尝试使用总函数(基本函数headtail除外)并且我不完全确定为什么cycle不是他们。在我看来,空列表的cycle是空列表,我不会看到这个问题。为什么空列表的cycle会引发错误?

修改: 基于第一个答案,我认为我的想法并不完全清楚:我不希望cycle []成为一个永无止境的计算。相反,我认为cycle []应该是:

cycle :: [a] -> [a]
cycle [] = []
cycle xs = xs ++ cycle xs

[]cycle [],因为所有操作都完全正如我所做的那样。例如,take 3 [][],因此take 3 (cycle [])可能为[]。这个解决方案的问题是什么?

5 个答案:

答案 0 :(得分:7)

cycle实际上定义为为所有输入返回无限列表。如果它尝试使用空输入进行天真的操作,那么它将处于无限循环中。错误条件使用相同的指称语义稍微提供更多信息。

编辑:

由于当我说空输出不好时,人们似乎不明白我的意思,请考虑这个简单的功能:

labelElements :: [a] -> [b] -> [(a, b)]
labelElements labels elements = zip (cycle labels) elements

它非常简单,当标签列表为空时有明显的错误条件。如果循环在空输入上返回一个空列表,它会使labelElements默默地将该错误传播到其输出。此刻,它尖叫并大喊你搞砸了。其中一个比另一个好得多。

答案 1 :(得分:3)

在访问列表中的元素时会出现问题。在非空列表上运行的自定义循环函数在被访问时没有问题但是试图获取,例如,循环空列表的前3个元素导致无限循环:

cycle' xs = xs ++ cycle' xs

take 3 (cycle' [1,2]) -- returns [1,2,1]
take 3 (cycle' [])    -- still looping

答案 2 :(得分:3)

我对实施cycle功能的人的思维没有任何特别的了解。

prelude has the following to say about cycle

  

循环将有限列表绑定为循环列表,或等效地,将原始列表无限重复。这是无限名单上的身份。

传统上,当您想到循环链接列表时,wiki entry您有: Screenshot from wiki

我如何表达循环空列表?一个指针自己?但即使这样也不适合。

我最好的解释是循环列表不是普通列表。它们是具有不同语义的不同野兽。就像head实际上只在非空的空列表中完全定义一样,因为没有空列表的第一个元素,cycle仅在非空列表上完全定义,因为没有空的循环链接列表。

答案 3 :(得分:3)

请注意,它目前已定义,与tail一致。

tail [] = error ...

cycle在概念上与tail相关。当您cycle列表时,这意味着您可以反复查看其tail并且永远不会到达“结束”([]),因为它是一个循环。 (参见Davorak的图片。)换句话说,在tail列表中使用cycle始终是安全的,当然,假设在cycle上使用tail [] = [] cycle [] = [] 是安全的首先列出。

我认为,定义是完全合理的。

cycle

但您应该重新定义tail {{1}}以保持一致。

答案 4 :(得分:1)

文档中描述的cycle的意图是:

import Data.List.Nonempty
import Data.Stream.Infinite

cycle :: NonEmpty a -> Stream a

Prelude的作者使用部分函数传入一个空列表,这在概念上是一个类型错误,类似于headtail

如果你想要一个返回[]的循环,它就像以下一样简单:

myCycle :: [a] -> [a]
myCycle xs = if null xs then xs else cycle xs

有关NonEmpty的定义和Stream的定义以及semigroups的完整定义,请参阅:streams