如何在方法之前对运算符进行求值?

时间:2014-06-29 01:49:59

标签: ruby metaprogramming

如果我定义了一个运算符,

class Object
  def ~@
    self || ErrorlessNil.new
  end
end

我怎样才能使~首先评估,而不是最后评估?现在,像是

~[1][100].undefined_method

会抛出错误,而

(~[0][22]).randomsadfldsf

工作正常。目标是定义一个像coffeescript的问号一样工作的工具。我可以制作??方法,但我不能以?开头,而_?可以正常工作,但这并不好玩。

3 个答案:

答案 0 :(得分:3)

JörgWMittag的回答提供了一个不能按你想要的方式工作的原因,但还有另外一个原因。

如果~的优先级高于其他方法应用,那么

~[0][22].randomsadfldsf

不会被解释为

(~[0][22]).randomsadfldsf

但是作为

(~[0])[22].randomsadfldsf

答案 1 :(得分:2)

你不能。

在Ruby中,您只能覆盖现有运算符的行为。您无法定义新的运算符,也无法更改其优先级,优点,关联性或固定性。

这与Haskell或Fortress不同,它允许您使用自己的固定(前缀,后缀,中缀),关联性(左,右,无)和优先级来定义自己的运算符。在这方面,Ruby就像Python,C#和C ++。

答案 2 :(得分:1)

明显而简单的答案是,除()外,您无法更改运算符优先级。

但是你真的想要,你可以使用eval *或/,你可以构建你的抽象语法树(AST)/ SEXP。

str = "~[1,2,3].first"
def foo str
  if str[0] == '~'
    obj, meth = str[1..-1].split '.'
    eval "(~#{obj}).#{meth}"
  end
end
foo str

foo要求str采用以下格式:

"<~><object/literal without dot><method without dot>"

随着代码越来越复杂,foo变得越来越复杂。您将以Regexp或AST / SEXP结尾。

Build-in Ruby Ripper可以将源变为sexp。有一个名为Sorcerer的宝石。它可以将Sexp从开膛手改回源头。然而,它仅用1.9.3和2.0测试。

* eval可能不安全,因为它可能会运行您不想要的代码。在评估之前检查一下你的字符串!