【问题标题】:Why doesn't Ruby support i++ or i--​ (increment/decrement operators)?为什么 Ruby 不支持 i++ 或 i--​(递增/递减运算符)?
【发布时间】:2011-04-09 07:14:39
【问题描述】:

前/后递增/递减运算符(++--)是非常标准的编程语言语法(至少对于过程和面向对象的语言)。

为什么 Ruby 不支持它们?我知道你可以用+=-= 完成同样的事情,但是排除这样的事情似乎有点奇怪,特别是因为它是如此简洁和传统。

例子:

i = 0    #=> 0
i += 1   #=> 1
i        #=> 1
i++      #=> expect 2, but as far as I can tell, 
         #=> irb ignores the second + and waits for a second number to add to i

我知道Fixnum 是不可变的,但如果+= 可以实例化一个新的Fixnum 并设置它,为什么不对++ 做同样的事情?

包含= 字符的作业的一致性是唯一原因,还是我遗漏了什么?

【问题讨论】:

  • 此类运算符的 Grep ruby​​ 源代码。如果没有 - Matz 不喜欢他们。
  • 您不能使用 += 运算符进行预增量。在 C 语言中,我尝试仅在条件句中使用 ++/--,而更喜欢在基本语句中使用更字面的 +=/-=。可能是因为我学了 Python(虽然在 C 之后很久......)
  • 昨天不是有这样的 Python 问题吗?
  • @Eimantas 显然该语言的创建者不喜欢他们。这太常见了,不容忽视。我想知道为什么,下面的答案已经澄清了这一点。
  • 我认为这(几乎)是一个典型的 SO 问题。获得深思熟虑的答复并不是一件不容易的事情。需要什么答案非常明确和具体,并且答案揭示了编程的一个方面,可以使人们思考的范围更广,而不仅仅是问题的核心。

标签: ruby operators language-design


【解决方案1】:

这是 Matz(Yukihiro Matsumoto)在旧的 thread 中的解释:

Hi,

In message "[ruby-talk:02706] X++?"
    on 00/05/10, Aleksi Niemelä <aleksi.niemela@cinnober.com> writes:

|I got an idea from http://www.pragprog.com:8080/rubyfaq/rubyfaq-5.html#ss5.3
|and thought to try. I didn't manage to make "auto(in|de)crement" working so
|could somebody help here? Does this contain some errors or is the idea
|wrong?

  (1) ++ and -- are NOT reserved operator in Ruby.

  (2) C's increment/decrement operators are in fact hidden assignment.
      They affect variables, not objects.  You cannot accomplish
      assignment via method.  Ruby uses +=/-= operator instead.

  (3) self cannot be a target of assignment.  In addition, altering
      the value of integer 1 might cause severe confusion throughout
      the program.

                            matz.

