2012-03-01 62 views
16

我希望能夠使用Ruby從本身內部調用匿名lambda。考慮下面的遞歸塊(返回一個階乘)。我知道我可以把它分配給一個變量,該變量是拉姆達的範圍內:我可以使用Ruby從本身內部引用lambda?

fac = lambda { |n| n == 1 ? 1 : n * fac.call(n - 1) } 
fac.call(5) 

但是,我希望能夠做到以下幾點(對沒有實際理由作爲然而,我米只是在探索語言多一些興趣):

(lambda { |n| n == 1 ? 1 : n * self.call(n - 1) }).call(5) 

我知道這無法工作,因爲selfmain對象。我做錯了嗎?我是否試圖做一些不可能的事情 - 如果沒有,這是由於一些理論上的限制,還是僅僅在Ruby中沒有實現?

+5

你熟悉Y組合?這可能不是最好的實際解決方案,但從理論角度來看,這非常有趣。如果你不這樣做,請看[這篇文章](http://nex-3.com/posts/43-fun-with-the-y-combinator-in-ruby)。小心點,它可能會讓你的大腦流失。 – 2012-03-01 12:56:21

回答

5

看來,匿名函數真的沒有任何參考。您可以通過callee

lambda{ __callee__ }.call #=> nil 

檢查,並沒有提及您不能調用此函數。 我可以建議你只有一點點更乾淨的變體:

(fac = lambda{ |n| n==1 ? 1 : n*fac.call(n-1) }).call(5) 
+1

它會*真的*是更清潔,只是創建一個命名的功能。 – 2012-03-01 14:03:55

+0

是的,我的原始版本確實將lambda包裝在圓括號中,我在那裏運行了一個.call()。這實際上並不是代碼簡潔的問題,但它更多的是關於Ruby功能性兔子洞可以走多遠的問題。KL-7在上面有一條評論,鏈接到描述Y組合器的文章,這是一個非常有趣的閱讀。 – 2012-03-01 14:22:55

7

在下面的例子中,拉姆達仍然是匿名的,但它有一個參考。 (這是否通過匿名?)

(l = lambda { l.call }).call 

(感謝尼克拉斯B.您指出我在原來的答覆的錯誤;我只在IRB測試,它在那裏工作)。

這當然以SystemStackError: stack level too deep錯誤結束,但它證明了目的。

1

除了KL-7's comment,這裏有一個Y組合的解決方案:

lambda { |f| 
    lambda { |x| x.call(x) }.call(
    lambda { |x| f.call(lambda { |v| x.call(x).call(v) }) }) 
}.call(
    lambda { |f| 
    lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } 
    } 
).call(5) #=> 120 

你通常會分裂這些:

y = lambda { |f| 
    lambda { |x| x.call(x) }.call(
    lambda { |x| f.call(lambda { |v| x.call(x).call(v) }) }) 
} 

fac = y.call(
    lambda { |f| lambda { |n| n == 0 ? 1 : n * f.call(n - 1) } } 
) 

fac.call(5) #=> 120 

注意,雖然fac被分配,它不是拉姆達內使用。

我會使用Ruby的語法->.(),而不是.call()

y = ->(f) { 
    ->(x) { x.(x) }.(
    ->(x) { f.(->(v) { x.(x).(v) }) }) 
} 

fac = y.(->(f) { 
    ->(n) { n == 0 ? 1 : n * f.(n - 1) } 
}) 

fac.(5) #=> 120 

y調用可以簡化一點用curry

y = ->(f) { 
    ->(x) { x.(x) }.(
    ->(x) { f.curry.(->(v) { x.(x).(v) }) }) 
} 

fac = y.(
    ->(f, n) { n == 0 ? 1 : n * f.(n - 1) } 
) 

fac.(5) #=> 120 
相關問題