我有一个简单的if语句表现得非常奇怪。当我在if语句中为attr_accessor
赋值时,它会清除该值。这是一个展示问题的简明示例。
class FName
attr_accessor :name
attr_accessor :file_ext
def full_name
puts "initial file_ext value: #{file_ext}"
if file_ext.nil?
file_ext = ".default"
end
puts "new file_ext value: #{file_ext}"
"#{name}#{file_ext}"
end
end
f = FName.new
f.name = "foo"
f.file_ext = ".txt"
puts f.full_name
这输出以下内容:
initial file_ext value: .txt
new file_ext value:
foo
这根本不是你所期望的。任何人都知道这里发生了什么?
请注意,我可以在Ruby 2.0.0-p247和Ruby 2.1.1_1中重现此问题。另请注意,如果我删除行file_ext = ".wrong"
,那么当file_ext不为nil时,代码示例的行为与预期一致,但当然,当file_ext为nil时,我无法提供默认值。
我尝试将puts
语句放入if块,并且(正如预期的那样)它们什么都不打印。当我删除行f.file_ext = ".txt"
时,我得到输出:
initial file_ext value:
new file_ext value: .default
foo.default
最后一点说明:我希望像这样懒惰地提供默认值,因为在我的实际代码中计算了值,我希望除非必要,否则我会避免计算。
希望有人可以告诉我为什么会这样!
答案 0 :(得分:3)
这是Ruby的一个怪癖。这条线
file_ext = ".default"
必须更改为
self.file_ext = ".default"
如果没有self.
,则会静默创建局部变量file_ext
,而不是您想要的访问者调用。
即使if
主体由于Ruby
变量声明语义而未执行,也会创建它。它会自动获取值nil
,该值将打印为空字符串。以下是Ruby RDoc:
当解析器遇到赋值时创建局部变量,而不是在赋值发生时创建:
a = 0 if false # does not assign to a
p local_variables # prints [:a]
p a # prints nil
我分享了你的痛苦,在我生命中浪费了几个小时的Ruby语法“功能”。很多Ruby都非常漂亮,但这是糟糕的语言设计。
答案 1 :(得分:1)
def full_name
puts "initial file_ext value: #{file_ext}" # line 2
if file_ext.nil?
self.file_ext = ".default" # update this line (Line 4)
end
puts "new file_ext value: #{file_ext}"
"#{name}#{file_ext}"
end
第4行实际上不会执行,但是,Ruby源文件分析器会将file_ext = ".default"
视为局部变量赋值,并在执行if
语句后创建局部变量。但是,由于实际上没有调用赋值,因此局部变量将不会存储值。
在第2行,Ruby在本地范围内搜索变量file_ext
,当它失败时,它试图调用绑定到file_ext
的方法self
并从{{1}检索方法}}