如何在Ruby中实现自定义变异方法?

时间:2018-11-03 09:25:29

标签: ruby

我正在编写一个gem,希望在C扩展中实现条件赋值。

该想法是为logical defined-or提供分配。仅当变量为nil时才分配。例如...

var = false
var //= 42   # var is false
var = nil
var //= 23   # var is 23

我在ruby/ruby存储库中发现了abbreviated assignment的定义:

There are also ||= and &&=. The former makes an assignment if
the value was nil or false while the latter makes an assignment 
if the value was not nil or false.

我没有在源代码中找到参考。 (如下面的||=注释中所述,它不是运算符,而只是语法糖)

我试图在apidock.com上查看一些爆炸方法的定义:例如Array#map!

static VALUE
rb_ary_collect_bang(VALUE ary)
{
    long i;

    RETURN_ENUMERATOR(ary, 0, 0);
    rb_ary_modify(ary);
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        rb_ary_store(ary, i, rb_yield(RARRAY_PTR(ary)[i]));
    }
    return ary;
}

我设法遵循array.c中对rb_ary_modifyrb_ary_store的引用。

您知道如何实现一种变异方法,例如:

require 'logic_or'

# Current behaviour
nil.lor('a') # => "a"
false.lor('a') # => false

a = nil # => nil
a.lor('a') # => "a"
a # => nil

# Expected behaviour
a = nil # => nil
a.lor!('a') # => "a"
a # => "a"

更新

正如评论中所指出的那样,先前的标题(如何用ruby实现Double pipe或等值(|| =)?)与问题的内容不一致

1 个答案:

答案 0 :(得分:2)

我认为您无法实现val //= 23,Perl将其称为“ dor”(定义或)。没有底层方法调用。相反,val ||= 23被编译为...

if !val
  val = 23
end

val //= 23是...

if val.nil?
  val = 23
end

您可以find the code for this in compile.c。您必须以某种方式修改解析器以添加新的idDOROP操作码并更改语法。这不是不可能,它一直在Perl中完成,但是非常混乱。

我也不认为您可以做val.dor(23)。那将需要分配到self,这是不允许的。

class Object
  def dor(value)
    self = value if self.nil?
  end
end

Can't change the value of self
    self = value if self.nil?

我能想到的最接近的是这个。

class Object
  def dor(value)
    value if self.nil?
  end
end

obj = obj.dor(42)
puts obj

相对于...而言,这是值得怀疑的改进

obj = 42 if obj.nil?
puts obj

通常,在Ruby中//=不是必需的。 //=之所以出现在Perl中,是因为Perl认为0""是错误的。在许多情况下,这些值是有效值,但会被$var ||= $default抹去。人们对写$var = $default if !defined $var感到厌倦,//=诞生了。

在Ruby中,只有两个错误值nilfalse。其他一切都是真的。 var ||= default将对0""做正确的事情,因此几乎不需要//=

如果您的设计需要经常区分nilfalse来编写新的运算符,请更改设计。