Rubyがi ++またはi--(インクリメント/デクリメント演算子)をサポートしないのはなぜですか?


130

前置/後置インクリメント/デクリメント演算子(++および--)は、かなり標準的なプログラミング言語構文です(少なくとも、手続き型言語とオブジェクト指向言語では)。

Rubyがそれらをサポートしないのはなぜですか?+=and -=で同じことを達成できると思いますが、特にそのように簡潔で慣習的であるため、そのようなものを除外することは奇妙に恣意的に思えます。

例:

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設定できるのであれば、同じことをしてみません++か?

=文字を含む割り当ての一貫性がこれの唯一の理由ですか、それとも何か不足していますか?


2
このような演算子のGrep ruby​​ソースコード。ない場合-マッツはそれらを好きではありません。
Eimantas

+=演算子でプリインクリメントすることはできません。CI では、基本的なステートメントでよりリテラルの/ を優先して、条件文の内部でのみ++/ を使用してみてください。たぶん私はPythonを習ったから(Cの後ずっと...)--+=-=
Nick T

昨日のPythonでこのような質問はありませんでしたか?
BoltClock

@Eimantasは明らかに言語の作成者が好きではありませんでした。見落とすことはあまりにも一般的です。WHYについて疑問に思っていました。これは、以下の回答でいくらか明らかにされています。
Andy_Vulhop

1
これは(ほぼ)モデルSOの質問だと思います。検討された返信を得ることは、簡単にグーグルに対応できるものではありません。どの回答が必要かは非常に明確で具体的であり、その回答は、質問の核心だけではなく、より広く考えることができるプログラミングの側面を明らかにします。
PurplePilot 2010

回答:


97

Matz(Yukihiro Matsumoto)が古いスレッドでそれを説明する方法は次のとおりです:

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.

