从块返回值而不从方法返回

时间:2012-12-09 04:15:26

标签: ruby block next

我有一个班级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]

如何修改这段代码(或者理想情况下我在第二段代码片段中指出的那一行),以便获得输出[1, 1, 3, 3]?换句话说,如何完成块完成,但将最后一个值传递回map?是否有一种简洁易读的方法(另外,例如,切换布尔标志break_now)?

3 个答案:

答案 0 :(得分:1)

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

这样的事情怎么样:

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

答案 1 :(得分:0)

一般情况下,您可以通过修改现有的屈服值来避免这种情况。例如,如果您的数组由字符串而不是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的原因。

答案 2 :(得分:-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]