为什么Ruby中的双铲不会改变状态?

时间:2016-11-29 23:25:57

标签: ruby string

我遇到了这种引起错误或混乱的奇怪副作用。所以想象一下,这不是一个简单的例子,而是一个 gotcha 的例子。

name = "Zorg"

def say_hello(name)
  greeting = "Hi there, " << name << "?"
  puts greeting
end

say_hello(name)
puts name

# Hi there, Zorg?
# Zorg

这不会改变名称。姓名仍为Zorg

但现在看一个非常微妙的区别。在下一个例子中:

name = "Zorg"

def say_hello(name)
  greeting = name << "?"
  puts "Hi there, #{greeting}"
end

say_hello(name)
puts name

# Hi there, Zorg?
# Zorg?  <-- name has mutated

现在名称为Zorg?。疯。 greeting =任务中的细微差别。 Ruby在内部使用解析(?)或消息传递链接?我认为这只会像name.<<("?")那样将铲子连在一起,但我想这不会发生。

这就是我在尝试连接时避免使用铲子操作符的原因。我一般尽量避免变异状态,但Ruby(目前)尚未针对此进行优化(尚未)。也许Ruby 3会改变一切。很抱歉有关Ruby未来的范围蔓延/侧面讨论。

我认为这特别奇怪,因为副作用较少的例子(第一个)有两个铲子操作员,其中副作用更多的铲子操作员更少。

更新 你是正确的DigitalRoss,我让它太复杂了。 简化示例:

one = "1"
two = "2"
three = "3"
message = one << two << three

现在你认为一切都是什么? (不偷看!) 如果我不得不猜测我会说:

one is 123
two is 23
three is 3
message is 123

但我对两个人的看法不对。二是2。

3 个答案:

答案 0 :(得分:9)

如果我们将你的a << b << c构造转换为更多的方法形式并抛出一堆隐含的括号,那么行为应该更清晰。重写:

greeting = "Hi there, " << name << "?"

的产率:

greeting = ("Hi there, ".<<(name)).<<("?")

String#<<正在修改内容,但name永远不会显示为<<的目标/ LHS,"Hi there ," << name字符串会执行但name不会。如果用变量替换第一个字符串文字:

hi_there = 'Hi there, '
greeting = hi_there << name << '?'
puts hi_there

您会看到<<已更改hi_there;在你的"Hi there, "案例中,这个更改是隐藏的,因为你正在修改一些你以后无法看到的东西(字符串文字)。

答案 1 :(得分:3)

你太复杂了。

操作员返回左侧,因此在第一种情况下,它只是阅读name(因为"Hi there, " << name首先被评估),但在第二个例子中它正在编写它。

现在,许多Ruby运算符 是右关联的,但<<不是其中之一。请参阅:https://stackoverflow.com/a/21060235/140740

答案 2 :(得分:1)

import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); const state = { dataRows: [], activeDataRow: {} }; const mutations = { UPDATE_DATA(state, data) { state.dataRows = data; state.activeDataRow = {}; }, RESET_DATA(state) { state.dataRows = []; state.activeDataRow = {}; } }; export default new Vuex.Store({ state, mutations }); 的右侧从左到右进行评估。

当你做的时候

=

操作从"Hello" << name << "?" 开始,向其添加"Hello",然后将name添加到变异的"?"

当你这样做时

"Hello"

操作以name << "?" 开头,并向其添加name,改变名称(存在于方法的内部范围之外。

因此,在"?"的示例中,您只是在变异one << two << three