10
2と3は矛盾しているようです。自己割り当てが悪い場合、なぜ+=/ -=大丈夫ですか?そして、それほど1+=1悪くはないでしょうか?(これはIRBで失敗しますsyntax error, unexpected ASSIGNMENT
Andy_Vulhop

2
(2)は、Cでは値自体を変更するのではなく、値を保持する変数の内容を変更することを意味します。これは、値で渡される言語にとっては少しメタすぎるものです。Rubyで参照によって何かを渡す方法がない場合(つまり、値による参照を渡さないで、本当に「参照によって」を意味する場合)を除き、メソッド内で変数自体を変更することはできません。
cHao

5
たぶんここで何かが足りない。+=変数が参照するオブジェクトをまったく新しいオブジェクトに置き換えます。これを確認するには、i.object_idbeforeとafterを呼び出しi+=1ます。なぜそれが技術的に難しいの++でしょうか?
Andy_Vulhop

6
@Andy_Vulhop:#3は、割り当てがメソッドであることが技術的に不可能である理由を説明しています。割り当てが一般的に不可能である理由ではありません(ポスターMatzは、++メソッドを作成することが可能であると考えて返信しました)。
Chuck

2
Rubyでは、すべてのリテラルもオブジェクトです。だからマッツは彼が1 ++をステートメントとして扱うという考えが好きかどうかわからないと言っていると思います。個人的には、@ Andy_Vulhopが言うように1 + = 2も同様に奇妙であり、Rubyはこれを実行するとエラーを発生させるので、不合理だと思います。したがって、1 ++の処理は難しくありません。おそらく、そのような構文上の砂糖に対処するパーサーの必要性は望ましくありません。
Steve Midgley 2014

28

1つの理由は、これまではすべての代入演算子(つまり、変数を変更する演算子)にa =があるためです。++and を追加すると--、それは当てはまりません。

もう1つの理由は、人々の行動を混乱させ++--しばしば混乱させることです。適例:このi++例のの戻り値は実際には2ではなく1です(iただし、の新しい値は2 になります)。


4
これまでの他のどの理由よりも、「すべての割り当てにaがある」という合理的な説明=は理にかなっているようです。私はそれを一貫性への激しい固執として尊敬することができます。
Andy_Vulhop

これはどうですか:a.capitalize!(暗黙の割り当て)
ルイスソアレス

1
@LuísSoares a.capitalize!はを再割り当てせずaa参照する文字列を変更します。同じ文字列への他の参照が影響を受けa.object_id、の呼び出しの前後にcapitalize同じ結果が得られます(a = a.capitalize代わりにそうした場合、どちらも当てはまりません)。
sepp2k 2016

1
@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と割り当てを使用した場合は影響を受けません。
sepp2k 2016

1
ワオ。それは怖いです... Javaの文字列は不変であることを知っています。しかし、力には責任が伴います。説明ありがとう。
ルイスソアレス2016

25

これはオブジェクト指向言語では一般的ではありません。実際、++Smalltalkには、「オブジェクト指向プログラミング」という用語を作り出した言語はありません(Rubyは最も強く影響を受けています)。つまり、これはCCによく似た言語では一般的です。RubyはややCに似た構文を持っていますが、Cの伝統に固執することは怠惰ではありません。

Rubyにない理由については、Matzはそれを望んでいませんでした。それが本当に究極の理由です。

Smalltalkにそのようなものが存在しない理由は、変数を割り当てることは基本的に異なる種類であるという言語のオーバーライド哲学の一部であるためですオブジェクトにメッセージを送信することのものです。それは異なるレベルにあります。この考え方は、おそらくRubyの設計においてMatzに影響を与えました。

Rubyでそれを含めることは不可能ではないでしょう-あなたは簡単にその変換すべてのプリプロセッサを書くことができます+++=1。しかし、明らかにマッツは「隠された割り当て」をするオペレーターの考えを好まなかった。内部に隠し整数オペランドを持つ演算子があるのも少し奇妙に思われます。言語の他の演算子はそのように機能しません。


1
あなたのプリプロセッサの提案がうまくいくとは思いません。(エキスパートではありません)しかし、i = 42と思います。i++は42を返し、i + = 1は43を返します。これは間違っていますか?したがって、その場合の提案は、通常は++ iが使用されるため、i ++を使用することです。
AturSams 2014

12

もう1つの理由があると思い++ます。Rubyは、Cやその直接の後継者とは異なり、リモートでは役に立ちません。

その理由は、forキーワードです。Cでは必須ですが、Rubyではほとんど不要です。Rubyでの繰り返しのほとんどは、次のような列挙方法、を介して行われるeachmap、いくつかのデータ構造を反復処理する場合、およびFixnum#times、あなたがループする回数の正確な数を必要とするとき、方法。

実際、私が見た限りでは、ほとんどの場合、+=1Cスタイルの言語からRubyに移行したばかりの人が使用しています。

要するに、メソッド++--がまったく使用されるかどうかは本当に疑わしいです。


1
これがベストな答えです。++は反復によく使用されます。Rubyはこのタイプの反復を推奨していません。
AturSams 2014

3

それらを好まないマッツの理由は、それが実際に変数を新しいものに置き換えるということだと思います。

例:

a = SomeClass.new
def a.go
  'こんにちは'
終わり
#この時点で、a.goを呼び出すことができます
#ただし、a ++を実行した場合
#それは本当にa = a + 1を意味します
#これで、a.goを呼び出せなくなりました
#オリジナルを失ったので

誰かが彼にそれをただ#succと呼ぶべきだと納得させたら!そうでない場合、それはより理にかなっており、問題を回避します。あなたはルビーコアでそれを提案することができます。


9
「Rubyコアで提案できます」... 前回提案された他のすべてのスレッドの引数、およびその前の時間とその前の時間とその前の時間を読ん理解した、そして、その前の時間、そして...私はRubyコミュニティにあまり長くいませんでしたが、ちょうど私の時間の間に、私はそのような議論を少なくとも20回覚えています。
イェルクWミッターク

3

.+自己増分演算子を定義できます。

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

「クラス変数」の詳細については、「Fixnumオブジェクトをインクリメントするクラス変数」を参照してください。


2

そして、彼の本「The Well-Grounded Rubyist」からのDavid Blackの言葉で:

Rubyの一部のオブジェクトは、即値として変数に格納されます。これらには、整数、記号(:thisのように見える)、および特殊オブジェクトtrue、false、nilが含まれます。これらの値の1つを変数(x = 1)に割り当てると、変数は値への参照ではなく値自体を保持します。実際には、これは重要ではありません(この本の参考文献や関連トピックの説明では、何度も説明するのではなく、暗黙的に示されていることがよくあります)。Rubyはオブジェクト参照の逆参照を自動的に処理します。即時整数値を含むオブジェクトとは対照的に、たとえば文字列への参照を含むオブジェクトにメッセージを送信するために追加の作業を行う必要はありません。しかし、即時値表現ルールには、いくつかの興味深い影響があります。特に整数に関しては。1つには、即値として表されるオブジェクトは、割り当てられている変数の数に関係なく、常にまったく同じオブジェクトです。オブジェクト100は1つだけ、偽オブジェクトは1つだけというように続きます。整数バインド変数の即時の固有の性質は、Rubyにはインクリメント前後の演算子がないためです。つまり、Rubyではこれを実行できません。x = 1 x ++#そのような演算子はありませんxに1が直接存在する場合、x ++は1 ++のようになります。つまり、数値1を数値2に変更することになります。これは意味がありません。変数がいくつ割り当てられていても。オブジェクト100は1つだけ、偽オブジェクトは1つだけというように続きます。整数バインド変数の即時の固有の性質は、Rubyにはインクリメント前後の演算子がないためです。つまり、Rubyではこれを実行できません。x = 1 x ++#そのような演算子はありませんxに1が直接存在する場合、x ++は1 ++のようになります。つまり、数値1を数値2に変更することになります。これは意味がありません。変数がいくつ割り当てられていても。オブジェクト100は1つだけ、偽オブジェクトは1つだけというように続きます。整数バインド変数の即時の固有の性質は、Rubyにはインクリメント前後の演算子がないためです。つまり、Rubyではこれを実行できません。x = 1 x ++#そのような演算子はありませんxに1が直接存在する場合、x ++は1 ++のようになります。つまり、数値1を数値2に変更することになります。これは意味がありません。


しかし、なぜ「1.next」を実行できるのでしょうか。
Magne

1

これは、fixnumクラスまたはIntegerクラスに新しいメソッドを追加することで実現できませんか?

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

2を返します

「破壊的」メソッドは、!可能性のあるユーザーに警告するために追加されているようです。そのため、呼び出される新しいメソッドを追加すると、next!要求された機能がほとんど実行されます。

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

2を返します(numbがインクリメントされているため)

もちろん、next!メソッドはオブジェクトが実数ではなく整変数であることをチェックする必要がありますが、これ利用可能であるべきです。


1
Integer#next(多かれ少なかれ)既に存在しますが、Integer#succ代わりに呼び出されます(「後継者」の場合)。しかしInteger#next!(またはInteger#succ!)はナンセンスです。メソッドは変数ではなくオブジェクトで機能するため、と完全に等しくなることを覚えておいてください。つまり、1を変更して2と等しくします。割り当ての構文上の砂糖になる可能性があるため、わずかに優れていますが、個人的には、すべての割り当てがで行われる現在の構文を好みます。numb.next!1.next!++=
哲学2016

上記のコメントを完成させ、Integer#pred前任者を検索する。
ヨニ

-6

RubyのirbでCファミリのこれらの演算子を確認し、自分でテストします。

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

3
これは明らかに間違っており(x++)、Rubyの無効なステートメントと同様に機能しません。
anothermh 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.