Haskell(GHCi)的范围

时间:2011-10-31 18:46:51

标签: haskell

我正在阅读Learn You A Haskell for Great GoodHis examples [2,2..20][3, 6..20]工作正常,但我得到了三个奇怪的结果:

  1. 从17到17计算:[17, 1..171]产生空列表。
  2. 从17到1711111计算17:[17, 17..171111]重复数字17,直到我中断GHCi。
  3. 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]
    
  4. 为什么?

4 个答案:

答案 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 > cd有不同的符号(在这种情况下,列表将是无限的,因此没有输出),或者直到{{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