Ruby“已定义?”操作员工作不正确? [英] Ruby "defined?" operator works wrong?

查看:95
本文介绍了Ruby“已定义?”操作员工作不正确?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我们有以下代码:

So, we have the code:

class Foo
  def bar
    puts "Before existent: #{(defined? some_variable)}"
    puts "Before not_existent: #{(defined? nonexistent_variable)}"

    raise "error"

    some_variable = 42
  rescue
    puts "exception"
  ensure
    puts "Ensure existent: #{(defined? some_variable)}"
    puts "Ensure not_existent: #{(defined? nonexistent_variable)}"
  end
end

并从irb调用:

> Foo.new.bar

然后,它将返回:

Before existent:
Before not_existent:
exception
Ensure existent: local-variable
Ensure not_existent:
=> nil

现在是问题-为什么?我们在之前引发了异常,该异常比定义的 some_variable 更高。
为什么这样工作?为什么在sure块中定义了 some_variable ? (顺便说一句,它定义为nil)

And now is question - why? We raised exception before than some_variable be defined. Why it works this way? Why some_variable is defined in ensure block? (btw, it defined as nil)

更新:
感谢@Max的回答,但是如果我们更改代码以使用实例变量:

UPDATE: Thanks @Max for answer, but if we change code to use instance variable:

class Foo
  def bar
    puts "Before existent: #{(defined? @some_variable)}"
    puts "Before not_existent: #{(defined? @nonexistent_variable)}"

    raise "error"

    @some_variable = 42
  ensure
    puts "Ensure existent: #{(defined? @some_variable)}"
    puts "Ensure not_existent: #{(defined? @nonexistent_variable)}"
  end
end

它按预期工作:

Before existent:
Before not_existent:
Ensure existent:
Ensure not_existent:

为什么?

推荐答案

首先要注意的是 关键字,而不是方法。这意味着解析器在编译期间由解析器专门处理。构造语法树(就像 if return next 等),而不是动态地查看运行时。

The first thing to notice is that defined? is a keyword, not a method. That means that is specially handled by the parser during compilation when the syntax tree is constructed (just like if, return, next, etc.) rather than dynamically looked up at runtime.

这就是为什么已定义?可以处理表达式的原因通常会引发错误: defined?(这是什至是什么)#=> nil ,因为解析器可以将其参数排除在常规评估过程之外。

This is why defined? can handle expressions that would normally raise an error: defined?(what is this even) #=> nil because the parser can exclude its argument from the normal evaluating process.

真正令人困惑的一点是,即使它是一个关键字,它的行为仍在运行时确定。它使用解析器魔术来确定其参数是否为实例变量,常量,方法等。但是随后调用常规的Ruby方法来确定是否已在运行时定义了这些特定类型:

The really confusing bit is that even though it is a keyword, its behavior is still determined at runtime. It uses parser magic to determine whether its argument is an instance variable, constant, method, etc. but then calls normal Ruby methods to determine whether these specific types have been defined at runtime:

// ...
case DEFINED_GVAR:
if (rb_gvar_defined(rb_global_entry(SYM2ID(obj)))) {
    expr_type = DEFINED_GVAR;
}
break;
case DEFINED_CVAR:
// ...
if (rb_cvar_defined(klass, SYM2ID(obj))) {
    expr_type = DEFINED_CVAR;
}
break;
case DEFINED_CONST:
// ...
if (vm_get_ev_const(th, klass, SYM2ID(obj), 1)) {
    expr_type = DEFINED_CONST;
}
break;
// ...

rb_cvar_defined 函数与 <例如,code> Module#class_variable_defined?

所以已定义?很奇怪。真奇怪。根据其参数的不同,它的行为可能会有很大的不同,我什至不敢相信在不同的Ruby实现中它是相同的。基于此,我建议不要使用它,而尽可能使用Ruby的 * _ defined?方法。

So defined? is weird. Really weird. Its behavior could vary a lot depending on its argument, and I wouldn't even bet on it being the same across different Ruby implementations. Based on this I would recommend not using it and instead use Ruby's *_defined? methods wherever possible.

这篇关于Ruby“已定义?”操作员工作不正确?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