驳“反驳老赵之“伪”递归”
晚上看到鹤冲天的“反驳老赵之“伪”递归”,大概看了一下,主要是反驳老赵提出的“伪”递归的概念,特别是“伪”,看起来说的都很有道理,但我个人认为,老赵说的没有错,Lambda这种看上去是递归的方式,根本不算是递归。
我引用鹤冲天的递归概念:
一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法
我觉得这句话说的很明白,通俗点就是自己调用自己,鹤兄说递归应该不仅仅是过程还是函数,应该包括匿名方法和lambda。我同意匿名方法应该算一种,但因为是匿名方法,我们在开发中无法知道方法名,故我们无法去调用它,但lambda(和委托)算不算一种递归呢?
我们都知道lambda构建的是一个委托,委托只是对一个方法的应用,lambda表达式只是构建了一个匿名方法体,并没有去执行,只有在使用的时候根据需求来延迟加载,但其中是有陷阱的,老赵先前写了一篇“.NET中*延迟*特性的几个陷阱”,其中介绍的非常清楚。那如何反驳鹤兄呢?从他的程序来讲吧。代码:
public static readonly Func<int, int> fac = x => x <= 1 ? 1 : x * fac(x - 1);
static void Main(string[] args)
{
int x = 5;
Console.WriteLine(fac(x));
Console.Read();
}
为了看清楚些,我索性执行了,照鹤兄所说,fac调用了fac就应该属于一种递归形式,但你知道它其中执行了什么吗?Look 反编译:
internal class Program
{
// Fields
public static readonly Func<int, int> fac;
// Methods
static Program()
{
if (CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
CS$<>9__CachedAnonymousMethodDelegate1 = new Func<int, int>(null, (IntPtr) <.cctor>b__0);
}
fac = CS$<>9__CachedAnonymousMethodDelegate1;
}
private static void Main(string[] args)
{
Console.WriteLine(fac.Invoke(5));
Console.Read();
}
}
这样我们能清楚些,当我们执行委托的时候,会使用Invoke(args)来调用方法体,看清楚,是Invoke方法,并不是委托自己哦,这一点已经偏离了递归的概念了。
再来看下我们原先的递归方法:
static int Fac(int i)
{
return i <= 1 ? 1 : i * Fac(i - 1);
}
反编译的代码:
private static int Fac(int i)
{
return ((i <= 1) ? 1 : (i * Fac(i)));
}
大家看清楚了吧,调用的是它自己哦。
继续说鹤兄的代码,就算鹤兄说委托调用自己委托属于一种递归,但存在着一个“延迟特性的陷阱”,这一点老赵已经说明,每一次调用的是方法体,其中的参数是从外部传进来的,并不是方法自身往下传的,老赵也在“使用Lambda表达式编写递归函数”进行了叙述,什么意思呢?就是我们在委托调用委托的时候,“递归”还没有结束的情况下,如果改变了外部这个参数值,就会影响到“递归”的结果,这也是闭包的一个陷阱。鹤兄用了readonly来让委托只读,想以此来构造一个递归的委托,但真正需要绑定的不是方法体,还需要绑定参数的,你的参数值能通过外部进行改变的,而在传统递归中,第二次调用的时候,参数值都是第一次调用说传入的,外部是无法改变的,这一点是最能说明问题所在的。
总结
太晚了,也不想写太多了,我想懂的人应该明白吧。最后说一下,虽然世界变化的很快,但编程的一些基础还是经的起考验的,并不是说有了匿名方法,委托等就能改变递归的定义,因为从它的诞生之日起,已经有很多人研究过,为什么没有把它定义为委托,肯定有一定道理在里面的。老赵说是一种“伪”递归,这是从代码层面来说的,严格来说,绝对不是递归,我也不是老赵的拥护者,老赵也说了他的SelfApplicable<T, TResult>也不是递归,所以这种驳论觉得没有什么意义。
不过有讨论总归比不讨论要好,我也不知道自己说的对不对,拿出来大家一起讨论,欢迎拍砖。
- python程序员开发必备的5大工具,你用过几个?
- Andrew Ng机器学习课程笔记--week8(K-means&PCA)
- Andrew Ng机器学习课程笔记--week7(SVM)
- Pytorch windows10安装教程
- Udacity并行计算课程笔记-The GPU Programming Model
- DeepLearning.ai学习笔记(二)改善深层神经网络:超参数调试、正则化以及优化--Week2优化算法
- DeepLearning.ai学习笔记(二)改善深层神经网络:超参数调试、正则化以及优化--Week1深度学习的实用层面
- 通俗理解决策树中的熵&条件熵&信息增益
- KNN实现手写数字识别
- softmax分类算法原理(用python实现)
- DeepLearning.ai学习笔记(一)神经网络和深度学习--Week4深层神经网络
- 博客园自定义皮肤扁平化设计
- DeepLearning.ai学习笔记(四)卷积神经网络 -- week3 目标检测
- DeepLearning.ai学习笔记(四)卷积神经网络 -- week2深度卷积神经网络 实例探究
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法