【讨论】:

  • 2 和 3 似乎矛盾。如果自我分配不好,为什么+=/-= ok? 1+=1 不会同样糟糕吗? (它在 IRB 中失败,syntax error, unexpected ASSIGNMENT
  • (2) 意味着在 C 中,您不会更改值本身……您正在更改保存该值的变量的内容。对于任何按值传递的语言来说,这有点太元了。除非有办法在 Ruby 中通过引用传递某些东西(我的意思是真正的“通过引用”,而不是通过值传递引用),否则在方法中改变变量本身是不可能的。
  • 也许我在这里遗漏了一些东西。 += 将变量引用的对象替换为一个全新的对象。您可以通过在i+=1 之前和之后调用i.object_id 来检查这一点。为什么使用++ 会在技术上更加棘手?
  • @Andy_Vulhop:#3 解释了为什么在技术上不可能将分配作为一种方法,而不是为什么分配通常是不可能的(发帖人 Matz 回复认为可能创建一个 @987654331 @方法)。
  • 在 Ruby 中,所有文字也是对象。因此,我相信 Matz 试图说他不确定他是否喜欢将 1++ 作为陈述来处理的想法。我个人认为这是不合理的,因为正如@Andy_Vulhop 所说 1+=2 一样古怪,而当你这样做时,Ruby 只会引发错误。所以 1++ 并不难处理。可能解析器需要处理这种语法糖是不可取的。
【解决方案2】:

一个原因是到目前为止,每个赋值运算符(即更改变量的运算符)中都有一个=。如果添加++--,则不再是这种情况。

另一个原因是++-- 的行为经常让人迷惑。恰当的例子:在您的示例中,i++ 的返回值实际上是 1,而不是 2(不过,i 的新值是 2)。

【讨论】:

  • 到目前为止,比任何其他原因更重要的是,“所有作业中都有一个=”的理性似乎是有道理的。我可以尊重它作为对一致性的强烈坚持。
  • 这个怎么样:a.capitalize! (a的隐式赋值)
  • @LuísSoares a.capitalize! 不会重新分配a,它会改变a 所指的字符串。对同一字符串的其他引用将受到影响,如果您在调用capitalize 之前和之后执行a.object_id,您将得到相同的结果(如果您改为使用a = a.capitalize,则两者都不是真的)。​​
  • @LuísSoares 正如我所说,a.capitalize! 会影响对同一字符串的其他引用。这是一个非常实际的区别。例如,如果你有def yell_at(name) name.capitalize!; puts "HEY, #{name}!" end,然后你像这样调用它:my_name = "luis"; yell_at(my_name)my_name 的值现在将是"LUIS",而如果你使用了capitalize 和赋值,它不会受到影响。
  • 哇。这太可怕了……知道在 Java 中字符串是不可变的……但是权力伴随着责任。谢谢你的解释。
【解决方案3】:

这在 OO 语言中不是传统的。事实上,Smalltalk 中没有++,这是一种创造术语“面向对象编程”的语言(Ruby 最受其影响的语言)。您的意思是它在 C 和密切模仿 C 的语言中是传统的。Ruby 确实有一些类似 C 的语法,但它在坚持 C 传统方面并不盲目。

至于为什么它不在 Ruby 中:Matz 不想要它。这才是真正的终极原因。

Smalltalk 中不存在这样的事情的原因是因为它是语言最重要哲学的一部分,即分配变量与向对象发送消息从根本上是不同的种类——它是在不同的等级。这种想法可能影响了 Matz 设计 Ruby。

将它包含在 Ruby 中并非不可能——您可以轻松编写一个预处理器,将所有 ++ 转换为 +=1。但显然,Matz 不喜欢操​​作员执行“隐藏分配”的想法。有一个内部带有隐藏整数操作数的运算符似乎也有点奇怪。该语言中没有其他运算符可以这样工作。

【讨论】:

  • 我认为你的预处理器建议行不通; (不是专家)但我认为 i= 42,i++ 将返回 42,而 i+=1 将返回 43。我在这方面不正确吗?因此,在这种情况下,您的建议是使用 i++,因为通常使用 ++i,这很糟糕,恕我直言,弊大于利。
【解决方案4】:

我认为还有另一个原因:Ruby 中的++ 不会像在 C 及其直接继承者中那样有用。

原因在于,for 关键字:虽然它在 C 中是必不可少的,但在 Ruby 中却是多余的。 Ruby 中的大部分迭代都是通过 Enumerable 方法完成的,例如 eachmap 在迭代某些数据结构时,以及 Fixnum#times 方法,当您需要循环准确次数时。

实际上,据我所见,+=1 大部分时间都被刚从 C 风格语言迁移到 Ruby 的人使用。

简而言之,是否会使用 ++-- 方法确实值得怀疑。

【讨论】:

  • 这是最好的答案恕我直言。 ++ 通常用于迭代。 Ruby 不鼓励这种类型的迭代。
【解决方案5】:

我认为 Matz 不喜欢它们的原因是它实际上用新变量替换了变量。

例如:

a = SomeClass.new def a.go '你好' 结尾 # 此时可以调用a.go # 但是如果你做了 a++ # 这实际上意味着 a = a + 1 # 所以你不能再调用 a.go # 因为你丢失了你的原件

现在,如果有人可以说服他应该调用#succ!或者什么不是,这会更有意义,并避免这个问题。你可以在 ruby​​ core 上推荐它。

【讨论】:

  • "你可以在 ruby​​ 内核上推荐它" ... 你已经阅读并且理解了它所在的所有其他线程中的参数上次建议的,之前的,之前的,之前的,之前的,之前的,还有...我记得至少有二十次这样的讨论。
【解决方案6】:

你可以定义一个.+自增运算符:

class Variable
  def initialize value = nil
    @value = value
  end
  attr_accessor :value
  def method_missing *args, &blk
    @value.send(*args, &blk)
  end
  def to_s
    @value.to_s
  end

  # pre-increment ".+" when x not present
  def +(x = nil)
    x ? @value + x : @value += 1
  end
  def -(x = nil)
    x ? @value - x : @value -= 1
  end
end

i = Variable.new 5
puts i                #=> 5

# normal use of +
puts i + 4            #=> 9
puts i                #=> 5

# incrementing
puts i.+              #=> 6
puts i                #=> 6

有关“类变量”的更多信息,请参见“Class Variable to increment Fixnum objects”。

【讨论】:

  • 请注意,虽然这只是因为您更改了内部变量@value 的值,但实际上您根本没有更改i 的值。除了递增和递减运算符之外,Variable 作为Fixnum 的替代品并不是很有用(正如@sony-santos 在链接帖子中指出的那样)。
【解决方案7】:

用大卫·布莱克 (David Black) 在他的书“The Well-Grounded Rubyist”中的话来说:

Ruby 中的一些对象作为立即值存储在变量中。这些包括 整数、符号(看起来像 :this)和特殊对象 true、false 和 零。当您将这些值之一分配给变量 (x = 1) 时,该变量保持 值本身,而不是对它的引用。 实际上,这并不重要(而且它通常会被隐含,而不是 在本书中讨论参考资料和相关主题时反复说明)。 Ruby 自动处理对象引用的取消引用;你不必 做任何额外的工作来向一个对象发送一条消息,该对象包含一个引用 一个字符串,而不是一个包含立即整数值的对象。 但是立即值表示规则有几个有趣的后果, 特别是在涉及整数时。一方面,任何表示的对象 因为立即数总是完全相同的对象,无论有多少 它分配给的变量。只有一个对象 100,只有一个对象是假的,并且 很快。 整数绑定变量的直接、独特性是 Ruby 缺乏 前置和后置操作符——也就是说,你不能在 Ruby 中这样做: x = 1 x++ # 没有这样的操作符 原因是由于 x 中立即存在 1,x++ 会像 1++, 这意味着您要将数字 1 更改为数字 2——这使得 没有意义。

【讨论】:

  • 那你怎么能做到“1.next”呢?
【解决方案8】:

Ruby 中的一些对象作为立即值存储在变量中。这些包括整数、符号(看起来像:this)和特殊对象true、false和nil。当您将这些值之一分配给变量 (x = 1) 时,该变量将保存该值本身,而不是对其的引用。

任何表示为立即值的对象始终是完全相同的对象,无论它分配了多少变量。只有一个对象 100,只有一个对象 false,以此类推。

整数绑定变量的直接、独特性是 Ruby 缺少前后自增运算符的原因——也就是说,您不能在 Ruby 中这样做:

x=1

x++ # 没有这样的操作符

原因是由于 x 中立即存在 1,x++ 会像 1++,这意味着您要将数字 1 更改为数字 2——这是没有意义的。

【讨论】:

    【解决方案9】:

    难道不能通过向 fixnum 或 Integer 类添加新方法来实现吗?

    $ ruby -e 'numb=1;puts numb.next'
    

    返回 2

    “破坏性”方法似乎附加了! 以警告可能的用户,因此添加一个名为next! 的新方法几乎可以满足要求,即。

    $ ruby -e 'numb=1; numb.next!; puts numb' 
    

    返回 2(因为 numb 已递增)

    当然,next! 方法必须检查对象是整数变量而不是实数,但是这个应该可用。

    【讨论】:

    • Integer#next 已经存在(或多或少),除了它被称为 Integer#succ (对于“继任者”)。但是Integer#next!(或Integer#succ!)是无稽之谈:记住方法适用于对象,而不是变量,因此numb.next! 将完全等于1.next! ,也就是说,它将将 1 变为等于 2++ 会稍微好一点,因为它可能是赋值的语法糖,但我个人更喜欢当前语法,所有赋值都使用 = 完成。
    • 完成上面的评论:和Integer#pred检索前任。
    【解决方案10】:

    在 Ruby 的 irb 中检查这些来自 C 系列的运算符并自己测试它们:

    x = 2    # x is 2
    x += 2   # x is 4
    x++      # x is now 8
    ++x      # x reverse to 4
    

    【讨论】:

    • 这显然是错误的并且不起作用,因为(x++) 在 Ruby 中是无效语句。
    猜你喜欢
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    • 2018-09-06
    • 2014-02-22
    • 2021-08-18
    • 1970-01-01
    • 2023-01-05
    相关资源
    最近更新 更多