我现在正在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
因此代码更清晰,更易于维护。
希望这是有道理的。谢谢大家!
答案 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}}以实现此目的。