【问题标题】:Return a value from a block without returning from method从块返回值而不从方法返回
【发布时间】:2012-11-26 21:36:42
【问题描述】:

我有一堂课Test:

class Test 
  attr_accessor :data
  def initialize
    @data = [0, 1, 2, 3]
  end
  def map
    @data.map!{|i| i = yield i }
  end
end

我尝试像这样使用它:

a = Test.new

a.map{|i|
  if(i==2)
    i+=1
    break i  #<--- -This line is the focus
  else
    1
  end
}

puts a.data

我期望的输出是[1, 1, 3, 3]。相反,我得到[1, 1, 2, 3]map 中块的最后一次迭代不会返回修改后的 i

我将break i 替换为next i。这按我的预期执行,并产生了输出[1, 1, 3, 1]

如何修改这段代码(或者,最好是我在第二个代码-sn-p 中指出的行),以便获得输出 [1, 1, 3, 3]?换句话说,我怎样才能使块完成,但将最后一个值传递回map?有没有一种简洁易读的方式来做到这一点(例如,切换布尔标志break_now)?

【问题讨论】:

    标签: ruby block next


    【解决方案1】:

    你必须这样做:

    a.map{ |i| (i % 2 == 0) ? i + 1 : i }
    

    当您使用地图功能时,您不会更改“a”变量,如果您想更改“a”变量,请执行以下操作:

    a.map!{ |i| (i % 2 == 0) ? i + 1 : i }
    

    'i'的新值是块返回的值,所以不要这样做:

    a.map{|i| i = 1 }
    

    因为如果你这样做:

    a.map{|i| i = 1; 5 }
    

    结果将是:

     [5, 5, 5, 5]
    

    【讨论】:

    • 我认为他是在询问如何在使用当前值而不是示例中提到的具体数值的同时离开块。
    【解决方案2】:

    我假设您问的是如何离开一个块并使用计算出的最后一个值,而不是如何计算一组特定的数字;对于后者,可能有一个聪明的单线。

    这样的事情怎么样:

    class Test
      attr_accessor :data
    
      def initialize
        @data = [0, 1, 2, 3]
      end
    
      def modify
        @data.map! {|i| yield i }
      end
    end
    
    a = Test.new
    a.modify do |i|
      break i if @done
      @done = i == 2
      @done ? (i + 1) : 1
    end
    
    puts a.data
    

    另外一个想法——#map 是 Ruby 中具有特定接口的重要方法。在您的示例中,您通过修改Test 中的字段来违反接口。出于这个原因,我改用了名称#modify

    【讨论】:

    • 非常感谢您的回答。你能澄清一下#map 是什么意思吗?我认为这差不多?
    • @Paul:根据调用方式,#map 在不修改调用对象的情况下返回ArrayEnumerable
    • @Paul:我应该更正之前的评论——将其设为枚举器,不一定是Enumerable
    【解决方案3】:

    一般来说,您可以通过修改产生的值来解决这个问题。例如,如果您的数组由字符串而不是 Fixnums 组成:

    class Test
      attr_accessor :data
    
      def initialize
        @data = %w{a b c d}
      end
    
      def map
        @data.map! { |i| yield i }
      end
    end
    
    a = Test.new
    a.map do |i|
      if i == 'c'
        i.next!
        break
      else
        'b'
      end
    end
    
    p a.data   #=> ["b", "b", "d", "d"]
    

    你的例子的问题是this:

    Fixnum 对象具有直接价值。这意味着当它们被分配或作为参数传递时,传递的是实际对象,而不是对该对象的引用。赋值不对 Fixnum 对象起别名。对于任何给定的整数值,实际上只有一个 Fixnum 对象实例……

    Fixnums 不能就地更改,因此您在下部块中的表达式 i += 1 不会影响上部块中 i 的值。这就是为什么你在你的例子中得到2,而在我的例子中得到d

    【讨论】:

      猜你喜欢
      • 2015-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多