我在erb库中遇到了以下代码。请注意双(( ))
:
class MyTest
def location=((filename, lineno))
@filename = filename
@lineno = lineno if lineno
end
end
以下locatia=
方法是没有(( ))
进行测试的另一个版本:
class MyTest
def locatia=(filename, lineno)
@filename = filename
@lineno = lineno if lineno
end
end
我得到了这个结果:
a = MyTest.new
a.location = "foo", 34
a # => #<MyTest:0x2a2e428 @filename="foo", @lineno=34>
b = MyTest.new
b.location = "foo"
b # => #<MyTest:0x2a2e338 @filename="foo">
c = MyTest.new
c.locatia = "foo", 34
c # >> `locatia=': wrong number of arguments (given 1, expected 2) (ArgumentError)
带有双括号的版本可以正常工作。单发失败。必须在源代码的某些级别上指定它。有任何线索吗?
答案 0 :(得分:8)
解构。
location=
接受一个参数。假定此参数是一个数组,并且已对其进行了结构分解,以便数组的第一个元素进入filename
,第二个元素进入lineno
。外部括号是方法定义的普通(通常是可选的)括号。内括号表示第一个(也是唯一的)参数的结构。
这是在工作中进行销毁的另一个例子:
{ foo: 17, bar: 34 }.each.with_index { |(key, value), index|
p [key, value, index]
}
# => [:foo, 17, 0]
# [:bar, 34, 1]
Hash#each
生成对[key, value]
; Enumerator#with_index
生成一对[value, index]
。将它们都应用,您将获得[[key, value], index]
传递给该块。我们可以这样做:
{ foo: 17, bar: 34 }.each.with_index { |pair, index|
key = pair[0]
value = pair[1]
p [key, value, index]
}
但是它的解构要简单得多。我们甚至可以编写(key, value) = pair
(或key, value = pair
,因为单值数组在多值赋值中会自动解构)作为解构的另一个示例。
答案 1 :(得分:8)
在生产代码中看到这种情况有点不寻常,但是这里发生的是参数列表中的 list扩展:
def location=((filename, lineno))
end
这意味着您这样称呼:
x.location = 1,2
将它们扩展为两个单独的参数的位置。 mutator方法只能使用一个参数,但该参数可以是一个列表,您可以将该参数扩展为多个值。
通常,您会在类似以下的迭代器中看到这种情况:
{ a: 'b', c: 'd' }.each_with_index.map do |(k,v), i|
# k, v come in as a pair, i is separate
end
尽管如此,它还是非常罕见的。
在其他情况下,您也可以看到它:
a = [ 1, 2 ]
b = 3
# Without list expansion, just one-to-one assignment
x, y, z = a, b
# x => [ 1, 2 ]
# y => 3
# z => nil
# With list expansion
(x, y), z = a, b
# x => 1
# y => 2
# z => 3