【问题标题】:Is this Fibonacci sequence function recursive?这个斐波那契序列函数是递归的吗?
【发布时间】:2010-10-30 04:40:48
【问题描述】:

考虑以下 (Haskell) 代码:

fib=0:1:zipWith (+) fib (tail fib)

一位同事试图断言这不是一个递归函数,因为fib 只是一个用自身定义自身的列表,并且在某种程度上不同于执行相同操作的函数。我认为他在吸食快克。

你怎么看?

【问题讨论】:

  • printf "哦,不,零参数的函数,我们不能拥有它!"或死;
  • zipWith 是递归函数,fib 指的是它的结果。它没有什么神奇或非递归的。 (下面声称“没有功能”的答案和你的同事一样困惑。)
  • 你的同事朋友正在挑剔。从技术上讲,它是一个递归定义的,而不是一个函数。
  • Piet,你的定义是非标准的。不要假设每个人都错了,因为他们都使用相同的定义,而这恰好与您的不匹配。
  • Carl:我不确定你指的是什么,但递归函数和递归数据结构在 Haskell 中定义得非常好。查看关于"Tying the Knot" 和相关主题的任意数量的论文和著作。

标签: haskell recursion


【解决方案1】:

zipWith 的斐波那契定义不是递归函数,实际上不涉及任何函数,fib 是一个延迟自定义的列表(数据),利用了 Haskell 的延迟语义。从某种意义上说,你可以称之为递归列表或递归数据;但不是递归函数。

你可能很难理解递归列表,因为很少有编程语言有任何接近的东西,但你会注意到在 Haskell 中所有函数都只接受一个参数。 fib 不带任何参数,因为它不是函数。由于不涉及函数,所以不能有递归函数。

你的同事没有吸食快克,他是开明的(或者吸食快克,如果这是你对开悟的定义)。

【讨论】:

  • @Piet Delport: fib 出现在它自己定义的右侧......你用什么词来形容它?我称之为“递归”。 zipWith 的递归性和功能性(功能性?)在这里都没有受到质疑。
  • pelotom:在 Haskell 中,递归 data 和递归 函数 之间存在根本区别。递归数据是当一个数据结构具体引用回它自己的构造函数时,就像你说xs = 1:2:3:xs 时发生的那样:最终的 cons 引用回最初的 cons,形成一个循环。有许多不同的方法可以实现这种数据递归:参见Tying the Knot。另一方面,递归函数就像zipWith。它们也可能定义或不定义递归数据结构,但在这种情况下,fib 不是。
  • @Piet:xs = 1:2:3:xs 是一个自引用定义,fib=0:1:zipWith (+) fib (tail fib) 也是如此。它们都是递归数据。有什么问题?
  • @Piet:fib 在其定义中使用zipWith 的事实既不存在也不存在。 fib 最肯定是递归数据,这可以通过它根据自身定义这一事实来证明。在等式的 RHS 上看到这两个 fib 实例吗?这意味着它是根据自身定义的,例如它是递归的。我不知道有多少种不同的方式可以重申这个基本观点。
  • @Piet Delport:递归数据是参考自身定义的数据;递归数据的一般形式为:recd = a1 : a2 : ... : an : (f recd)xs = 1 : 2 : 3 : xs 的情况只是f x = x 时的特例。因此fib是一个递归数据,当f x = zipWith (+) x (tail x)f 在内部使用递归函数(例如zipWith)是完全不相关的。
【解决方案2】:

天哪,这是多么微妙的术语区别。 “这”是什么?

fib=0:1:zipWith (+) fib (tail fib)

它不是递归函数。它不是递归数据。这是一个递归定义。

定义了什么?

fib

根据这个定义,fib 是什么类型的东西?

[Integer]

整数列表(或者可能是任何旧数字的列表)。

fib 是一个函数吗?不,这是一个列表。 fib 是递归定义的吗?是的。如果我们将zipWith 替换为相同类型的非递归函数(例如\ f xs ys -> xs),fib 会被递归定义吗?是的,虽然它会是一个不同的递归定义列表。

fib 是循环列表吗?不,“递归数据结构”是指“循环数据结构”吗?不是根据 Hoare 的论文“递归数据结构”:http://portal.acm.org/book_gateway.cfm?id=63445&type=pdf&bookpath=%2F70000%2F63445%2Fcb-p217-hoare.pdf&coll=&dl=&CFID=15151515&CFTOKEN=6184618

在类型化设置中,“递归数据结构”意味着不超过或少于“递归定义类型的居民”。相应地,"fred" 是一种递归数据结构,尽管它不是递归定义的,而且确实可以通过诸如++ 之类的递归函数对其进行操作。

短语“递归函数”的意思是“递归定义的函数”。短语“递归值”表示“递归定义的值”,例如存在于非严格语言中:严格语言存在“值递归”问题。

如果您认为这很迂腐,请尝试在 total 编程语言中以这种方式定义 fib,您会发现“递归定义”的概念拆分为“结构递归定义” "(以一种停止的方式消费数据)和“通过受保护的 corecursion 定义”(以一种持续的方式产生数据),而 fib 属于后者。在这种情况下,fib 的生产力主要取决于zipWith 的懒惰。当然,在 Haskell 环境中,您无需担心任何这些事情来弄清楚某物是什么类型的定义,只需弄清楚它是否有一半的机会实际工作。

