+ =表示整数/字符串与<<对于阵列?

时间:2013-04-05 12:18:03

标签: ruby syntactic-sugar

我对在Ruby中对整数,字符串和数组执行简单的加法/连接时得到的不同结果感到困惑。我的印象是,当将变量b分配给a(见下文),然后更改a的值时,b将保持不变。它在前两个例子中也这样做了。但是当我在第三个例子中修改数组a时,a和b都被修改了。

a = 100
b = a
a+= 5
puts a
puts b

a = 'abcd'
b = a
a += 'e'
puts a
puts b

a = [1,2,3,4]
b = a
a << 5
puts a.inspect
puts b.inspect

以下是在终端中为上述代码返回的内容:

Ricks-MacBook-Pro:programs rickthomas$ ruby variablework.rb
105
100
abcde
abcd
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
Ricks-MacBook-Pro:programs rickthomas$ 

我的编程讲师给了我以下解释:

  

为新变量分配内容只是给它一个额外的标签,它不会复制。

     

看起来+ =是一个方法,就像&lt;&lt;,所以你希望它的行为类似。但实际上,它是“语法糖”,在语言中增加了一些东西,使开发人员更容易。

     

当你运行+ = 1时,Ruby会将其转换为a = a + 1。

     

在这种情况下,我们不会修改一个Fixnum。相反,我们实际上是在它之上重新分配,有效地吹走了之前的值。

     

另一方面,当你运行b&lt;&lt; “c”,您正在修改基础数组,方法是将字符串“c”附加到它。

我的问题是这些:

1)他提到了句法糖,但不是那个&lt;&lt;是,即.push方法的语法糖?

2)如果+ =是语法糖还是更正式的方法,为什么会这么重要?如果两者之间存在一些差异,那么这并不意味着我之前理解的语法糖(“编程语言中的语法旨在使事情更容易阅读或表达”)是不完整的,因为这不是'它的唯一目的是什么?

3)如果将b分配给a并不复制a,那么为什么不擦除旧值意味着b的旧值也会被删除所有3种情况(整数,字符串和数组)?

正如你所看到的那样,我对于我认为直到现在才明白的东西已经转过身了。非常感谢任何帮助!

7 个答案:

答案 0 :(得分:4)

您看,名称(变量名称,如ab)本身不包含任何值。他们只是指向一个值。当你做作业时

a = 5

然后a现在指向值5,无论它之前指向什么。这很重要。

a = 'abcd'
b = a

此处ab都指向相同的字符串。但是,当你这样做时

a += 'e'

它实际上已翻译为

a = a + 'e'
# a = 'abcd' + 'e'

因此,名称a现在绑定到一个新值,而b则指向“abcd”。

a = [1,2,3,4]
b = a
a << 5

此处没有赋值,方法<<修改现有数组而不替换它。由于没有替换,ab仍然指向同一个数组,并且可以看到对另一个进行的更改。

答案 1 :(得分:3)

你问题的答案1)和2):

+= syntactic sugar<<的原因不是很简单:+=抽象出一些句法表达式:a += 1只是一个简短的表达式版本a = a + 1<<是一种单独的方法,push的别名:<<只能接受一个参数,而push可以采用任意数量的参数:我在这里使用send来证明这一点,因为[1,2]<<(1,2)在语法上是不正确的:

[1,2].send(:<<, 4, 5) #=> ArgumentError: wrong number of arguments (2 for 1)

push将所有参数追加到数组中:

[1,2].push(4,5,6) #=> [1,2,4,5,6]

因此,<<是ruby数组中不可替代的部分,因为没有等效的方法。有人可能会认为push某种语法糖,忽略了上面显示的差异,因为它使大多数操作涉及将数组附加到数组更简单且语法更容易识别。

如果我们深入了解整个ruby中<<的不同用法:

  • 将元素推送到数组:

    [1,2] << 5 
    
  • 将字符串连接到另一个字符串,<<实际上是concat的别名

    "hello " << "world"
    
  • 打开单例类并在类上定义方法:

    class Foo
      class << self
        def bar
          puts 'baz'
        end
      end
    end
    
  • 最后但并非最不重要的是将self附加到整数中的self

    1 << 2 #translates to ((1 + 1) + (1 + 1))
    

我们可以看到<<实际上代表整个ruby中的追加,因为它总是出现在某个内容中,其中某些内容会附加到已存在的内容中。因此,我宁愿认为<<是ruby语法的重要组成部分,而不是语法糖。

答案3)

如果您使用b运算符,+=的分配未被修改(或擦除其旧值,如您所说)的原因仅仅是a += 1a = a + 1的缩写,重新分配a的值,因此会分配一个新对象。 <<正在修改原始对象。您可以使用object_id

轻松查看此内容
a = 1
b = a
b.object_id == a.object_id #=> true

a += 1
b.object_id == a.object_id #=> false

a = [1,2]
b = a
b.object_id == a.object_id #=> true
a << 3
b.object_id == a.object_id #=> true

Integer个实例(100101)也有一些警告,等等:相同的数字始终是同一个对象,因为它没有任何意义多个实例,例如100

a = 100
b = a
b.object_id == a.object_id #=> true
a += 1
b.object_id == a.object_id #=> false
a -= 1
b.object_id == a.object_id #=> true

这也表明该值或Integer实例(100)只是分配到变量,因此变量本身不是一个对象,它只是指向它。

答案 2 :(得分:1)

  
    

String#+ :: str + other_str → new_str连接 - 返回包含other_str连接到str的新String。

         

String#<< :: str << integer → str:追加 - 将给定对象连接到str

  

