【问题标题】:What is going on in this function (haskell)?这个函数(haskell)发生了什么?
【发布时间】:2014-01-25 11:53:15
【问题描述】:

我有这个我不太明白的haskell函数。

ns :: [Integer]
ns = 0 : [n+k | (n, k) <- zip ns [1,3..]]

我被要求“花费 3 ns”。

我认为 ns 是恒定的,所以它只会与列表的第一个元素一起压缩,给出 (0,1)。然后添加时给出 1 的答案。然后它说“需要 3 ns”所以我用列表的前 5 个元素压缩 0,给出... (0,1),(0,3), (0,5 ) 然后添加后,我得到 [1,3,5] 的最终答案。然而,这不是正确的答案。

ns 到底发生了什么?我很难理解...

【问题讨论】:

  • ns 是常量,因为它是一个不带参数的函数,并且是纯函数,因此它的值完全取决于 0 个参数的值; 0 个参数可以恰好有 0 个唯一值;也就是说,它的价值不依赖任何东西。 “恒定”并不意味着“评估微不足道”或“有限”或类似的东西。 ns 中发生的事情与 x = 1:x 中发生的事情相同。很明显'x'的第一个值是1,很明显x的下一个值是x的第一个值,也就是1,之后的下一个值是x的第一个值……等
  • 没有零参数的函数,所有函数都有一个参数。

标签: list haskell list-comprehension


【解决方案1】:
ns :: [Integer]
ns = 0 : [n+k | (n, k) <- zip ns [1,3..]]

这是一个核心递归数据定义。 ns 是一个常量,一个列表,但它是通过访问“充实”的,因为 Haskell 是惰性的。

插图:

1      n1 n2 n3 n4 n5 ...  -- the list ns, [n1,n2,n3,...],
2      0  1  4 ...         -- starts with 0
3      -----------------
4      1  3  5  7  9       -- [1,3..]
5      -----------------
6      1  4 ...            -- sum the lines 2 and 4 pairwise, from left to right, and
7      n2 n3 n4 n5 ...     -- store the results starting at (tail ns), i.e. from n2

我们可以准确地看到 访问是如何逐步强制列表 ns 存在的,例如在print $ take 4 ns 之后,通过命名临时实体:

ns :: [Integer]
ns = 0 : [n+k | (n, k) <- zip ns [1,3..]]

ns = 0 : tail1
tail1 = [n+k | (n, k) <- zip ns [1,3..]]
      = [n+k | (n, k) <- zip (0 : tail1) [1,3..]]
      = [n+k | (n, k) <- (0,1) : zip tail1 [3,5..]]
      = 1 : [n+k | (n, k) <- zip tail1 [3,5..]]
      = 1 : tail2

tail2 = [n+k | (n, k) <- zip (1 : tail2) [3,5..]]
      = [n+k | (n, k) <- (1,3) : zip tail2 [5,7..]]
      = 4 : tail3

tail3 = [n+k | (n, k) <- zip (4 : tail3) [5,7..]]
      = 9 : tail4

tail4 = [n+k | (n, k) <- zip (9 : tail4) [7,9..]]
------    
ns = 0 : 1 : 4 : 9 : tail4

【讨论】:

    【解决方案2】:

    相当于ns = 0 : (zipWith (+) ns [1,3,...]),可能更容易理解:第k+1个元素是第k个元素加上第k个奇数,有合适的起始条件。

    【讨论】:

      【解决方案3】:

      Haskell 懒惰地评估事物,因此它只会精确计算所需的值。这意味着我们需要以某种方式需要 ns 的值来查看它是如何计算的。

       head ns
       head (0 : ...)
       0
      

      显然,head 并不足以让任何有趣的事情发生,但您已经可以看到 ns 的有趣部分只是被丢弃了。当我们要求更多时,这种效果会更进一步,例如打印每个元素。让我们一个接一个地强制每个元素来查看模式。首先,让我们用一个等效的函数调用替换列表推导

      zipWith f []      _     = []
      zipWith f _      []     = []
      zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
      
      ns = 0 : zipwith (+) ns [1,3..]
      

      现在我们可以一一评估ns 的元素。实际上,更详细地说,我们正在评估ns 并确定第一个构造函数是(:),然后决定评估(:) 的第二个参数作为下一步。我将使用 {...} 来表示尚未评估的 thunk。

      ns
      { 0 } : zipWith (+) ns [1,3...]
      { 0 } : zipWith (+) ({ 0 } : { ... }) [1,3...]   -- notice the { 0 } thunk gets evaluated
      0     : { 0 + 1 } : zipWith f { ... } [3,5...]
      0     : 1         : { 1 + 3 } : zipWith f { ... } [5,7...]
      0     : 1         : 4         : { 4 + 5 } : zipWith f { ... } [7,9...]
      

      上面需要注意的重要一点是,由于ns 只被逐个评估,它从不要求知道尚未计算的内容。这样,ns 本身就形成了一个紧密而巧妙的小循环。

      【讨论】:

        【解决方案4】:

        Haskell 中的所有内容都是惰性的,因此虽然 ns 是常量,但这并不意味着以后不能“添加”(或更准确地说,“计算”)列表中的项目。此外,由于 ns 是递归定义的,因此列表中较晚出现的值可能取决于列表中较早出现的值。

        让我们一步一步来。

        首先,我们知道ns以0开头,所以暂时ns看起来是这样的:

        ns: 0, ?, ?, ...
        

        那么第一个问号是什么?根据你的函数,它是n + k,其中nns 中的第一个元素,k[1, 3..] 中的第一个元素。所以n = 0k = 1n + k = 1

        ns: 0, 1, ?, ...
        

        继续,下一个元素也是n + k,这里我们使用ns[1, 3...]的第二个元素。我们现在知道ns 的第二个元素是1,所以n = 1k = 3n + k = 4

        ns: 0, 1, 4, ...
        

        等等。

        【讨论】:

          【解决方案5】:

          haskell 是惰性的,所以你可以有递归定义。在这里,它被布置了。

          ns = 0 : something
          
          (n,k) <- zip (0 : something ) [1,3,5,7...]
          (n,k) <- [(0,1) : something )
          
          ns = 0 : 1 : something
          
          (n,k) <- zip ( 0 : 1 : something ) [3,5,7...]
          (n,k) <- (0,1) : (1,3) : something
          
          ns = 0 : 1 : 4 : something
          
          (n,k) <- zip ( 0 : 1 : 4 : something ) [5,7...]
          (n,k) <- (0,1) : (1,3) : (4,5) : something
          
          ns = 0 : 1 : 4 : 9 : something
          
          ....
          

          看看我们如何确定下一个元组是什么,然后添加它的两个元素。这使我们能够确定下一个元素。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-05-12
            • 1970-01-01
            • 2017-09-09
            • 2020-03-12
            • 1970-01-01
            相关资源
            最近更新 更多