使用 Lambda 表達式編寫遞歸五:推導裝配腦袋的 Fix


Print Gallery
《Print Gallery》      作者:埃舍爾

本系列文章目錄:

 

上一篇文章 最后提到, 裝配腦袋 給出的 Fix 函數精簡到極致:

1
2
3
static Func<int, long> Fix(Func<Func<int, long>, Func<int, long>> f) {
    return x => f(Fix(f))(x);
}

下面我們看下是怎么推導出的,從 λ 演算入手:

λ 演算

根據 β-歸約 存在以下等式:

1
(λx.x(fix x)) f = f(fix f)

根據 不動點組合子 的定義:

1
f(fix f) = fix f

由以上兩個等式得出:

1
(λx.x(fix x)) f = fix f

左右互換下:

1
fix f = (λx.x(fix x)) f

根據 c a == d a 時 c == d,得出:

1
fix = λx.x(fix x)

x(fix x) η-展開為 λn.x(fix x)n

1
fix = λx.λn.x(fix x)n

再變換一步:

1
fix = λx.λn.x(fix(x))(n)

轉為 c# 代碼

Fix 是不動點算子,根據前文的假定和推斷它的類型是:Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>。(基於輸入是 int,返回值是 long 的假定)

借助 前言及基礎一文中總結出的小規律,可以寫出:

1
Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> fix = x(fix(x))(n)

當然這個是編譯不通過的,讓我們把它改造成一個方法:

1
2
3
static Func<int, long> fix(Func<Func<int, long>, Func<int, long>> x) {
    return n => x(fix(x))(n);
}

將 int 改為 T、long 改為 TResult,抽象為泛型通用版本:

1
2
3
static Func<T, TResult> Fix<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> x) {
    return n => x(Fix(x))(n);
}

裝配腦袋 的對比下,一樣吧?好像參數名不同,沒關系,把參數 x 更改為 f:

1
2
3
static Func<T, TResult> Fix<T, TResult>(Func<Func<T, TResult>, Func<T, TResult>> f) {
    return n => f(Fix(f))(n);
}

再比對下吧,一樣了吧!(如果還是看不出來的話,把 n 更改 x 試試)

后記

其實呢,我是反推出來的,先從 裝配腦袋 的 Fix 得出:

1
fix = λx.λn.x(fix(x))(n)

然后運用 η-變換λn.x(fix(x))(n) 簡化為 x(fix(x))

1
fix = λx.x(fix(x))

兩側應用 f:

1
fix f = (λx.x(fix(x))) f

運用 β-歸約

1
fix f = f(fix(f))

調整下:

1
fix(f) = f(fix(f))

正是不定點算子的定義。

把這個過程序反過來,就是本文 λ 演算 部分的內容。(如果用五個字評價我的這篇文章就是:事后諸葛亮。)

至於 裝配腦袋 怎么推出的這個 FIX,我就無從得知了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM