Ruby修改参数值

时间:2014-02-24 18:06:33

标签: ruby-on-rails ruby

我现在正在Ruby中练习ducktyping,并且我试图根据传递给同一方法的另一个参数来修改参数的值。但是,它只是不起作用。

例如:

  class Duck

  attr_reader :foo, :bar

  def initialize
    @foo = false
    @bar = false
  end

  def duck_foo
    ducktype(@foo, @bar)
  end

  def duck_bar
    ducktype(@bar, @foo)
  end

  def ducktype(duck1, duck2)
    p duck1 #=> false
    p duck2 #=> false
    puts "foo: #{foo} bar: #{bar}" #=> "foo: false bar: false"
    duck1 = true if duck2 == false #<= For #duck_foo: I want to make @foo = true if @bar == false. But it only changes duck1 to true. / For #duck_bar: I want to make @bar = true.
    p duck1 #=> true
    p duck2 #=> false
    puts "foo: #{foo} bar: #{bar}" #=> "foo: false bar: false" => @foo is still false. I want it true!
  end

end

duck = Duck.new
duck.duck_foo
duck.duck_bar

#duck_foo的输出,我希望看到@foo变为真。但是,它只将duck1更改为true,而@foo仍为false。

我怎样才能让它发挥作用?

从本质上讲,我正在努力:

 def duck_foo
    p foo
    p bar
    puts "foo: #{foo} bar: #{bar}"
    @foo = true if @bar == false #=> change @foo to true.
    p foo
    p bar
    puts "foo: #{foo} bar: #{bar}"
  end

  def duck_bar
    p foo
    p bar
    puts "foo: #{foo} bar: #{bar}"
    @bar = true if @foo == false #=> change @bar to true.
    p foo
    p bar
    puts "foo: #{foo} bar: #{bar}"
  end

分为:

  def duck_foo
    ducktype(@foo, @bar)
  end

  def duck_bar
    ducktype(@bar, @foo)
  end

  def ducktype(duck1, duck2)
    #whatever code necessary to preserve the original methods' behavior.
  end

因此代码更清晰,更易于维护。

希望这是有道理的。谢谢大家!

2 个答案:

答案 0 :(得分:1)

这里的问题是你试图通过赋值给局部变量引用来修改实例变量所持有的值。抱歉,但这不会在ruby中起作用 - 分配给局部变量只会使局部变量指向新对象而不会影响第一个对象。

但是,您可以通过一些小修改轻松地完成所需的操作(尽管您希望使用attr_accessor代替attr_reader来避免丑陋的eval块):

class Duck

  attr_accessor :foo, :bar

  def initialize
    @foo = false
    @bar = false
  end

  def duck_foo
    ducktype(:foo, :bar)
  end

  def duck_bar
    ducktype(:bar, :foo)
  end

  def ducktype(duck1, duck2)
    p send(duck1) #=> false
    p send(duck2) #=> false
    puts "foo: #{foo} bar: #{bar}" #=> "foo: false bar: false"
    send(:"#{duck1}=", true) if send(duck2) == false 
    p send(duck1) #=> true
    p send(duck2) #=> false
    puts "foo: #{foo} bar: #{bar}" #=> "foo: true bar: false"
  end
end

在这个实现中,我们不是传递实例变量(当传递给局部变量时失去上下文),而是将我们的意图传递为符号'messages'。然后我们可以根据需要使用这些符号与我们的对象进行交互。

答案 1 :(得分:0)

一种方法是检查object_id

  def ducktype(duck1, duck2)
    puts "foo: #{foo} bar: #{bar}" #=> "foo: false bar: false"
    if duck1.object_id == @foo.object_id
        @foo = true if !duck2
    elsif duck1.object_id == @bar.object_id
        @bar = true if !duck2
    end
    puts "foo: #{foo} bar: #{bar}" #=> "foo: true bar: false"
  end

由于您传入布尔值,因此您无法使用str.replace('true')来使用object_id从函数中更改其值,就像使用字符串一样。


您何时需要查看@foo

如果@bar def ducktype(duck1, duck2) puts "foo: #{foo} bar: #{bar}" #=> "foo: false bar: false" duck1.replace('true') if duck2 == 'false' puts "foo: #{foo} bar: #{bar}" #=> "foo: true bar: false" end 是字符串而不是布尔值,那么你可以这样做:

duck1 = true

duck1 = true # => true duck1.object_id # => 2 duck1 = false # => false duck1.object_id # => 0 <-- id changes duck1 = 'true' # => "true" duck1.object_id # => 6563940 duck1.replace('false') # => "false" duck1.object_id # => 6563940 <-- id stays the same 无论其类型如何都不起作用的原因是因为为其分配了新实例。即:

object_id

因此,如果数据类型不具有可以更改其自身值的成员函数,那么似乎必须按上述方法检查{{1}}以实现此目的。