从基础概念解释“伪”递归

简介:

起因 老赵 使用Lambda表达式编写递归函数

经过 鹤冲天 反驳老赵之“伪”递归

结果 James.Ying 驳“反驳老赵之“伪”递归”

 

摘要:老赵提了个“伪”递归的说法

=========================

Func<int, int> fac = null;
fac = x => x <= 1 ? 1 : x * fac(x - 1);
Console.WriteLine(fac(5)); // 120;

Func<int, int> facAlias = fac;
fac = x => x;
Console.WriteLine(facAlias(5)); // 20

  第一次打印出的120是正确的结果。不过facAlias从fac那里“接过”了使用Lambda表达式构造的委托对象之后,我们让fac引用指向了新的匿名方法x => x。于是facAlias在调用时:

facAlias(5)     <— facAlias是x => x <= 1 ? 1 : x * fac(x – 1)
= 5 <= 1 ? 1 : 5 * fac(5 - 1)
= 5 * fac(4)    <— 注意此时fac是x => x
= 5 * 4
= 20

  自然就不对了。

=============================

老赵其实解释得也很清楚:

这个Lambda表达式构造的“委托对象”在调用时,它会去寻找fac这个引用所指向的委托对象。请注意,这里是根据“引用”去找“对象”,这意味着Lambda表达式构造的委托对象在调用时,fac可能已经不再指向当初的委托对象了。

 

我理解的意思就是老赵说facAlias的定义虽然看起来很像递归,但是本质上不是递归,所以一时兴起起个名字叫“伪”递归

结果人家鹤冲天不干了,觉得老赵对于Lambda递归有偏见。

================================== 

先说下老赵这篇文章的由来,我之前也写过一篇和递归有关的随笔《由Fibonacci数列引出“委托扩展”及“递推递归委托”》,里面给出了这样的一个递归定义(以下称为代码一):

public static Func<int, int> Fibonacci = n => n > 1 ? Fibonacci(n - 1) + Fibonacci(n - 2) : n;

是计算Fibonacci数列的,注意上这句代码中用了“static”,可以编译通过,绝对没有问题!老赵回复中说不用static无法编译通过,于是我又给出了以下代码(以下称为代码二):

    Func<int, int> Fibonacci = null;
    Fibonacci = n => n > 1 ? Fibonacci(n - 1) + Fibonacci(n - 2) : n;

代码二就是老赵随笔中开始处的那两行被称为“伪”递归代码,所说的朋友自然就是我了!

===================================

呵呵,人家老赵没说代码二是伪递归呀。facAlies才是呢。并且老赵的希望不是仅仅构造一个递归,而是希望

我的想法是,既然使用“Lambda表达式来构造一个递归函数”的难点是因为“我们正在构造的东西是没有名字的”,因此“我们无法调用自身”。那么,如果我们换种写法,把我们正在调用的匿名函数作为参数传给自己,那么不就可以在匿名函数的方法体中,通过调用参数来调用自身了吗?

以及从这个出发点开始扩展下去的东西

不过结果是James.Ying对鹤冲天进行了一些使用IL的探究,不过问题出在这么一个表达上

这样我们能清楚些,当我们执行委托的时候,会使用Invoke(args)来调用方法体,看清楚,是Invoke方法,并不是委托自己哦,这一点已经偏离了递归的概念了。

这是个原则性问题,我的跟帖也就是冲这个去的

其实很多问题根本不需要IL来凑热闹。

最后,使用最基础的内存堆栈结构概念来解释老赵的代码

回顾基本概念,值类型、引用类型。

Func<int, int> fac = null;

fac = x => x <= 1 ? 1 : x * fac(x - 1);

Func<int, int> facAlias = fac;

fac = x => x;

Console.WriteLine(facAlias(5)); // 20

第一行,在内存中的情况如下

image

第二行

image

 

第三行

image

第四行

image

第五行的运行结果自然就是老赵说的:

facAlias(5) <— facAlias是x => x <= 1 ? 1 : x * fac(x – 1)

= 5 <= 1 ? 1 : 5 * fac(5 - 1)

= 5 * fac(4) <— 注意此时fac是x => x

= 5 * 4

= 20

所以facAlias不是一个递归Lambda表达式,因此可以认为是一种“伪递归”

Func<int, int> fac = null;

fac = x => x <= 1 ? 1 : x * fac(x - 1);

这里的fac是一个如假包换的递归

=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

最后谢谢各位观赏

作者: 徐少侠
出处: http://www.cnblogs.com/Chinese-xu/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
如有问题,可以通过 Chinese_Xu@126.com 联系我,非常感谢。

分享家:Addthis中文版
标签: 伪递归, lambda

本文转自徐少侠博客园博客,原文链接:http://www.cnblogs.com/Chinese-xu/archive/2009/09/02/1558547.html,如需转载请自行联系原作者
目录
相关文章
|
3月前
|
算法 搜索推荐 图计算
图计算中的社区发现算法是什么?请解释其作用和常用算法。
图计算中的社区发现算法是什么?请解释其作用和常用算法。
24 0
|
3月前
|
机器学习/深度学习 NoSQL 容器
递归的本质与基本实现形式
递归的本质与基本实现形式
|
2月前
|
存储 Java 程序员
Java数组全套深入探究——基础知识阶段2、数组的定义语法
Java数组全套深入探究——基础知识阶段2、数组的定义语法
27 0
|
4月前
|
存储 算法 Python
Python 数据结构和算法: 解释动态规划的概念,并提供一个实际应用的例子。
Python 数据结构和算法: 解释动态规划的概念,并提供一个实际应用的例子。
|
8月前
|
编解码 JavaScript
解释基本的3D理论
本文介绍了所有基本理论,这些理论在开始使用 3D 时很有用。
63 0
解释基本的3D理论
|
10月前
|
存储 编译器 C语言
【C】函数真的难嘛?其实一点也不难,原理很简单。
# 什么是函数 程序是由多个零件组合而成的,而函数就是这种“零件”的一个较小单位。 ## main函数和库函数 C语言程序中,main函数是必不可少的。程序运行的时候,会执行main函数的主题部分。main函数中使用了printf、scanf、puts等函数。由C语言提供的这些为数众多的函数称为库函数。 ## 什么是函数 当然,我们也可以自己创建函数。而实际上,我们也必须亲自动手创建各种函数。下面我们来自己创建一个简单的函数。 创建一个函数,接收两个整数参数,返回较大整数的值。 printf函数和scanf函数等创建得比较好得函数,即使不知道其内容,只要了解使用方法,也可以轻松使用。 ## 函
|
11月前
|
算法 搜索推荐 程序员
c++模板的概念全新解释(二)
c++模板的概念全新解释(二)
100 0
|
11月前
|
算法 安全 程序员
c++模板的概念全新解释(一)
c++模板的概念全新解释(一)
167 0
|
算法
算法设计与分析/数据结构与算法实验4:添加括号数目问题
算法设计与分析/数据结构与算法实验4:添加括号数目问题
134 0
算法设计与分析/数据结构与算法实验4:添加括号数目问题
|
存储 算法 Go
周而复始,往复循环,递归、尾递归算法与无限极层级结构的探究和使用(Golang1.18)
所有人都听过这样一个歌谣:从前有座山,山里有座庙,庙里有个和尚在讲故事:从前有座山。。。。,虽然这个歌谣并没有一个递归边界条件跳出循环,但无疑地,这是递归算法最朴素的落地实现,本次我们使用Golang1.18回溯递归与迭代算法的落地场景应用。
周而复始,往复循环,递归、尾递归算法与无限极层级结构的探究和使用(Golang1.18)