广告
返回顶部
首页 > 资讯 > 精选 >如何使用Lambda表达式编写递归函数
  • 404
分享到

如何使用Lambda表达式编写递归函数

2023-06-17 22:06:10 404人浏览 泡泡鱼
摘要

这篇文章主要介绍“如何使用Lambda表达式编写递归函数”,在日常操作中,相信很多人在如何使用Lambda表达式编写递归函数问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用Lambda表达式编写递归函数

这篇文章主要介绍“如何使用Lambda表达式编写递归函数”,在日常操作中,相信很多人在如何使用Lambda表达式编写递归函数问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用Lambda表达式编写递归函数”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Lambda表达式实现递归表达,在.net编程中具有很重要的意义。递归可以更简便循环过程,但这里需要从“伪”递归开始谈起。

其实这从来不是一个很简单的事情,虽然有些朋友认为这很简单。

“伪”递归

例如,我们想要使用Lambda表达式编写一个计算递归的fac函数,一开始我们总会设法这样做:

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

不过此时编译器会无情地告诉我们,fac还没有定义。于是您可能会想,这个简单,分两行写咯。于是有朋友就会给出这样的代码:

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

这样看起来也很“递归”,执行起来似乎也没有问题。但是,其实这并没有使用Lambda表达式构造一个递归函数,为什么呢?因为我们使用Lambda表达式构造的其实只是一个普通的匿名方法,它是这样的:

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

既然是“匿名方法”,这个构造的东西是没有名字的——因此用Lambda表达式写递归“从来不是一个很简单的事情”。那么这个Lambda表达式里的fac是什么呢?是一个“委托”。因此,这只是个“调用了一个委托”的Lambda表达式。“委托对象”和“匿名方法”是有区别的,前者是一个实际的对象,而后者只是个“定义方式”,只是“委托对象”可以成为“匿名方法”的载体而已。这个Lambda表达式构造的“委托对象”在调用时,它会去寻找fac这个引用所指向的委托对象。请注意,这里是根据“引用”去找“对象”,这意味着Lambda表达式构造的委托对象在调用时,fac可能已经不再指向当初的委托对象了。例如:

Func fac = null;  fac = x => x <= 1 ? 1 : x * fac(x - 1);  Console.WriteLine(fac(5)); // 120;   Func 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表达式构造一个递归函数不是一件容易的事情。把自己传给自己吧

可能已经有朋友知道“标准”的做法是什么样的,不过我这里还想谈一下我当时遇到这个问题时想到的一个做法。比较笨(非常符合我的特点),但是可以解决问题。

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

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

就需要变成:

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

请注意,这里的f参数是一个函数,它也是我们正在使用Lambda表达式定义的匿名委托(就是“(f, x) => ...”这一长串)。为了递归调用,它还必须把自身作为***个参数传入下一层的调用中去,所以***不是fac(x - 1)而是f(f, x - 1)。我们可以把这个匿名函数放到一个叫做selfFac的变量中去:

var selfFac = (f, x) => x <= 1 ? 1 : x * f(f, x - 1);

在***次调用selfFac时,我们必须把它自身传递进去。于是我们可以这样来获得阶乘的结果:

Console.WriteLine(selfFac(selfFac, 5)); // 120;

但是这段代码没法编译通过,因为编译器不知道selfFac应该是什么类型的委托对象。不过根据selfFac(selfFac, 5)的调用方式,我们可以推断出,这个委托类型会接受两个参数,***个是它自身的类型,第二个是个整型,而返回的也是个整型。于是,我们可以得出委托的签名了:

delegate int SelfFactorial(SelfFactorial selfFac, int x);

哎,但是这一点都不通用啊。没关系,我们可以写的通用一些:

delegate TResult SelfApplicable(SelfApplicable self, T arg);

这样,我们便可以定义selfFac,甚至于selfFib(菲波纳契数列):

SelfApplicable selfFib = (f, x) => x <= 1 ? 1 : f(f, x - 1) + f(f, x - 2);  Console.WriteLine(selfFib(selfFib, 5)); // 8

但是,这还不是我们所需要的递归函数啊。没错,我们需要的是传入一个x就可以得到结果的函数,这种每次还需要把自己传进去的东西算什么?不过这个倒也容易,在selfXxx的基础上再前进一步就可以了:

