使用 Lambda 表達式編寫遞歸四:實現 Θ 組合子


Fish and Scales
《Fish and Scales》      作者:埃舍爾

本系列文章目錄:

 

上一篇文章 我們實現 Y 組合子,這篇文章討論 Θ 組合子的實現。(Θ 讀 Theta,希臘語第八個字母,小寫為 θ。)

繼續使用前文中的類型假定,假定遞歸函數:

  • 參數為 int;
  • 返回值為 long。

Θ 組合子

Θ 組合子也是一個常見不動點組合子,由 阿蘭·圖靈 發現,也稱為圖靈不動點組合子:

1
Θ = (λx.λy.(y(x x y))) (λx.λy.(y(x x y)))

傳值調用形式為((x x y) η-展開為 λz. x x y z):

1
Θv = (λx.λy.(y(λz. x x y z))) (λx.λy.(y(λz. x x y z)))

不過我還是喜歡把 y (x x y) η-展開為: λn.(y (x x y) n),得出:

1
Θ = (λx.λy.λn.(y(x x y) n)) (λx.λy.λn.(y(x x y) n))

定義一個中間變量 h,令:

1
h = λx.λy.λn.(y(x x y)n)

則:

1
Θ = h h 
Θ = h(h)

實現 h

確定 h 的類型

根據前文的推斷,Θ 的類型為 Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> , 這也是 h 返回值的類型。

h 可調用自身 h(h),需要用到 上一篇文章 用的 SelfApplicable<TResult>委托:

1
delgate TResult SelfApplicable<TResult>(SelfApplicable<TResult> self);

h 的類型為:SelfApplicable<Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>>。

實現 h

對 h 作一步簡單變換:

1
2
h = λx.λy.λn.(y(x x y)n)
h = λx.λy.λn.(y(x(x)(y))(n)

使用本系列第一篇文章總結出的規律,可寫出:

1
SelfApplicable<Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>> h = x => y => n => y(x(x)(y))(n);

實現 Θ

根據 Θ 的簡單式:

1
Θ = h(h)

可寫出:

1
Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> Θ = h(h);

與 h 的代碼放在一塊:

1
2
SelfApplicable<Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>>> h = x => y => n => y(x(x)(y))(n);
Func<Func<Func<int, long>, Func<int, long>>, Func<int, long>> Θ = h(h);

還記得 上一篇文章 最后給出的代嗎?(出處:http://blogs.msdn.com/b/madst/archive/2007/05/11/recursive-lambda-expressions.aspx

比較下,看看是不是一回事的。

調用下試試:

1
2
var factorial = Θ(f => x => x == 0 ? 1 : x * f(x - 1));
var result = factorial(5);  // 120

封裝 Θ

1
2
3
4
5
6
7
8
9
10
public class ThetaCombinator {
    public static Func<TInput, TResult> Fix<TInput, TResult>(Func<Func<TInput, TResult>, Func<TInput, TResult>> f) {
        return Theta<TInput, TResult>.Fix(f);
    }
    static class Theta<TInput, TResult> {
        delegate T SelfApplicable<T>(SelfApplicable<T> self);
        static readonly SelfApplicable<Func<Func<Func<TInput, TResult>, Func<TInput, TResult>>, Func<TInput, TResult>>> h = x => y => n => y(x(x)(y))(n);
        public static readonly Func<Func<Func<TInput, TResult>, Func<TInput, TResult>>, Func<TInput, TResult>> Fix = h(h);
    }
}

調用示例代碼:

1
2
3
4
5
var factorial = ThetaCombinator.Fix<int, int>(f => n => n == 0 ? 1 : n * f(n - 1));
var result1 = factorial(5);   // 120

var fibonacci = ThetaCombinator.Fix<int, int>(f => n => n < 2 ? n : f(n - 1) + f(n - 2));
var result2 = fibonacci(5);   // 5

后記

http://blogs.msdn.com/b/madst/archive/2007/05/11/recursive-lambda-expressions.aspx 文中也給出了下面一種簡單到極致的寫法:

1
2
3
Func<T, T> Fix<T>(Func<Func<T,T>, Func<T,T>> F) {
  return t => F(Fix(F))(t);
}

增加一個泛型參數並修改下參數的名稱,得出:

1
Func<T, TResult> Fix<T, TResult>(Func<Func<T,TResult>, Func<T,TResult>> f) {
  return t => f(Fix(f))(t);
}

便是 裝配腦袋 給出的寫法。下一篇文章 中將說明這個是如何編導出的。


免責聲明!

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



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