2016-01-20 21 views
1

我覺得很難程序,而不按以下模式使用閉包:實現JavaScript的封樣在Ruby中

function outerFunction(input) { 
    var closureVar = "I'm here with "; 

    function innerFunction() { 
    return closureVar + input; 
    } 

    return innerFunction(); 
} 

outerFunction('outer input'); // I'm here with outer input 

這對我來說很有意義。 outerFunction定義了一個innerFunction在調用時引用的環境。

我想看看Ruby如何完成同樣的事情。我知道Ruby使用塊,特效和lambas,但我似乎無法像使用JavaScript一樣達到相同的詞彙範圍。

紅寶石天真的嘗試:

def outer_function 
    list = [ 1, 2, 3 ] 

    list.each { |item| print item } 
end 

# Fails because list has an undefined method on the second iteration of the loop 

另一種嘗試

def outer_function 
    list = Proc.new { return [ 1, 2, 3 ] } 

    list.call.each { |item| another_function item } 
end 

def another_function item 
    puts item 
end 

# Same problem 

那麼,如何才能實現我在Ruby中這種模式?

+1

你是什麼意思由*#失敗,因爲列表中有環路*的第二次迭代一個未定義的方法? – Atri

+0

對不起,我遺漏了我的代碼不完整。我實際上正在調用循環中的另一個函數。所以我相信一旦它調用該函數,它會將列表重置爲零。我用了一個開始和救援塊,這對我來說似乎正在發生。 – user3162553

+0

然後顯示實際的代碼。 – Atri

回答

3

雖然Javascript有一種類型的lambda構造(匿名函數),但Ruby有幾種。最常見的是塊(傳遞給在你的例子中的each中的方法),Procs(它們就像綁定到它們保留的執行上下文,即使傳遞時也保留),以及可能最接近匿名函數的Lambdas。我不會在這裏詳細介紹Procs vs Lambdas。

下面是這些結構的例子在Ruby中:

# Using a block + yield 
def my_method(arg) 
    puts arg + yield(3) 
end 

my_method(5) { |arg| arg * 2 } 
# => 11 

# Binding the block to a Proc then using #call 
def print(&block) 
    puts block.call 
end 

# Creating a Proc that can be passed around and then #called 
def other_method(arg, other_args*) 
    arg.call(other_args*) 
end 

var = 3 
prc = Proc.new { puts var } 
other_method(prc) 

# Creating a Lambda using stabby syntax 
num = 3 
l = -> (arg) { arg + 2 + num } 
other_method(l, 1) 

在Ruby中,你可以寫你的JS例子如:

def outer(input) 
    closure_var = "I'm here with " 

    inner = -> { closure_var + input } 

    inner.call 
end 

outer('outer input') 

在IRB:

[email protected] 2.1.3p242 ~ (none) $ irb 
irb(main):001:0>  def outer(input) 
irb(main):002:1>  closure_var = "I'm here with " 
irb(main):003:1> 
irb(main):004:1*  inner = -> { closure_var + input } 
irb(main):005:1> 
irb(main):006:1*  inner.call 
irb(main):007:1>  end 
=> :outer 
irb(main):008:0> 
irb(main):009:0*  outer('outer input') 
=> "I'm here with outer input" 
0

下將爲你做,只要注意inner_function不是一個真正的方法。因此它是一個lambda,其行爲像一個方法。在這裏閱讀更多LINK

def outer_function(item) 
    outer_variable = "input" 
    inner_function = lambda { puts item puts outer_variable } 
    inner_function[] 
end 

outer_function "I'm here with " # prints "I'm here with input" 
3

function outerFunction(input) { 
 
    var closureVar = "I'm here with "; 
 

 
    function innerFunction() { 
 
    return closureVar + input; 
 
    } 
 

 
    return innerFunction(); 
 
} 
 

 
console.log(outerFunction('outer input')); // I'm here with outer input

讓我們先來重新寫你的ECMAScript使用函數表達式:

const outerFunction = function (input) { 
 
    const closureVar = "I'm here with "; 
 

 
    const innerFunction = function() { 
 
    return closureVar + input; 
 
    } 
 

 
    return innerFunction(); 
 
} 
 

 
console.log(outerFunction('outer input')); // I'm here with outer input