<<不会创建新对象,而+会这样做。

样本1:

a = 100
p a.object_id
b = a
p b.object_id
a+= 5
p a.object_id
p b.object_id
puts a
puts b

输出:

201
201
211
201
105
100

答案 3 :(得分:1)

你的例子:

a = 100
b = a
a+= 5

相当于:

a = 100
b = a
a = 100 + 5

之后a保留对105的引用,b仍然引用100。这就是赋值在Ruby中的工作方式。

您希望+=更改对象实例100。但是在Ruby中(引用docs):

  

对于任何给定的整数值,实际上只有一个Fixnum对象实例

因此100只有一个对象实例,105只有一个(但总是相同)。将100更改为105会将所有 100更改为105。因此,无法在Ruby中修改这些实例,它们是已修复

另一方面可以修改String实例,与Integer不同,对于相同的字节序列,可以有多个实例:

a = "abcd"
b = "abcd"
a.equal? b # returns true only if a and b are the same object
# => false

a << "e""e"连接到a,从而更改了接收者:a仍在引用相同的对象实例。

a += "e"等其他方法返回(并分配) new Stringa之后会引用此新实例。

documentation非常明确:

  

str + other_str→new_str

     

连接 - 返回一个新的String,其other_str连接到str

     

str&lt;&lt; obj→str

     

追加 - 将给定对象连接到str

答案 4 :(得分:1)

我可以回答你的问题。

1)不,<<方法不是push的语法糖。它们都是具有不同名称的方法。您可以在Ruby中使用定义一个而不是另一个的对象(例如String)。

2)对于像<<这样的常规方法, a << x导致唯一可能发生的事情是{{1}指向被修改。语句aa << x无法创建新对象,并将变量a.push(x)更改为指向它。这就是Ruby的工作方式。这种事情被称为“调用方法”。

a语法糖很重要的原因是它可以用来修改变量而不会改变变量用来指向的旧对象。考虑+=。该声明可以修改指向的对象,因为它是实际赋值a += x的语法糖:

a

上面发生了两件事。首先,在a = a + x 上调用+方法,其中一个参数为a。然后,x方法的返回值,无论它是什么,都被赋值给变量+

3)您的Array情况不同的原因是您选择改变数组而不是创建新数组。您可以使用a来避免变异数组。我认为这六个示例将为您清理并向您展示Ruby中的可能性:

没有突变的字符串

+=

具有突变的字符串

a = "xy"
b = a
a += "z"
p a  # => "xyz"
p b  # => "xy"

无突变的阵列

a = "xy"
b = a
a << "z"
p a  # => "xyz"
p b  # => "xyz"

具有突变的阵列

a = [1, 2, 3]
b = a
a += [4]
p a  # => [1, 2, 3, 4]
p b  # => [1, 2, 3]

没有突变的整数

a = [1, 2, 3]
b = a
a.concat [4]
p a  # => [1, 2, 3, 4]
p b  # => [1, 2, 3, 4]

具有突变的整数

在Ruby中实际上不可能改变整数。写a = 100 b = a a += 5 puts a # => 105 puts b # => 100 实际上确实创建了数字89的两个副本,并且该数字不能永远变异。只有少数特殊类型的对象表现得像这样。

结论

您应该将变量视为名称,将对象视为无名数据。

Ruby中的所有对象都可以以不可变的方式使用,您永远不会实际修改对象的内容。如果您这样做,那么您不必担心我们的示例中的a = b = 89变量会自行更改; b将始终指向同一个对象,该对象永远不会更改。变量b只会在您执行某种形式的b时更改。

Ruby中的大多数对象都可以进行变异。如果有几个变量引用同一个对象并且您选择改变对象(例如通过调用b = x),则该更改将影响指向该对象的所有变量。你不能改变符号和整数。

答案 5 :(得分:0)

我猜上面的答案解释了原因。另请注意,如果您想确保b没有指针,则可以使用b = a.dup代替b=a(复制重复)

答案 6 :(得分:0)

我会尽力回答你的问题。

是的,两者都是“糖”,但它们的工作方式不同,正如Sergio Tulentsev所说,<<它不是真正的糖,但它是别名。

这些作为Unix中的别名就像语言一样,它是用你喜欢的名字命名的简短速记。

所以对于第一个场景:+=基本上正在发生的事情是你说的:

for the value 100 assign label 'a'.
for label 'b' assign the value of label 'a'.
for label 'a' take the value of label 'a' and add 5 to label 'a's value and return a new value
print label 'a' #this now holds the value 105
print label 'b' #this now holds the value 100

在Ruby的引擎下,这与+=在发生这种情况时返回一个新字符串有关。

对于第二种情况:<<它说:

for value [1,2,3,4] assign label 'a'  
for label 'b' assign the value of label 'a'
for label 'a' do the '<<' thing on the value of label 'a'.
print label 'a'
print label 'b'

如果您将<<应用于字符串,它将修改现有对象并附加到该字符串。

那有什么不同。差异在于<<糖的行为不是这样的:

a is the new value of a + 5

它的行为如下:

5 into the value of 'a'

2)因为在这种情况下你使用语法糖的方式是让它更容易 开发人员阅读并理解代码。这是一个简写。

嗯,矮人,如果你打电话给他们,那就是为了不同的目的。 句法糖不是同质的,即。它对所有“糖”都不起作用。

3)擦拭值:

就是这样。

put value 100 into the label 'a'
put the value of label 'a' into label 'b'
remove label 'a' from the value.

所以

a = 100
b = a
a = nil
puts a
puts b
=> 100

Ruby中的变量不包含指向值的值!