SelfApplicable selfFac = (f, x) => x <= 1 ? 1 : x * f(f, x - 1);  Func fac = x => selfFac(selfFac, x);   SelfApplicable selfFib = (f, x) => x <= 1 ? 1 : f(f, x - 1) + f(f, x - 2);  Func fib = x => selfFib(selfFib, x);

为此,我们甚至可以总结出一个辅助方法:

static Func Make(SelfApplicable self)  {      return x => self(self, x);  }于是乎:   var fac = Make((f, x) => x <= 1 ? 1 : x * f(f, x - 1));  var fib = Make((f, x) => x <= 1 ? 1 : f(f, x - 1) + f(f, x - 2));

这样我们便使用Lambda表达式定义了递归函数。当然,需要两个参数的递归函数定义方式也比较类似。首先是SelfApplicable委托类型和对应的辅助方法:

// 委托类型  delegate TResult SelfApplicable(SelfApplicable self, T1 arg1, T2 arg2);   // 辅助方法  static Func Make(SelfApplicable self)  {      return (x, y) => self(self, x, y);  }于是使用“辗转相除法”计算***公约数的GCd函数便是:   var gcd = Make((f, x, y) => y == 0 ? x : f(f, y, x % y));  Console.WriteLine(gcd(20, 36)); // 4

这也是我目前凭“个人能力”能够走出的最远距离了。

不动点组合子

但是装配脑袋很早给了我们更好的解决方法:

static Func Fix(Func, Func> f)  {      return x => f(Fix(f))(x);  }   static Func Fix(Func, Func> f)  {      return (x, y) => f(Fix(f))(x, y);  }

Fix求出的是函数f的不动点,它就是我们所需要的递归函数:

var fac = Fix(f => x => x <= 1 ? 1 : x * f(x - 1));  var fib = Fix(f => x => x <= 1 ? 1 : f(x - 1) + f(x - 2));  var gcd = Fix(f => (x, y) => y == 0 ? x : f(y, x % y));

用脑袋的话来说,Fix方法应该被视为是内置方法。您比较Fix方法内部和之前的Make方法内部的写法,就能够意识到两种做法之间的差距了。

由于我的脑袋不如装配脑袋的脑袋装配的那么好,即使看来一些推导过程之后还是无法做到100%的理解,我还需要阅读更多的内容。希望在以后的某一天,我可以把这部分内容融会贯通地理解下来,并且可以详细地解释给大家听。在这之前,我还是听脑袋的话,把Fix强行记在脑袋里吧。

***,希望大家多多参与一些如“函数式链表快速排序”这样的趣味编程——不过,千万不要学脑袋这样做:

var qsort = Fix, IEnumerable>(f => l => l.Any() ? f(l.Skip(1).Where(e => e < l.First())).Concat(Enumerable.Repeat(l.First(), 1)).Concat(f(l.Skip(1).Where(e => e >= l.First()))) : Enumerable.Empty());

到此,关于“如何使用Lambda表达式编写递归函数”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: 如何使用Lambda表达式编写递归函数