現在讓我們重新編寫成多模式RN風格:

const outerFunction = input => { 
 
    const closureVar = "I'm here with "; 
 

 
    const innerFunction =() => closureVar + input; 
 

 
    return innerFunction(); 
 
}; 
 

 
console.log(outerFunction('outer input')); // I'm here with outer input

現在,它實際上是相當簡單的這個翻譯到Ruby:

outer_function = -> input { 
    closure_var = "I'm here with " 

    inner_function = -> { closure_var + input } 

    inner_function.() 
} 

puts outer_function.('outer input') # I'm here with outer input 

正如你所看到的,翻譯其實是非常簡單和語義類似。但是,它不是非常習慣的Ruby。一般而言,對於封裝狀態,對象和方法比封閉更受歡迎,但有時,封閉是有用的和使用的。

更地道的風格可能是這樣的:

def outer_method(input) 
    closure_var = "I'm here with " 

    inner_function = -> { closure_var + input } 

    inner_function.() 
end 

puts outer_method('outer input') # I'm here with outer input 

但對於這樣一個小玩具例如,它很難說,很難證明紅寶石會是什麼樣習慣等。

如果您對這樣的東西感興趣,您可能需要查看toy project of mine,在那裏我將展示如何使用各種語言(包括Clojure和其他各種Lisp,Smalltalk和一些它的後代,ECMAScript和CoffeeScript,Ruby,Python,PHP,Perl等。請注意,代碼在大多數語言中看起來非常相似,但是請注意代碼在幾乎所有語言中都非常不習慣,除了Scheme和Clojure的。就我所知,它甚至不是慣用的CommonLisp。


你在你的問題下半年發佈的Ruby代碼是完全無關的任何代碼或在你的問題上半年的概念:沒有關閉,沒有詞法作用域的問題,並有不需要使用它們。

事實上,您發佈的第一個片段只是工作作爲-是:

def outer_function 
    list = [ 1, 2, 3 ] 

    list.each { |item| print item } 
end 

outer_function 
# 123 

第二隻是有一個小問題:

def outer_function 
    list = Proc.new { return [ 1, 2, 3 ] } 

    # The call to `each` is dead code since the call to `list.call` will already return 
    list.call.each { |item| another_function item } 
end 

# This method never gets called 
def another_function item 
    puts item 
end 

outer_function 
# => [1, 2, 3] 

return被用於從返回一個值方法。那麼,return從哪個方法返回?那麼,它從outer_function返回,因爲那是唯一的方法! list是一個Proc,而不是一種方法。要從Proc回報,你使用next關鍵字來代替:

def outer_function 
    list = Proc.new { next [ 1, 2, 3 ] } 

    list.call.each { |item| another_function item } 
end 

def another_function item 
    puts item 
end 

outer_function 
# 1 
# 2 
# 3 

或者,你可以乾脆離開了next,自上次表達的化合物表達內評估(塊,一個方法,一個類,無論)是它的返回值反正:

def outer_function 
    list = Proc.new { [ 1, 2, 3 ] } 

    list.call.each { |item| another_function item } 
end 

def another_function item 
    puts item 
end 

outer_function 
# 1 
# 2 
# 3 

或者,你可以用一個lambda更換Proc(這實際上也是一個Proc但略有不同的語義):

def outer_function 
    list = -> { return [ 1, 2, 3 ] } 

    list.call.each { |item| another_function item } 
end 

def another_function item 
    puts item 
end 

outer_function 
# 1 
# 2 
# 3 

Proc S(由Proc.newKernel#proc創建)和lambda表達式(由Kernel#lambda創建或stabby拉姆達字面-> (params) { code })之間的兩個語義差異:在從詞法封閉方法Proc返回return(就像在一個塊中)而lambda中的return從lambda本身返回(就像在方法中一樣),並且Proc的參數綁定語義與塊的參數綁定語義相同,而lambda表達式的參數綁定語義與方法的相同。

回顧:有兩個區別,return和參數綁定。在這兩種情況下,Proc的行爲就像一個塊,lambda表現得像一個方法。助記符:Proc帶有「block」和「lambda」和「method」的押韻都是希臘字母。