承诺不修改Ruby函数中的参数

时间:2016-01-24 11:57:43

标签: ruby immutability

Ruby允许您通过以大写字母开头来声明不应修改变量。你也可以为正式的函数参数做这个吗?

解释器应警告您是否修改了值。

作为有效的例子:

def append(Constant xs, x)
     xs + x
end

无效的例子:

def append(Constant xs, x)
     xs += x
end

另一个有效的例子:

def shift(Constant x)
     xs.first + xs.all_but_first
end

另一个无效的例子:

def append(Constant xs, x)
     xs.shift!
end

2 个答案:

答案 0 :(得分:4)

您可以freeze该对象。

  

防止对obj的进一步修改。将引发RuntimeError   如果尝试修改。没有办法解冻冻结   对象

示例:

def meth a,b
    a,b = a.clone.freeze, b.clone.freeze
    a[:d] = 10  # Not allowed
end

meth({:a=> 10},"World")
#=> something.rb:3:in `meth': can't modify frozen Hash (RuntimeError)

正如@Jorg的评论所指出的,为了避免副作用,我们可能必须在冻结之前克隆参数。

另请注意,您可以为参数ab分配新值,即使它们已被冻结,因为赋值不会改变现有的冻结对象 - 这些分配在方法外部将不可见。

同样,如果您尝试修改b

b.replace("xyz")

会导致错误:

something.rb:3: in `replace': can't modify frozen String (RuntimeError)

答案 1 :(得分:2)

  

Ruby允许您通过以大写字母开头来声明不应修改变量。你也可以为正式的函数参数做这个吗?

     

解释器应警告您是否修改了值。

我觉得你很困惑。 Ruby是按值传递,而不是按引用传递。您根本无法在调用者的范围中修改方法参数的绑定:

def foo(bar)
  bar = 'something else'
end

baz = "I won't change."

foo(baz)

baz
# => "I won't change."

将示例添加到问题中:

# valid
def append(xs, x)
  xs + x
end

xs, x = 'Hello ', 'World'

append(xs, x)
# => 'Hello World'

xs
# => 'Hello '

# invalid
def append(xs, x)
  xs += x
end

xs, x = 'Hello ', 'World'

append(xs, x)
# => 'Hello World'

# as you can see, xs wasn't modified, since Ruby is pass-by-value:
xs
# => 'Hello '

可以做的事情当然是改变对象:

def foo(bar)
  bar.replace('Hah! Of course you will!')
end

baz = "I won't change."

foo(baz)

baz
# => 'Hah! Of course you will!'

请注意,这完全等同于你的常数类比:

Baz = "I won't change"

Baz = 'something else'
# warning: already initialized constant Baz

但:

Baz = "I won't change"

Baz.replace('Hah! Of course you will!')
# no warning

Baz
# => 'Hah! Of course you will!'

作为来电者,您可以执行以下两项操作之一:

  1. Copy您传入的对象,以便保留未经修改的副本,在这种情况下,该方法可以做任何想做的事情,而不会伤害您。

    foo(bar.clone)
    
  2. freeze您传入的对象,以便该方法无法对其进行修改(但如果需要,可能会失败)。

    foo(bar.freeze)
    
  3. 作为方法 author ,无法与调用者或您不打算改变参数的语言进行通信。但请注意,改变参数被认为是非常糟糕的样式,实际上,我无法在核心库,stdlib,我遇到的任何代码或我编写的任何代码中命名单个方法,即使是初学者

    所以,一般来说,你可以假设如果文档没有提到它,那么方法不会改变参数,相反,如果方法做了变异这些论点在文档中会有一个很大的警告。

    请注意,使用语言检查您不会修改参数根本不可能。你只是在调用方法。语言如何知道哪些方法修改了对象以及哪些方法没有?它必须弄清楚一个方法是否可以对对象的内部状态产生任何形式的外部可观察的副作用。然而,这相当于解决Halting Problem,因此不可能。