我如何实现类似下面的内容,以便在我在块中设置s
变量时,它还在我的Topic类中设置@subject
实例变量?
class Topic
def subject(&blk)
blk.call(@subject) if block_given?
@subject unless block_given?
end
end
my_topic = Topic.new
p my_topic.subject #=> nil
my_topic.subject do |s|
s = ['one', 'two', 'three']
s.pop
p s #=> ['one', 'two']
end
p my_topic.subject #=> nil... want it to be ['one, 'two']
答案 0 :(得分:4)
你不能按照你想要的方式去做。 block参数引用与实例变量相同的对象,但它们是完全不同的变量,设置一个永远不会设置另一个。有两种选择:
将变量设置为块的结果,如下所示:
class Topic
def subject
@subject = yield if block_given?
@subject unless block_given?
end
end
并在街区内:
my_topic.subject do
s = ['one', 'two', 'three']
s.pop
p s #=> ['one', 'two']
s
end
使用subject
方法instance_eval
块,以便块可以显式设置实例变量
答案 1 :(得分:3)
您想要做的是被称为传递引用。这在红宝石中是不可能的。您有两种选择:
a)执行@subject = blk.call
并从块中返回s。通常是最简单,最干净的选择。
b)代替s =
执行@subject =
,然后使用instance_eval(&blk)
代替blk.call
。这将设置@subject
变量,但是它需要主题方法的用户知道@subject
变量,并且它不允许您多次调用块来设置不同的变量。
答案 2 :(得分:2)
除了给定的解决方案,如果您知道ivar将保留String / Array / Hash,无论如何,您可以执行以下操作:
class Topic
def subject
@subject ||= 'sane default'
if block_given? then yield(@subject)
else @subject
end
end
end
t = Topic.new
t.subject { |s| s.replace 'fancy stuff' }
虽然我猜你正在做的事情,但这是最合适的代码:
class Topic
def subject
return @subject unless block_given?
@subject = yield(@subject)
end
end
t = Topic.new
t.subject { |s| 'fancy stuff' }
t.subject { |s| "very #{s}" }
t.subject # => "very fancy stuff"
另外,你可以在没有阻止的情况下实现这一点:
class Topic
def subject(value = nil)
@subject = value % @subject if value
@subject = yield @subject if block_given?
@subject
end
end
t = Topic.new
t.subject 'fancy stuff' # => "fancy stuff"
t.subject 'very %s' # => "very fancy stuff"
t.subject { |s| s.sub 'fancy', 'freaky' } # => "very freaky stuff"
请注意,您使用的语句p s
会返回nil
。