Ruby中的奇怪之处

时间:2014-04-19 01:33:01

标签: ruby

我有一个简单的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

最后一点说明:我希望像这样懒惰地提供默认值,因为在我的实际代码中计算了值,我希望除非必要,否则我会避免计算。

希望有人可以告诉我为什么会这样!

2 个答案:

答案 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}检索方法}}