【讨论】:

    【解决方案3】:

    它是递归的。你可以知道,因为= 的 LHS 名称也出现在 RHS 上。

    然而它不是一个函数。你可以知道,因为fib 的类型不包含->

    【讨论】:

    • @Piet:但这不是问题所在。 OP专门说“这个斐波那契序列函数”,所以他显然不是在问 zipWith (除非你说 zipWith 是“斐波那契序列函数”)。
    • @Piet:但zipWith (+) fib (tail fib) 也不是一个函数。 zipWith 是一个函数,zipWith (+) fib (tail fib) 是应用该函数的结果。
    • @Piet:嗯,很好,但是函数应用程序和函数还是不一样的。
    • @Piet:不,这根本不是重点。正如您自己所说,问题是这里是否定义了递归函数。由于这里唯一定义的是fib,而fib 不是函数,因此答案显然是否定的。
    • 这里肯定有人迟钝。
    【解决方案4】:

    您给出的示例是递归的。但斐波那契数列本质上不一定是这样。有iterative versions of the algorithm,甚至有explicit functions

    【讨论】:

    • 迭代只是递归的一种特殊情况,生成一个序列在某种意义上总是递归的,原因很明显。然而,独立计算单个项的公式本身并不是递归的(尽管它使用可能会递归计算的函数)。
    【解决方案5】:

    由于大多数答案都支持您的同事关于 function 部分:“fib is not a recursive function。”我想详细说明 Conor McBride 在他的回答中暗示的递归部分。

    fib 给出的定义不是递归的,它是共同递归的

    协同递归看起来很像递归,正如许多海报所指出的那样,定义的 LHS 也出现在 RHS 上。但没有基本情况。递归和核心递归“以相反的方向运行”。

    fib 的上述定义从初始值 0 和 1 开始,然后从那里“向上”移动并继续下去。 另一方面,递归定义,例如(计算)第 n 个斐波那契数的函数

    fib' 0 = 0
    fib' 1 = 1
    fib' n = fib' (n-1) + fib' (n-2)
    

    从第 n 个数字“向下”走,直到到达基本情况并停止。

    我想这在两点上都证明了这个疯子是正确的:-)


    如需进一步阅读,请查看Corecursion 上的维基百科文章及其链接。如果您能亲身体验,Kees Doets 和 Jan van Eijck 撰写的 Haskell 逻辑、数学和编程之路的第 10 章可能值得一看。

    【讨论】:

      【解决方案6】:

      他在破解 - 上面的函数显然是递归的。

      【讨论】:

      • sepp2k: zipWith 是一个函数。
      • @Piet:问题在于fib 是否是递归函数,而不是zipWith 是否是。
      • sepp2k:问题是“下面的代码”是否是一个递归函数,显然是。 fib 仅指递归函数的结果这一点是题外话,并没有改变body是递归函数的事实。
      【解决方案7】:

      除了这里的 Haskell 实现之外,斐波那契数是由递归关系定义的序列。从数学上讲,每个项都被定义为前面项的函数。用数学语义打败他。

      【讨论】:

      • 虽然标准定义是递归关系,但任何给定的实现都可以使用封闭形式(即使这个没有),所以这不是实现问题的合理论据。跨度>
      【解决方案8】:

      要使 this 成为递归函数,它需要既是递归又是函数。正如 sepp2k 指出的那样,它显然是递归的,因为名称 fib 出现在 = 的两侧。也就是说,fib 是根据自身定义的。

      它是一个函数吗?不是根据它的类型。在 haskell 中,我们将 0 参数函数称为“数据”。所以fib的这个定义创建了一个递归数据结构,而不是一个递归函数。

      【讨论】:

      • fib 在这种情况下不是递归数据结构:“递归数据结构”在 Haskell 中意味着完全不同的东西。 fib 是由递归函数 (zipWith) 生成的无限、非重复(因此非递归)数据结构。
      【解决方案9】:

      虽然cmets中很多人都在争论这个定义是不是一个函数,但似乎大家都同意它是递归的。

      对于函数/非函数参数,在 Haskell 中,从程序员的角度来看,它并不重要!因为函数和数据结构都是惰性求值的,所以一个值和一个没有参数返回值的函数是无法区分的。你所拥有的是一个整数列表,它被懒惰地和递归地评估。 fib 同时是“整数列表”、“返回整数列表的无参数函数”、“返回整数的无参数函数列表”和“返回整数列表的无参数函数”没有返回整数的参数”。

      老实说,这并不重要。语言没有区分这四种。类型论没有区分这四种(以及无数其他方法:无参数返回其中任何一个的函数同样有效,无参数返回的函数也是如此,无穷无尽)。老实说,你是否称fib 为“函数”并没有什么区别。

      但它是递归的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-08
        • 2011-07-27
        • 2012-11-19
        • 2021-12-01
        • 2020-03-21
        • 2016-11-07
        • 2012-02-16
        相关资源
        最近更新 更多