首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当块有两个参数时,Ruby是如何表现的?

当块有两个参数时,Ruby是如何表现的?
EN

Stack Overflow用户
提问于 2019-01-02 00:15:42
回答 2查看 367关注 0票数 2

我有以下代码:

代码语言:javascript
复制
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]

这真的把我弄糊涂了,任何帮助都会很感激的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-01-02 04:10:02

我们得到两个数组:

代码语言:javascript
复制
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"] 

我们的第一步是计算这两个数组的乘积:

代码语言:javascript
复制
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"]]

要打印该数组元素的元素,我们可以编写:

代码语言:javascript
复制
arr.map do |a|
    rank = a[0]
    suit = a[1]
    p rank
    p suit
end
"2"
"Hearts"
"2"
"Clubs"
"2"
...
"Ace"
"Spades"

由于一次只有一个Ruby对象传递给一个块,因此有一个块变量是非常有意义的。arr的第一个元素被传递给块,块变量被赋值:

代码语言:javascript
复制
a = arr.first
  #=> ["2", "Hearts"] 

然后,我使用方法Array#[] (实际上,该方法的第一种形式显示在doc中)来提取数组a的第一个和第二个元素,并将这些值赋值给变量ranksuit

我们可以这样做的第二种方法如下。

代码语言:javascript
复制
arr.map do |a|
    rank, suit = a
    p rank
    p suit
end

它使用Array#decomposition

为了方便起见,Ruby允许我们使用数组分解直接计算多个块变量:

代码语言:javascript
复制
arr.map do |rank, suit|
    p rank
    p suit
end

当将arr的第一个元素传递给块时,Ruby会执行以下操作:

代码语言:javascript
复制
rank, suit = arr.first
  #=> ["2", "Hearts"] 
rank
  #=> "2" 
suit
  #=> "Hearts"

有时,块变量是以更复杂的方式编写的。假设,例如,

代码语言:javascript
复制
arr = [[1, [2, [3, {:a=>4}]], 5, 6]], [7, [8, [9, {:b=>10}]], 11, 12]]] 

我们可以写以下内容:

代码语言:javascript
复制
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的每个元素中包含的括号的位置与围绕块变量组的括号的位置进行比较,您将看到我的意思。

假设现在我们对bef的值没有兴趣。然后我们可以写:

代码语言:javascript
复制
arr.each do |a,(_,(c,d),*)|
  p a
  p c 
  p d
end
1
3
{:a=>4}
7
9
{:b=>10}

编写这样的块变量的一个重要原因是,它告诉读者在块计算中使用了arr中每个元素的哪些组件。

最后,以下是一个人经常遇到的典型模式。

代码语言:javascript
复制
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]

我们在这里的实际情况如下。

代码语言:javascript
复制
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生成的前三个值的情况。

代码语言:javascript
复制
(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

票数 3
EN

Stack Overflow用户

发布于 2019-01-02 08:10:01

块形式参数的形式参数绑定语义不同于方法形式参数的形式参数绑定语义。特别是,它们在处理形式参数数和实际参数之间的不匹配方面要灵活得多。

  • 如果正好有一个块形式参数,并且您需要yield多个块实际参数,则块形式参数将绑定到包含块实际参数的Array
  • 如果有多个块形式的参数,并且yield正好是一个块实际参数,而这一个实际参数是Array,那么块形式参数就绑定到Array的各个元素。
  • 如果yield的块实际参数比具有形式参数的块多,则忽略额外的实际参数。
  • 如果传递的实际参数比块具有形式参数的要少,那么这些额外的形式参数将被定义但不绑定,并计算为nil (就像定义的但统一的局部变量)。

如果仔细观察,您可以看到块形式参数的形式参数绑定语义更接近于赋值语义,也就是说,您可以想象赋值中赋值中的块形式参数位于赋值操作符的左侧,而块实际参数位于右侧。

如果您有这样定义的块:

代码语言:javascript
复制
{|a, b, c|}

yield是这样对待它的:

代码语言:javascript
复制
yield 1, 2, 3, 4

您几乎可以想象到,块形式参数绑定如下所示:

代码语言:javascript
复制
a, b, c = 1, 2, 3, 4

有趣的事实:到Ruby1.8,块形式参数绑定使用的是实际赋值!例如,您可以定义一个常量、一个实例变量、一个类变量、一个全局变量,甚至一个属性写入器(!)作为一个正式的参数,当您将yield编辑到该块时,Ruby实际上会执行赋值:

代码语言:javascript
复制
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对待。通常,我们更喜欢写作

代码语言:javascript
复制
hsh.each do |k, v|
  puts "The key is #{k} and the value is #{v}"
end

结束

代码语言:javascript
复制
hsh.each do |key_value_pair|
  k, v = key_value_pair
  puts "The key is #{k} and the value is #{v}"
end
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53999977

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档