本文链接: https://www.lsjlt.com/news/293999.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何使用Lambda表达式编写递归函数
    这篇文章主要介绍“如何使用Lambda表达式编写递归函数”,在日常操作中,相信很多人在如何使用Lambda表达式编写递归函数问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用Lambda表达式编写递归函数...
    99+
    2023-06-17
  • 如何使用Lambda表达式编写递归
    这篇文章主要介绍“如何使用Lambda表达式编写递归”,在日常操作中,相信很多人在如何使用Lambda表达式编写递归问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何使用Lambda表达式编写递归”的疑惑有所...
    99+
    2023-06-17
  • 怎么使用Lambda表达式编写递归
    本篇内容主要讲解“怎么使用Lambda表达式编写递归”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用Lambda表达式编写递归”吧!使用 Lambda 表达式构建递归函数很多朋友认为这很容...
    99+
    2023-06-17
  • C#如何实现递归调用的Lambda表达式
    这篇文章主要讲解了“C#如何实现递归调用的Lambda表达式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#如何实现递归调用的Lambda表达式”吧!首先给一个简单的示例: &n...
    99+
    2023-07-02
  • Java中使用Lambda表达式和函数编程示例
    目录1、简单介绍 2、Lambdas和Scopes3、Lambdas与局部变量4、Lambda体与局部变量5、Lambdas和'This'和'Super'关键字6、Lambdas和E...
    99+
    2022-11-12
  • sql server使用公用表表达式CTE通过递归方式如何编写通用函数自动生成连续数字和日期
    sql server使用公用表表达式CTE通过递归方式如何编写通用函数自动生成连续数字和日期,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。问题...
    99+
    2022-10-19
  • python如何使用Lambda表达式
    这篇文章主要为大家展示了“python如何使用Lambda表达式”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“python如何使用Lambda表达式”这篇文章吧。Lambda表达式厌倦了定义用不...
    99+
    2023-06-27
  • python递归函数如何使用
    在python中使用递归函数的方法利用递归函数求阶乘def p(n):if n==1 or n==0:return 1else:return n*p(n-1)n=int(input("请输入一个整数:"))print(n,"!的值为:",p...
    99+
    2022-10-11
  • C#中如何使用Lambda表达式
    本篇文章为大家展示了C#中如何使用Lambda表达式,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。C# Lambda表达式我们从“所有字符串查找包含YJingLee子字符串”说起。在C# 2.0中,...
    99+
    2023-06-17
  • Java的Lambda表达式如何使用
    这篇文章主要介绍“Java的Lambda表达式如何使用”,在日常操作中,相信很多人在Java的Lambda表达式如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java的Lambda表达式如何使用”的疑...
    99+
    2023-06-30
  • C#中Lambda表达式如何使用
    本篇内容介绍了“C#中Lambda表达式如何使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、介绍"Lambda表达式&quo...
    99+
    2023-06-30
  • lambda表达式与传统接口函数如何实现
    这篇文章主要讲解了“lambda表达式与传统接口函数如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“lambda表达式与传统接口函数如何实现”吧!一、接口定义首先,我们要明白lambd...
    99+
    2023-06-29
  • C++11中lambda表达式如何使用
    本篇文章为大家展示了C++11中lambda表达式如何使用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。首先这个lambda就是罗马字母λ,lambda表达式即λ表达式。数学上有一个概念叫λ演算,其...
    99+
    2023-06-19
  • Lambda表达式如何在Android 中使用
    这篇文章给大家介绍Lambda表达式如何在Android 中使用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1.Lambda表达式的基本写法如果想要在 Android 项目中使用 Lambda表达式 或者 Java8...
    99+
    2023-05-31
    android lambda roi
  • 如何在Java8中使用lambda表达式
    这篇文章给大家介绍如何在Java8中使用lambda表达式,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。 0. 函数式编程    函数式编程(Functional Pr...
    99+
    2023-06-14
  • lambda表达式如何在java中使用
    这篇文章给大家介绍lambda表达式如何在java中使用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Lamda表达式λ 希腊字母表中排序第十一位字母,英语名称为Lambda避免匿名内部类定义过多其实质属于函数式 编程...
    99+
    2023-06-14
  • lambda表达式如何在JAVA8中使用
    这期内容当中小编将会给大家带来有关lambda表达式如何在JAVA8中使用,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一:什么是 Stream?Stream(流)是一个来自数据源的元素队列并支持聚合操作...
    99+
    2023-06-15
  • Java中Lambda表达式和函数式接口的使用和特性
    目录1.概述2.lambda表达式的基础3.JAVA中的lambda表达式4.函数式接口概述FunctionPredicateConsumerSuppiler5.级联与柯里化1.概述...
    99+
    2023-05-16
    Java lambda表达式与函数式接口 Java lambda表达式 Java函数式接口
  • oracle中如何使用connectbyprior递归函数
    这篇文章将为大家详细讲解有关oracle中如何使用connectbyprior递归函数,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。  oracle中con...
    99+
    2022-10-18
  • 在vue中如何使用递归函数
    在vue中使用递归函数的方法:1.新建vue.js项目;2.在项目中定义递归函数;3.使用this方法引用递归函数;4.执行代码调用递归函数;具体步骤如下:首先,在vue-cli中创建一个vue.js项目;vue init webpack ...
    99+
    2022-10-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作