我发现在不使用以下模式的闭包的情况下编程很困难:
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使用块,procs和lambas,但我似乎无法用JavaScript实现相同的词法范围。
Ruby天真的尝试:
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中实现这种模式呢?
答案 0 :(得分: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:
jbodah@Joshs-MacBook-Pro-2 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"
答案 1 :(得分: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

现在让我们把它改写成更现代的风格:
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
但对于这样一个小玩具的例子,很难说并且很难证明Ruby会是什么样的。
如果您对此类内容感兴趣,可能需要查看as_matrix,其中我将展示如何使用各种语言(包括Clojure和其他各种Lisps,Smalltalk)中的闭包来实现链接列表。以及它的一些后代,ECMAScript和CoffeeScript,Ruby,Python,PHP,Perl等。请注意,代码在大多数语言中看起来非常相似,但请注意,代码在几乎所有语言中都是非常不恰当的,除了计划,也许是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
或者,您可以将Proc
替换为lambda(实际上也是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
(由Proc.new
或Kernel#proc
创建)和lambdas(由Kernel#lambda
创建或stabby lambda literal -> (params) { code }
之间存在两个语义差异):return
中的Proc
从词法封闭方法返回(就像在块中一样)而lambda中的return
从lambda本身返回(就像在方法中一样),并且Proc
s的参数绑定语义与块的相同,而lambdas的参数绑定语义与方法相同。
回顾一下:有两个区别,return
和参数绑定。在这两种情况下,Proc
的行为都像一个块,lambda的行为就像一个方法。助记符:Proc
押韵"阻止"和" lambda"和"方法"都是希腊人。
答案 2 :(得分: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"