使用数组或多个参数的Ruby块

使用数组或多个参数的Ruby块,ruby,block,enumerable,Ruby,Block,Enumerable,今天,我惊奇地发现ruby能够自动找到作为块参数给出的数组的值 例如: foo = "foo" bar = "bar" p foo.chars.zip(bar.chars).map { |pair| pair }.first #=> ["f", "b"] p foo.chars.zip(bar.chars).map { |a, b| "#{a},#{b}" }.first #=> "f,b" p foo.chars.zip(bar.chars).map { |a, b,c| "#{a

今天,我惊奇地发现ruby能够自动找到作为块参数给出的数组的值

例如:

foo = "foo"
bar = "bar"
p foo.chars.zip(bar.chars).map { |pair| pair }.first #=> ["f", "b"]
p foo.chars.zip(bar.chars).map { |a, b| "#{a},#{b}" }.first #=> "f,b"
p foo.chars.zip(bar.chars).map { |a, b,c| "#{a},#{b},#{c}" }.first #=> "f,b,"
我原以为最后两个例子会给出某种错误

  • 这是ruby中更一般概念的一个例子吗
  • 我不认为我问题开头的措辞是正确的,我该怎么称呼这里发生的事情

  • Ruby的块机制有一个怪癖,那就是如果你在迭代包含数组的东西,你可以将它们展开成不同的变量:

    [ %w[ a b ], %w[ c d ] ].each do |a, b|
      puts 'a=%s b=%s' % [ a, b ]
    end
    
    a, b = %w[ a b ]
    a
    # => 'a'
    b
    # => 'b'
    
    当使用
    Hash#each
    时,此模式非常有用,您需要将
    分成两部分:
    each{k,v |…}
    在Ruby代码中非常常见

    如果块接受多个参数,并且被迭代的元素是一个数组,那么它将切换参数的解释方式。您始终可以强制展开:

    [ %w[ a b ], %w[ c d ] ].each do |(a, b)|
      puts 'a=%s b=%s' % [ a, b ]
    end
    
    这对于情况更复杂的情况非常有用:

    [ %w[ a b ], %w[ c d ] ].each_with_index do |(a, b), i|
      puts 'a=%s b=%s @ %d' % [ a, b, i ]
    end
    
    因为在本例中,它迭代一个数组和附加的另一个元素,所以每个项实际上是一个元组,内部形式为
    %w[ab],0
    ,如果块只接受一个参数,它将转换为数组

    这与定义变量时可以使用的原理大致相同:

    [ %w[ a b ], %w[ c d ] ].each do |a, b|
      puts 'a=%s b=%s' % [ a, b ]
    end
    
    a, b = %w[ a b ]
    a
    # => 'a'
    b
    # => 'b'
    
    这实际上为
    a
    b
    分配了独立的值。与之相比:

    a, b = [ %w[ a b ] ]
    a
    # => [ 'a', 'b' ]
    b
    # => nil
    

    Ruby block就是这样古怪的

    规则是这样的,如果一个块接受多个参数,并生成一个响应
    到_ary
    的单个对象,则该对象将展开。这使得生成数组与生成元组对于包含两个或多个参数的块的行为似乎相同

    yield[a,b]
    yield a,b
    确实不同,尽管当块只接受一个参数或块接受可变数量的参数时

    让我来演示这两个方面

    def yield_tuple
      yield 1, 2, 3
    end
    
    yield_tuple { |*a| p a }
    yield_tuple { |a| p [a] }
    yield_tuple { |a, b| p [a, b] }
    yield_tuple { |a, b, c| p [a, b, c] }
    yield_tuple { |a, b, c, d| p [a, b, c, d] } 
    
    印刷品

    [1, 2, 3]
    [1] 
    [1, 2]
    [1, 2, 3]
    [1, 2, 3, nil]
    
    [[1, 2, 3]]
    [[1, 2, 3]] 
    [1, 2] # array expansion makes it look like a tuple
    [1, 2, 3] # array expansion makes it look like a tuple
    [1, 2, 3, nil] # array expansion makes it look like a tuple
    
    [#<A:0x007fc3c2969190>]
    [#<A:0x007fc3c2969050>]
    [1, 2] # array expansion makes it look like a tuple
    [1, 2, 3] # array expansion makes it look like a tuple
    [1, 2, 3, nil] # array expansion makes it look like a tuple
    
    鉴于

    def yield_array
      yield [1,2,3]
    end
    
    yield_array { |*a| p a }
    yield_array { |a| p [a] }
    yield_array { |a, b| p [a, b] }
    yield_array { |a, b, c| p [a, b, c] }
    yield_array { |a, b, c, d| p [a, b, c, d] }
    
    印刷品

    [1, 2, 3]
    [1] 
    [1, 2]
    [1, 2, 3]
    [1, 2, 3, nil]
    
    [[1, 2, 3]]
    [[1, 2, 3]] 
    [1, 2] # array expansion makes it look like a tuple
    [1, 2, 3] # array expansion makes it look like a tuple
    [1, 2, 3, nil] # array expansion makes it look like a tuple
    
    [#<A:0x007fc3c2969190>]
    [#<A:0x007fc3c2969050>]
    [1, 2] # array expansion makes it look like a tuple
    [1, 2, 3] # array expansion makes it look like a tuple
    [1, 2, 3, nil] # array expansion makes it look like a tuple
    
    最后要说明的是Ruby中的所有东西都使用duck类型

    class A
      def to_ary
        [1,2,3]
      end
    end
    
    def yield_arrayish
      yield A.new
    end
    
    yield_arrayish { |*a| p a }
    yield_arrayish { |a| p [a] }
    yield_arrayish { |a, b| p [a, b] }
    yield_arrayish { |a, b, c| p [a, b, c] }
    yield_arrayish { |a, b, c, d| p [a, b, c, d] }
    
    印刷品

    [1, 2, 3]
    [1] 
    [1, 2]
    [1, 2, 3]
    [1, 2, 3, nil]
    
    [[1, 2, 3]]
    [[1, 2, 3]] 
    [1, 2] # array expansion makes it look like a tuple
    [1, 2, 3] # array expansion makes it look like a tuple
    [1, 2, 3, nil] # array expansion makes it look like a tuple
    
    [#<A:0x007fc3c2969190>]
    [#<A:0x007fc3c2969050>]
    [1, 2] # array expansion makes it look like a tuple
    [1, 2, 3] # array expansion makes it look like a tuple
    [1, 2, 3, nil] # array expansion makes it look like a tuple
    
    [#]
    [#]
    [1,2]#数组扩展使它看起来像一个元组
    [1,2,3]#数组扩展使其看起来像一个元组
    [1,2,3,nil]#数组扩展使它看起来像一个元组
    
    另外,相同的数组扩展行为适用于行为类似于块的
    proc
    闭包,而
    lambda
    闭包的行为类似于方法

    我原以为最后两个例子会给出某种错误

    如果您从一个方法传递
    proc
    ,它实际上就是这样工作的。屈服于这样一个过程要严格得多——它检查其算术性,不尝试将数组参数转换为参数列表:

    def m(a, b)
      "#{a}-#{b}"
    end
    
    ['a', 'b', 'c'].zip([0, 1, 2]).map(&method(:m))
    #=> wrong number of arguments (given 1, expected 2) (ArgumentError)
    
    这是因为
    zip
    创建了一个数组,而
    map
    只生成每个元素,即

    yield ['a', 0]
    yield ['b', 1]
    yield ['c', 2]
    
    另一方面,每个带有索引的\u工作:

    ['a', 'b', 'c'].each_with_index.map(&method(:m))
    #=> ["a-0", "b-1", "c-2"]
    
    因为它产生两个独立的值,元素和它的索引,即

    yield 'a', 0
    yield 'b', 1
    yield 'c', 2
    

    这类似于(可能与?)数组中变量的并行赋值。因此,这里的正确术语是:“Ruby正在将每个数组扩展到传递给map实例方法的块中的变量a、b或c”@mbigras大多数语言都将其称为“destructuring”,ES6现在有它、LISP()、clojure()等等。在
    yield[a,b]之间存在差异
    产生a,b
    ,尽管它们在块只需要一个参数时的行为。元素不是
    [%w[ab],0]
    内部,请参见
    [%w[ab],%w[cd]]。每个{a | pa}
    只打印没有索引的元素。在内部
    每个带有_索引的_
    生成块的元组,而不是数组。所以它使用了等价的
    yield each,index
    而不是
    yield[each,index]
    是的,我明白你的意思了。我喜欢你的答案,但是你应该把输出移到更接近示例的地方,这样就更容易理解了。我还试图用一种更抽象的方式来描述这种行为,而元组和数组之间的微妙之处很难表达。我又更新了措辞,说得好。您还可以使用
    lambda
    来演示这一点<代码>[1,2,3]。每个&lambda{a,b}
    都失败,但
    [1,2,3]。每个&proc{a,b}
    都工作。