我正在阅读Learn You A Haskell for Great Good。 His examples [2,2..20]
和[3, 6..20]
工作正常,但我得到了三个奇怪的结果:
[17, 1..171]
产生空列表。[17, 17..171111]
重复数字17
,直到我中断GHCi。 take 54 [171, 234..]
和take 54 [171, 244..]
之间有一个奇怪的区别:
ghci> take 54 [171, 234..]
[171,234,297,360,423,486,549,612,675,738,801,864,927,990,1053,1116,1179,1242,1305,1368,1431,1494,1557,1620,1683,1746,1809,1872,1935,1998,2061,2124,2187,2250,2313,2376,2439,2502,2565,2628,2691,2754,2817,2880,2943,3006,3069,3132,3195,3258,3321,3384,3447,3510]
ghci> take 54 [171, 244..]
[171,244,317,390,463,536,609,682,755,828,901,974,1047,1120,1193,1266,1339,1412,1485,1558,1631,1704,1777,1850,1923,1996,2069,2142,2215,2288,2361,2434,2507,2580,2653,2726,2799,2872,2945,3018,3091,3164,3237,3310,3383,3456,3529,3602,3675,3748,3821,3894,3967,4040]
为什么?
答案 0 :(得分:47)
你有略微偏离范围的含义。 Haskell范围语法是以下四种方法之一:[first..]
,[first,second..]
,[first..last]
,[first,second..last]
。 Learn You A Haskell中的示例是
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
ghci> [3,6..20]
[3,6,9,12,15,18]
请注意,在第一种情况下,列表按两次计数,在第二种情况下,列表按三次计算。那是因为第一和第二项之间的差异分别是2和3。在您的语法中,您尝试编写[first,step..last]
以获取列表[first,first+step,first+2*step,...,last]
;但是,这样的范围的步长实际上是前两个数字之间的差异。没有第二个元素,步长总是一个;如果没有最终元素,列表将永远持续(或直到达到类型的最大/最小元素)。
因此,让我们看看你的三个例子:
[17,1..171] == []
。由于您指定了17,1
,因此Haskell看到列表的前两个元素应该是17和1,因此您必须按-16
计算。在这种情况下,Haskell想要在元素小于而不是最后一个元素时停止 - 但它们以这种方式开始,因此不会产生任何元素。要按一个计算,您需要[17,18..171]
(列表的前两个元素是17和18),或者只是[17..171]
。
[17, 17..171111] == repeat 17
。这个很有趣。由于列表的前两个元素都是17
,因此Haskell确定您必须按零计数,并且它会很乐意继续计数,直到结果超过171111
。当然,当计数为零时,这将永远不会发生,因此您将得到七个无限的列表。要计算十七,如果您认为更清楚,则需要[17,34..171111]
或[17,17+17..171111]
。
take 54 [171,234..]
vs。 take 54 [171,244..]
。我不确定你在这里期待什么行为,但是他们每个人都在做同样的事情:第一个返回一个包含54个整数的列表,从171
开始并按{{1}计算};第二个返回一个包含54个整数的列表,从234 - 171 = 63
开始,按171
计算。如果列表是有限244 - 171 = 73
而不是任意大maxBound
),则每个列表都会无限远(或者至少等到Ints
,因此您只需要请求前五十四个元件。
关于范围语法意味着什么(它被翻译成Integers
类型类中的函数)的一些更细微的细节,包括对浮点数范围的略微惊人的行为,hammar has a good answer to another question
答案 1 :(得分:11)
嗯,这些操作的语义与您预期的略有不同。构造[a,b..c]
实际上只是enumFromThenTo a b c
的语法糖,其行为有点像这样:
计算d = b - a
。 [a,b..c]
的输出为[a,a+d,a+d+d,a+d+d+d,...]
。如果a+n*d > c
和d
有不同的符号(在这种情况下,列表将是无限的,因此没有输出),或者直到{{1},这一过重复,直至c - a
。达到或maxBound
,然后输出结束。 (当然,这是以不同的方式实现的,因为我们在这里使用了minBound
的任意实例。
因此Enum
变为[1,3..10]
,自[1,3,5,7,9]
起,17 - 17 = 0
产生[17, 17..171111]
。通过这个稍微复杂的规则,[17,17+0,17+0+0...]
产生空列表。
要添加:[17, 1..171]
是使用函数[x,y..]
实现的,其行为与enumFromThen x y
类似,但没有边界条件,因此如果enumFromThenTo
是无限的,结果列表也是如此。
答案 2 :(得分:2)
我也对这种行为感到有些惊讶,所以我写了一个对我来说感觉更自然的范围函数(也许对你来说也是如此):
range step start end = takeWhile (<=end) $ iterate (+step) start
引用你的例子:
按17分从1到171
计算
由range 17 1 171
完成,生成[1,18,35,52,69,86,103,120,137,154,171]
从17年到1711111年由17岁计算
由range 17 17 1711111
完成,生成[17,34,51,68,85, ...
答案 3 :(得分:0)
我也对本教程感到困惑:本教程使用了单词step,这是没有解释的,在我看来并不是我认为的一步。然后它显示了很容易被误解的例子。因为[2,4..20]
看起来像是从4开始的第2步。
线索在输出中:
ghci> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
如果你仔细看(我没有)。它意味着从2开始,下一个是4,从(4 - 2)开始隐式步骤,继续输出数字,步长为2到最多20个。
"ghci>" [1,6..20]
[1,6,11,16]
注释20不输出,因为16 + 5大于20