我有以下代码:
RANKS = ((2..10).to_a + %w(Jack Queen King Ace)).freeze
SUITS = %w(Hearts Clubs Diamonds Spades).freeze
RANKS.product(SUITS).map do |rank, suit|
p rank
p suit
end我注意到,当我运行这段代码时,我得到了值并打印了西装,但是当我只使用一个参数(例如,| rank | )时,我就会得到一个子数组,比如[2, "Hearts"]。
这是否意味着当块有2个参数时,它正在访问sub-array[0]和sub-array[1]?
这真的把我弄糊涂了,任何帮助都会很感激的。
发布于 2019-01-02 04:10:02
我们得到两个数组:
RANKS = (("2".."10").to_a + %w(Jack Queen King Ace)).freeze
#=> ["2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"]
SUITS = %w(Hearts Clubs Diamonds Spades).freeze
#=> ["Hearts", "Clubs", "Diamonds", "Spades"] 我们的第一步是计算这两个数组的乘积:
arr = RANKS.product(SUITS)
#=> [["2", "Hearts"], ["2", "Clubs"], ["2", "Diamonds"], ["2", "Spades"],
# ["3", "Hearts"], ["3", "Clubs"], ["3", "Diamonds"], ["3", "Spades"],
# ...
# ["Ace", "Clubs"], ["Ace", "Diamonds"], ["Ace", "Spades"]]要打印该数组元素的元素,我们可以编写:
arr.map do |a|
rank = a[0]
suit = a[1]
p rank
p suit
end
"2"
"Hearts"
"2"
"Clubs"
"2"
...
"Ace"
"Spades"由于一次只有一个Ruby对象传递给一个块,因此有一个块变量是非常有意义的。arr的第一个元素被传递给块,块变量被赋值:
a = arr.first
#=> ["2", "Hearts"] 然后,我使用方法Array#[] (实际上,该方法的第一种形式显示在doc中)来提取数组a的第一个和第二个元素,并将这些值赋值给变量rank和suit。
我们可以这样做的第二种方法如下。
arr.map do |a|
rank, suit = a
p rank
p suit
end为了方便起见,Ruby允许我们使用数组分解直接计算多个块变量:
arr.map do |rank, suit|
p rank
p suit
end当将arr的第一个元素传递给块时,Ruby会执行以下操作:
rank, suit = arr.first
#=> ["2", "Hearts"]
rank
#=> "2"
suit
#=> "Hearts"有时,块变量是以更复杂的方式编写的。假设,例如,
arr = [[1, [2, [3, {:a=>4}]], 5, 6]], [7, [8, [9, {:b=>10}]], 11, 12]]] 我们可以写以下内容:
arr.each do |a,(b,(c,d),e,f)|
p a
p b
p c
p d
p e
p f
end
1
2
3
{:a=>4}
5
6
7
8
9
{:b=>10}
11
12这似乎有些先进,但它确实相当简单。如果您将arr的每个元素中包含的括号的位置与围绕块变量组的括号的位置进行比较,您将看到我的意思。
假设现在我们对b、e或f的值没有兴趣。然后我们可以写:
arr.each do |a,(_,(c,d),*)|
p a
p c
p d
end
1
3
{:a=>4}
7
9
{:b=>10}编写这样的块变量的一个重要原因是,它告诉读者在块计算中使用了arr中每个元素的哪些组件。
最后,以下是一个人经常遇到的典型模式。
arr = [1, 3, 4, 7, 2, 6, 9]
arr.each_with_object([]).with_index { |(n,a),i| a << n*n if i.odd? }
#=> [9, 49, 36]我们在这里的实际情况如下。
enum0 = arr.each_with_object([])
#=> #<Enumerator: [1, 3, 4, 7, 2, 6, 9]:each_with_object([])>
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator:
# [1, 3, 4, 7, 2, 6, 9]:each_with_object([])>:with_index> 枚举器enum1生成每个值,将其传递给块,块值被赋值并执行块计算。下面是enum1生成的前三个值的情况。
(n,a),i = enum1.next
#=> [[1, []], 0]
n #=> 1
a #=> []
i #=> 0
a << n*n if i.odd?
#=> nil
(n,a),i = enum1.next
#=> [[3, []], 1]
n #=> 3
a #=> []
i #=> 1
a << n*n if i.odd?
#=> [9]
(n,a),i = enum1.next
#=> [[4, [9]], 2]
n #=> 4
a #=> [9]
i #=> 2
a << n*n if i.odd?
#=> nil
a #=> [9] 见对象,索引和Enumerator#next。
发布于 2019-01-02 08:10:01
块形式参数的形式参数绑定语义不同于方法形式参数的形式参数绑定语义。特别是,它们在处理形式参数数和实际参数之间的不匹配方面要灵活得多。
yield多个块实际参数,则块形式参数将绑定到包含块实际参数的Array。yield正好是一个块实际参数,而这一个实际参数是Array,那么块形式参数就绑定到Array的各个元素。yield的块实际参数比具有形式参数的块多,则忽略额外的实际参数。nil (就像定义的但统一的局部变量)。如果仔细观察,您可以看到块形式参数的形式参数绑定语义更接近于赋值语义,也就是说,您可以想象赋值中赋值中的块形式参数位于赋值操作符的左侧,而块实际参数位于右侧。
如果您有这样定义的块:
{|a, b, c|}yield是这样对待它的:
yield 1, 2, 3, 4您几乎可以想象到,块形式参数绑定如下所示:
a, b, c = 1, 2, 3, 4有趣的事实:到Ruby1.8,块形式参数绑定使用的是实际赋值!例如,您可以定义一个常量、一个实例变量、一个类变量、一个全局变量,甚至一个属性写入器(!)作为一个正式的参数,当您将yield编辑到该块时,Ruby实际上会执行赋值:
class Foo
def bar=(value)
puts "`#{__method__}` called with `#{value.inspect}`"
@bar = value
end
attr_reader :bar
end
def set_foo
yield 42
end
foo = Foo.new
set_foo {|foo.bar|}
# `bar=` called with `42`
foo.bar
#=> 42很疯狂,是吧?
这些块形式参数绑定语义使用最广泛的应用程序是使用Hash#each (或以Hash实例作为接收方的任何Enumerable方法)。Hash#each方法yield是一个包含键和值的单一双元素Array,它是块的实际参数,但是我们几乎总是把它当作将键和值作为单独的实际参数的yield对待。通常,我们更喜欢写作
hsh.each do |k, v|
puts "The key is #{k} and the value is #{v}"
end结束
hsh.each do |key_value_pair|
k, v = key_value_pair
puts "The key is #{k} and the value is #{v}"
endhttps://stackoverflow.com/questions/53999977
复制相似问题