【问题标题】:Understanding direct self-reference in Haskell理解 Haskell 中的直接自引用
【发布时间】:2015-08-27 11:53:01
【问题描述】:

我刚开始学习 Haskell 几个小时,试图理解这是什么 The Fibonacci sequence 确实:

fibs = 0 : 1 : next fibs
  where
    next (a : t@(b:_)) = (a+b) : next t

next 函数对我来说很奇怪,它最终会得到一些“无效”的输入,就像一开始它是这样的:

next (0:1) = (0+1) : next [1]

但是next ([1]) 是不可操作的,因为t@(b:_) 没有输入。那么next 是如何工作的呢?

我的下一个困惑是fib 本身,因为它假设是一个斐波那契数列,我假设它会在第一步之后得到fibs = 0 : 1 : 1 : next fibs,但是我们需要计算next([0, 1, 1]) 女巫给出(0+1): next([1, 1]) == 1 : next([1, 1]),我们得到初始元素 1,所以在 next([0, 1, 1]) 中,列表的第一个值(在下一个 fib 中)将是 1,但是将此 1 附加到原始 fib,我们得到0 : 1 : 1 : 1,即不是斐波那契数列。

我想我误解了一些东西,那么它实际上是如何工作的?

【问题讨论】:

  • 第一行中对fibs 的引用指的是整个(无限)列表,而不仅仅是明确定义的两个元素。第一步不是next (0:1),而是next (0:1:next fibs),计算结果为(0+1) : next (1:next fibs)。从不涉及像 [0, 1, 1] 这样的有限列表。

标签: haskell


【解决方案1】:

定义递归定义结果的标准方法是从undefined 开始近似该值,并从那里展开递归,如下所示:

-- A function describing the recursion
f x = 0 : 1 : next x

fibs0 = undefined
fibs1 = f fibs0 = 0 : 1 : next undefined 
      -- next requires at least 2 elements
      = 0 : 1 : undefined
fibs2 = f fibs1 = 0 : 1 : next fibs1
      = 0 : 1 : next (0 : 1 : undefined)
      = 0 : 1 : 1 : next (1 : undefined)
      -- next requires at least 2 elements
      = 0 : 1 : 1 : undefined
fibs3 = f fibs2 = 0 : 1 : next fibs2
      = 0 : 1 : next (0 : 1 : 1 : undefined)
      = 0 : 1 : 1 : next (1 : 1 : undefined)
      = 0 : 1 : 1 : 2 : next (1 : undefined)
      -- next requires at least 2 elements
      = 0 : 1 : 1 : 2 : undefined
fibs4 = f fibs3 = 0 : 1 : next fibs3
      = 0 : 1 : next (0 : 1 : 1 : 2 : undefined)
      ...

如果我们继续前进,我们将“在极限”接近完整序列,逐步逼近它。这种非正式的论证可以通过 Kleene 不动点定理正式证明。

【讨论】:

  • 澄清一下,这么直观的想,fib是序列[a0, a1 ,a2...] (=x) 实际上满足x = [0,1, (next x)] (以数学方式),next(=g)是实际满足g([a,b0,b1,b2....])=[a+b0, g([b0, b1 ,b2...])]的函数,而这样的x and g是存在且唯一的,并且可以通过近似(通过重复输入值)获得,这是由Kleene不动点定理保证的?
  • @CYC 大致是的。 fibnext 都是通过递归定义的,因此从技术上讲,需要在列表域 (fib) 和从列表到列表的函数域 (next) 应用 Kleene 两次。上面我更精确地展示了列表域中的近似值,而我没有对next 进行相同级别的详细说明,而是使用直觉进行工作。事实上,即使忽略固定点,您也可以通过简单地扩展 fibsnext 的定义得到一些结果,假装它们定义的递归方程成立。
【解决方案2】:

next 函数实际上生成了fibs - 所以它不会调用next [0, 1, 1] 它会调用next (0 : 1 : 1 : next rest)

图片中有发生的事情:

fib = 0 : 1 : not-yet-evaluated-part

fib = 0 : 1 : [+] : not-yet-evaluated-part
      ^   ^    |
      *---*----* (applying next to fib)

fib = 0 : 1 : 1 : [+] : not-yet-evaluated-part
          ^   ^    |
          *---*----* (next calls itself)

fib = 0 : 1 : 1 : 2 : [+] : not-yet-evaluated-part
              ^   ^    |
              *---*----* (etc)

next 是“铁路建设列车”,需要一些初始轨道才能运行 (0 : 1 : ...)。

因为[]-end of list 在 fibs 列表中没有任何位置,所以它会变成无限的。

但是,我建议您从不太晦涩的事物开始 - 例如,您应该尝试单独理解列表。

【讨论】:

  • 递归调用不会得到整个列表,而只会得到一个尾部。并且只有一个递归调用而不是两个。
猜你喜欢
  • 1970-01-01
  • 2018-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多