【问题标题】:Ranges in Haskell (GHCi)Haskell (GHCi) 中的范围
【发布时间】:2011-12-18 23:32:11
【问题描述】:

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

  1. 从 1 到 171 按 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]
    

为什么?

【问题讨论】:

    标签: haskell


    【解决方案1】:

    你有稍微偏离范围的意思。 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]   
    

    请注意,在第一种情况下,列表以二为单位计数,在第二种情况下,列表以三为单位计数。这是因为第一项和第二项之间的差异分别是二和三。在您的语法中,您尝试编写 [first,step..last] 以获取列表 [first,first+step,first+2*step,...,last];但是,这样的范围的步长实际上是前两个数字之间的差异。没有第二个元素,步长总是一;如果没有最终元素,列表将永远存在(或直到达到该类型的最大/最小元素)。

    因此,让我们看一下您的三个示例:

    • [17,1..171] == []。由于您指定了17,1,Haskell 看到列表的前两个元素应该是十七和一,所以您必须按-16 计数。在这种情况下,Haskell 想要在元素比最后一个元素 时立即停止——但它们以这种方式开始,因此不会产生任何元素。要加一,您需要 [17,18..171](列表的前两个元素是 17 和 18),或者只是 [17..171]

    • [17, 17..171111] == repeat 17。这个很好玩由于列表的前两个元素都是17,Haskell 确定你必须从零开始计数——它会很高兴地继续计数,直到结果超过171111。当然,当从零开始计数时,这永远不会发生,因此您会得到一个无限的 17 列表。要计数到 17,您可以使用 [17,34..171111],如果您认为这样更清楚,可以使用 [17,17+17..171111]

    • take 54 [171,234..] 对比take 54 [171,244..]。我不确定你在这里期待什么行为,但他们每个人所做的都与上面相同:第一个返回一个包含 54 个整数的列表,从 171 开始,按 234 - 171 = 63 计数;第二个返回一个由 54 个整数组成的列表,从 171 开始,按 244 - 171 = 73 计数。每个列表无限远(或者至少直到maxBound,如果列表是有限的Ints 并且不是任意大的Integers),因此您只需请求前五十四个元素。

    有关范围语法含义的一些更详细的信息(它已转换为 Enum 类型类中的函数),包括浮点数范围的稍微令人惊讶的行为,hammar has a good answer to another question

    【讨论】:

      【解决方案2】:

      嗯,这些操作的语义与您预期的有点不同。构造 [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,如果dc - a 有不同的符号(在这种情况下,列表将是无限的,因此没有输出),或者直到达到maxBoundminBound ,则输出结束。 (当然,这是以不同的方式实现的,因为我们在这里使用了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 类似,只是没有边界条件,因此如果您的Enum 是无限的,那么结果列表也是如此.

      【讨论】:

        【解决方案3】:

        我也对这种行为感到有些惊讶,因此我编写了一个对我来说更自然的范围函数(也许对你来说也是如此):

        range step start end = takeWhile (<=end) $ iterate (+step) start
        

        引用你的例子:

        从 1 到 171 按 17 计数

        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, ...

        【讨论】:

          【解决方案4】:

          我也被这个教程弄糊涂了:教程使用了step这个词,没有解释,在我看来并不是我认为的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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-04-17
            • 2011-10-11
            • 2015-01-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多