中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用Lambda表達式編寫遞歸

發布時間:2021-07-24 09:34:13 來源:億速云 閱讀:301 作者:chen 欄目:編程語言

本篇內容主要講解“怎么使用Lambda表達式編寫遞歸”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么使用Lambda表達式編寫遞歸”吧!

使用 Lambda 表達式構建遞歸函數

很多朋友認為這很容易,隨手便可用 lambda 表達式寫出一個階乘遞歸:

Func<int, int> fact = x => x <= 1 ? 1 : x * fact(x - 1);

不過,很抱歉,這行代碼是無法通過編譯的,VS 提示:使用了未賦值的變量 fact。

有種簡單的解決辦法,把上面這行代碼拆成兩行:

Func<int, int> fact = null;  fact = x => x <= 1 ? 1 : x * fact(x - 1);

不過這種寫法也有問題,老趙說得比較清楚,我就不在贅述了,請查看《使用Lambda表達式編寫遞歸函數》一文中偽遞歸部分。

那么如何解決 lambda 表達式構建遞歸函數的問題呢?根據函數式編程理論,我們可以使用不定點組合子。

在學習不定點組合子之前,需要先了解更基礎 &lambda; 演算。

&lambda; 演算

&lambda; 演算的基礎請大家參考維基百科:

http://zh.wikipedia.org/wiki/Lambda_演算

http://en.wikipedia.org/wiki/Lambda_calculus

非形式化的描述

請確保你已經理解了文中幾個表達式的等價關系:

(&lambda;f. f 3)(&lambda;x. x + 2) == (&lambda;x. x + 2) 3 == 3 + 2   (&lambda;x. &lambda;y. x - y) 7 2 == (&lambda;y.7 - y) 2 == 7 - 2

還清楚知道函數應用(application)的概念及其左結合性:

f x y == (f x) y

還有它的各種等價變換

f x y == (f x) y = (f(x))y = (f(x))(y) = f(x)(y)

歸約

并會運用三個常用的規約(Reduction)

1.&alpha;-變換(&alpha;-conversion)

2.&beta;-歸約(&beta;-reduction)

3.&eta;-變換(&eta;-conversion)

不動點組合子

請參考:

http://zh.wikipedia.org/wiki/不動點組合子

http://en.wikipedia.org/wiki/Fixed-point_combinator

定義

不動點組合子(Fixed-point combinator,或不動點算子,使用 FIX 表示)是計算其他函數的一個不動點的高階函數。

不動點算子具有以下特性,對于任何函數 f 都有:

FIX ff = f (FIX f)

定義匿名的遞歸函數

不動點組合子允許定義匿名的遞歸函數,具體來說是將一個非遞歸的單步函數(只執行遞歸中的一步,a single step of this recursion,使用 g 表示)轉換為遞歸函數。

如下是階乘的單步函數定義:

g = &lambda;f. &lambda;n. (ISZERO n) 1 (MULT n (f (PRED n)))

FIX g 可獲取到匿名的遞歸函數:

FIX g = &lambda;n. (ISZERO n) 1 (MULT n ((FIX g) (PRED n)))

向 FIX g 的參數 n 傳入值 5,可最終得出:

FIX g 55 = 5 * (4 * (3 * (2 * (1 * 1)))) = 120

常用的不定點組合子

不動點組合子中最有名的(也可能是最簡單的)是 Y 組合子:

Y = &lambda;f. (&lambda;x. f (x x)) (&lambda;x. f (x x))

另一個常見不動點組合子是圖靈不動點組合子(阿蘭&middot;圖靈發現的):

&Theta; = (&lambda;x. &lambda;y. (y (x x y))) (&lambda;x.&lambda;y.(y (x x y)))

傳值調用(call-by-value)

在 &lambda; 演算中,每個表達式(lambda term)都代表一個只有單獨參數的函數,這個函數的參數本身也是一個只有單一參數的函數,同時,函數的值是又一個只有單一參數的函數。

根據此描述,可知 &lambda; 演算中函數只有一個參數,這個參數是一個函數,而不是一個值。而對于我們常見的遞歸(階乘、斐波那契數列求值),參數都是值(自然數)。兩者是不匹配的。

為了適當傳值調用,需要將不動點組合子 &eta;-展開:

簡單而言 &eta;-變換 是說 &lambda;x. f x 和 f 可以互相轉換。從 f 這種簡單形式 &eta;-變換 為 &lambda;x. f x 復雜形式,稱為 &eta;-展開

對于 Y 組合子,通常是將其中的 (x x) &eta;-展開為  &lambda;y. x x y,由此得出傳值調用版本的 Y組合子(也稱為 Z 組合子):

Y = &lambda;f. (&lambda;x. f (&lambda;y. x x y)) (&lambda;x. f (&lambda;y. x x y))

如果不展開呢?會怎樣?

如果使用不展開的不動點算子,也能寫出可編譯通過的代碼,但最終執行會陷入死循環,直至堆棧溢出。

小結

后續章節將使用以下符號和名稱,不再另行說明:

1.FIX:不動點組合子

2.g:單步函數

3.n:表示遞歸函數的參數(在階乘、斐波那契數列求值中是一個自然數)

對于 FIX、g、n:

1.FIX g: 將會生成對應的遞歸函數

