Ruby 周五闲谈,method 自身对比时竟不相等

dfzy5566 · 2017年08月04日 · 最后由 dfzy5566 回复于 2017年08月04日 · 1888 次阅读

在 ruby 中一切皆对象 (Object),既然是对象,则可以用来对等比较。

Object.methods.grep /\=\=/ #=> [:==, :===]

我们可以看到确实存在 == 的方法可以用来对等比较所有对象。

于是,我们对下列对象自身作对比:

# Module
Object == Object  #=> true

# Class
String == String  #=> true

# Proc
p = -> {x + y + z}
p == p            #=> true

# Object
"A" == String.new("A")  #=> true
1 == Integer(1)         #=> true
nil == nil              #=> true

这看起来自身对比的结果都是一样的,皆为true, 但当我们对比到method时,情况发生了变化:

require 'date'
method_one = Object.instance_method(:method).bind(Date.today).call(:year)  #=> #<Method: Date#year>
method_two = Date.today.method(:year)                                      #=> #<Method: Date#year>

method_one.receiver == method_two.receiver  # true
method_one == method_two                    # false
# method_two == method_two   # false  # 经测试,结果为 true,此处写错

# or

Date.today.method(:year)  ==  Date.today.method(:year)  # false

我们发现,同一个method在自身对比时并不相等,有意思了。怎解? [手动滑稽]

补充

我发现 Method== 方法被重写了,它和普通的 Object 不一样,其 C 代码如下:

static VALUE
method_eq(VALUE method, VALUE other)
{
    struct METHOD *m1, *m2;
    VALUE klass1, klass2;

    if (!rb_obj_is_method(other))
    return Qfalse;
    if (CLASS_OF(method) != CLASS_OF(other))
    return Qfalse;

    Check_TypedStruct(method, &method_data_type);
    m1 = (struct METHOD *)DATA_PTR(method);
    m2 = (struct METHOD *)DATA_PTR(other);

    klass1 = method_entry_defined_class(m1->me);
    klass2 = method_entry_defined_class(m2->me);

    if (!rb_method_entry_eq(m1->me, m2->me) ||
    klass1 != klass2 ||
    m1->klass != m2->klass ||
    m1->recv != m2->recv) {
    return Qfalse;
    }

    return Qtrue;
}

所以,method 的判断机制就包含在代码中了,可惜,看不太懂 - -

如果有人能解读一下就好 😃

两个方法不是同一个对象的,你这里有两个 Date.today,牵扯到影子类

我的运行结果,与你不同

method_one == method_two   # false
method_two == method_two   # true

对于method_one == method_two # false,是因为 方法的接收者 不同,所以不相等。 以下是我的运行结果:

method_one.receiver.object_id # 70321140521840
method_two.receiver.object_id # 70321397845200
zjyzxun 回复

嗯嗯,有些道理,不过我有点疑问,如果是根据 receiver__id__ 的不一致,所以不相等的话,那为何 receiver 本身又是相等的啊 - -

method_one.receiver == method_two.receiver  # true

当然也有可能只是 method 比较特殊,在其判断相等时,需要判断 receiver.__id__ , 因为连string 的判断都不需要判断 object_id 的:

"A".__id__ == String.new("A").__id__  # fasle
"A" == String.new("A")                # true
winse 回复

嗯嗯。那这样呢:

Date.today.method(:year)  ==  Date.today.method(:year)  # false
dfzy5566 回复

receiver 其实就是 Date.today

require 'date'
a = Date.today
b = Date.today

a == b # true
a.object_id == b.object_id # false

a 和 b 的值是相等的。但是不是同一个对象。

zjyzxun 回复

您没理解我的意思,我的意思是 == 的判断应该不会去判断 object_id。 如果会判断 object_id 的话:

require 'date'
a = Date.today
b = Date.today

a == b # true
a.object_id == b.object_id # false

其中 a == b 就应该为 false

既然为true 那说明没有去判断 object_id, 所以您之前说的method判断 receiver.object_id来看是不是相等就值得商榷了。

method 需要 绑定在同一个对象下 == 才为 true

zjyzxun 回复

嗯嗯,是的呢,我也发现了method中的==方法和其它的相等判断不一样。

那还有什么疑问吗?一起探讨下

zjyzxun 回复

暂时没有了,下次有了再来请教,给你点赞!

需要 登录 后方可回复, 如果你还没有账号请 注册新账号