讲述可变对象

时间:2012-10-11 22:34:10

标签: ruby immutability mutable

是否有方法可以判断对象是否可变,类似于以下mutable??如果没有,实施它的最佳方法是什么?

"abcde".mutable? # => true
0.mutable? # => false

要回答mu太短而dbenhur的问题,我不喜欢enumerated.inject(initial){...}enumerated.each_with_object(initial){...}的语法。我想要一个方法来反转接收器和参数,我希望它可用于各种各样的类;所以我有:

initial.my_new_method(enumerated){...}

0.my_new_method(1..10){|sum, i| sum + i} # => 55
"a".my_new_method(b: 3, c: 4){|s, (k, v)| s + k.to_s * v} # => "abbbcccc"

这将使返回成为接收器的修改版本,并且在概念上更自然。对于my_new_method,我希望它具有非破坏性。当接收器是可变的时,我还想要定义破坏性版本

initial.my_new_method!(enumerated){...}

"a".my_new_method!(b: 3, c: 4){|s, (k, v)| s << k.to_s * v} # => "abbbcccc"

因此,检测接收器是否可变是必要的。它是否冻结无关紧要。如果我使用冻结对象的方法的破坏性版本,它只会引发错误。没错。

4 个答案:

答案 0 :(得分:2)

AFAIK所有(解冻)对象都是可变的,除了nil,true,false以及所有整数和符号。

答案 1 :(得分:1)

您可以在对象上调用.frozen?以查看该对象的实例是否是不可变的:

1.9.3p194 :001 > a = Array.new
 => [] 
1.9.3p194 :002 > a.frozen?
 => false 
1.9.3p194 :003 > a.freeze
 => [] 
1.9.3p194 :004 > a.frozen?
 => true 

调用.freeze后,对象的该实例不允许进行其他更改,如果有人试图更改冻结对象,则会抛出RuntimeError

修改

如下面的评论0.frozen?中提到的检查将正确返回false,因为0是Fixnum类的实例。

答案 2 :(得分:1)

有两个属性可以使对象在ruby中不可变

1)对象可能会被Object#freeze冻结,在这种情况下,您的不变性检查为Object#frozen?

2)对象可以是immediate value。我知道没有内置的方法告诉对象是直接的,所以必须依赖于直接性质的副作用。立即值不允许在其上定义单例类,因此我可以尝试以下作为代理:

class Object
  def immediate_value?
    class <<self; end
    return false
  rescue TypeError
    return true
  end

  def mutable?
    !(frozen? || immediate_value?)
  end
end

虽然这可能是一个非常可靠的探测器(我不知道另一种阻止打开对象的单例类的机制),但它确实会产生令人遗憾的副作用,即为每个被查询的对象创建一个单例类。

答案 3 :(得分:-2)

我想出了这个

class Object
  def mutable?; !!(dup rescue false) end
end