2.FIX g n: 將進行遞歸運算

&lambda; 演算表達式與 c# lambda 表達式的對應關系

&lambda;x. x + 2

&lambda;x. x + 2 在 c#中的 lambda 表達式可表式為:x => x+ 2;

假定 x 的 int 類型,可寫作:

Func<int, int> f = x => x + 2;

相應 (&lambda;x. x + 2) 1 可寫為:

var result = f(1);    // 結果為 3

&lambda;x. &lambda;y. x + y

復雜點,&lambda;x. &lambda;y. x + y 用 c# 的 lambda 表達式表示為:x => y => x + y;

x, y 類型為均整數時,可寫作:

Func<int, Func<int, int>> f = x => y => x + y;

相應 (&lambda;x. &lambda;y. x + y) 1 2 便是:

var result = f(1)(2);     // 結果為 3

&lambda;x. &lambda;y. &lambda;z. x + y + z

再復雜些,&lambda;x. &lambda;y. &lambda;z. x + y + z 表示為:x => y=> z => x + y + z,三個參數都為 int 時 c# 代碼:

Func<int, Func<int, Func<int, int>>> f = x => y => z => x + y + z;

可如下調用:

// (&lambda;x. &lambda;y. &lambda;z. x + y + z) 1 2 3  var result1 = f(1)(2)(3);    //結果為 6   // (&lambda;x. &lambda;y. &lambda;z. x + y + z) 1  &rarr;  &lambda;y. &lambda;z. 1 + y + z   Func<int, Func<int, int>> g = f(1);   // (&lambda;y. &lambda;z. 1 + y + z) 2  &rarr;  &lambda;z. 1 + 2 + z  &rarr;  &lambda;z. 3 + z  Func<int, int> h = g(2);  // (&lambda;z. 3 + z) 3  &rarr;  3 + 3  &rarr;  6  var result2 = h(3);        // 結果為 6

每 5 行,向 f 傳入一個常量 1,返回一個新的方法 g;再經過第 7 行,向 g 傳入常量 2,再次返回一個新方法 h。

方法 h 只能接受一個參數,***得出 h(3) = 6。

為什么不是  (x, y, z) => x + y + z?

也許你會有疑問,不就是 x、y、z 三個整數加起來嘛,為什么搞這么復雜,像下面這樣不是更簡單嗎?

Func<int, int, int, int> f = (x, y, z) => x + y + z;  var result = f(1, 2, 3);

確實簡單,不過:

在 &lambda; 演算中,每個表達式(lambda term)都代表一個只有單獨參數的函數,這個函數的參數本身也是一個只有單一參數的函數,同時,函數的值是又一個只有單一參數的函數。

注意都是只有一個參數,對應到 c# 的 lambda 表達式,也應是一個參數,所以是:x => y=> z => x + y + z。

總結

&lambda; 演算表達式c# lambda 表達式
&lambda;x. x + 2x => x+ 2
&lambda;x. &lambda;y. x + yx => y => x + y
&lambda;x. &lambda;y. &lambda;z. x + y + zx => y=> z => x + y + z

好像有些規律:對于一個 Lambda terms,去掉“&lambda;”并把“.”替換為”=>”便可變成對應 lambda 表達式。(注意,這個規律不嚴謹!)

練習一下,看看下面這個如何轉為 lambda 表達式:

&lambda;x. &lambda;n. (g (x x) n)

先對它進行一步演算得出:

&lambda;x. &lambda;n. (g (x(x)) (n))

運用上的的規律,可以寫出 lambda 表達式:x => n => g((x(x))(n)

對于復雜點的如:&lambda;x. f ( &lambda;v. (x x) v),這條規律就不適用了。文后續部分會通過演算繞開這種復雜的轉換,不對此進行討論。

理解本文中的泛型和 lambda 表達式

對于上一部分使用的泛型和 lambda 表達式,尤其是下面這行代碼,你需要花點時間去理理思路(因為后續章節中泛型要遠比此復雜):

Func<int, Func<int, Func<int, int>>> f = x => y => z => x + y + z;

如果對你對泛型和 lambda 認識不是非常深刻的話,難度有點大,不妨先從下面這個簡單點的開始:

Func<int, Func<int, int>> f = x => y => x + y;

換種寫法,或許有助于理解:

Func<int, Func<int, int>>  f = x => {                    Func<int, int> g =  y => { return x + y ;};      return g;  };

本文簡單闡述了 lambda 構建遞歸函數的問題,粗略提及 &lambda; 演算及不動點組合子的知識,并總結了下 &lambda; 演算表達式與 c# lambda 表達式的對應關系。

到此,相信大家對“怎么使用Lambda表達式編寫遞歸”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

滨州市| 晋中市| 封丘县| 蒲江县| 武汉市| 漳浦县| 克拉玛依市| 肃宁县| 赞皇县| 东平县| 潼南县| 文安县| 普陀区| 垦利县| 抚州市| 潮州市| 平远县| 大埔县| 聂荣县| 隆昌县| 遂平县| 泌阳县| 长乐市| 黑山县| 嵊泗县| 微博| 芷江| 康定县| 亳州市| 左云县| 桐梓县| 商河县| 石屏县| 仙游县| 洪雅县| 林甸县| 景泰县| 鹿邑县| 和静县| 淳安县| 商城县|