質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.52%

  • Java

    13776questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Javaの評価のしくみ

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 320

nom_0124

score 15

int i = 2;
System.out.println(i * i + ++i);

実行結果
7

上記の実行結果になる理由を教えてください。
「i * i」が評価されて「4」に置き換わり、そのあと「++i」が評価されて「3」に置き換わったから(この時点で「4 + 3」)でしょうか?
「i * i」のふたつの「i」が評価されて「2」に置き換わり、そのあと「++i」が評価されて「3」に置き換わったから(この時点で「2 * 2 + 3」)でしょうか?
あるいは、ほかの理由があるのでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+8

仕様は

「i * i」が評価されて「4」に置き換わり、そのあと「++i」が評価されて「3」に置き換わったから(この時点で「4 + 3」)

ということになります。

厳密な仕様は言語仕様の15.7(java10)に書いてあります。

https://docs.oracle.com/javase/specs/jls/se10/html/jls-15.html#jls-15.7

大雑把に言えば

  • 概ね左から右
  • 演算子の計算順序は優先順位によって決まる
  • 演算子の左側のオペランドが先に評価される

よってa*b+cの場合、cよりも「a、bおよびa*bが先に計算される」と決まっています。

・・・が、言語仕様に「計算順序に依存しないようなコードの方がよくて、特に副作用は多くても一つの式で一か所だけの方がよい」とあるように、あまりこの仕様に左右されないような書き方を心がけるのが一番よいと思います。


追記:用語を用いてしゃべるとどうしても意志が伝わっているかどうかわからなくなります。自分が日本語下手なせいなのだと思います。そこで自分が頭の中にイメージしていることを図で表現してみました。

a + b + c * d

最初に優先度により次のようにグルーピングされる(グルーピングしなくてもいいけど、とりあえずしてみます)

a + b + (c * d)

次に演算子+が左結合なのと、左オペランドが先に評価されることを整理すると

a
    b
  +
        (c * d)
      +

こういうイメージになる。上の方が先に評価すべきもの。
で、グルーピングした乗算式も左オペランド優先の規則に従って書けば括弧を記述する必要がなくなり

a
.   b
  +  
  .     c
  .     .   d
  .       *
  .       .
      + 

a + b + ++ cも同様にすると

a + b + (++c)

==>

a
.   b
  +
  .         c
  .      ++ .
  .        .
      +

こんなふうに捉えています。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/13 05:30

    「*」演算子よりも「++」演算子のほうが優先順位は高いと思いますが、この場合、計算順序に影響しないのでしょうか?

    キャンセル

  • 2018/04/13 08:20

    演算子の計算順序は優先順位と結合規則で決まります。「+」演算子は左結合のため、左側の「i * i」が先に評価されます。

    キャンセル

  • 2018/04/13 11:33

    計算順序は優先順位によって決まるというのは大雑把すぎる説明でしたね。hichonさんコメントにある結合順位が演算子の優先順位によって決まります。この例では+より++の方が優先順位が高く+より*が優先順位が高いので

    x * y + ++z => (x * y) + (++z)

    こう結合されます。計算の順番は
    (1)+の左から計算=>(x * y)が(++z)より先
    (2)*の左から計算=>xがyより先
    (3)(++z)を計算
    (4)最後に(x * y)と(++z)の結果を加算
    そういうふうに考えます。このコメントも大雑把ですが...

    キャンセル

  • 2018/04/13 14:40

    例えば

    int i = 2;
    System.out.println(i + i + i * i);

    このソースコード中の「i + i + i * i」という式においても、「i + i」の部分から評価される(4に置き換わる)ということでしょうか?

    キャンセル

  • 2018/04/13 14:42

    そうなります。

    キャンセル

  • 2018/04/13 14:51

    その例だと先頭のi+iが先に評価されるのかi*iがそれより先に評価されるかわかりにくいと思います。javapでbyte codeを見ても確認できますが、例えば

    int i=1;
    int j=i/0/f();
    (f()は何かprintして1を返すようにしておく)

    を動かしてみると、f()が呼び出される前に例外がおきるのが観察できますよ?

    キャンセル

  • 2018/04/13 14:55

    a() + b() + c() * d() でいいですね。

    キャンセル

  • 2018/04/13 15:00

    最初そう書こうと思ったんですが、よく見ると質問者さんは+の演算が先かc()、d()、*が先かといったことを質問しておられたので、a(), b(),...でprintしてもそれが観察できないなぁと思い、言語仕様を持ち出して回答してみました。上のコメントは演算子が実行されている順番を知る手軽な方法として0割りが使えるなぁと思ってのことです。

    キャンセル

  • 2018/04/13 15:23

    >>演算子が実行されている順番
    あぁ‥なるほど。

    キャンセル

  • 2018/04/14 03:22

    僕は結合規則のことを「同じ優先順位グループに属する演算子のあいだに成り立つこと」と思っていました。
    たとえば、僕がひとつまえのコメントで取り上げた「i + i + i * i」という式では、「+」演算子が二回登場しています。これらは同じ演算子です。つまり同じ優先順位グループに属します。なので評価順序が結合規則に定められるのは納得できました。
    しかし、「+」演算子と「*」演算子は優先順位が違います。「*」演算子のほうが高いです。なので評価順序が結合規則に定められるのが納得できませんでした。

    int i=1;
    int j=i/0/f();

    上記のコードを実行すると、f()が呼び出される前に例外がおきました。今では僕の認識は間違っていたと思います。
    では結合規則とはなんでしょうか?僕なりに本やネットで調べましたがはっきりしません。正しく理解したいので、説明して欲しいです。どうかお願いします。

    キャンセル

  • 2018/04/14 04:10 編集

    すみません。上で「演算子の計算順序は優先順位と結合規則で決まります。」と書きましたが正確ではありませんでした。演算子の優先順位と結合規則は、演算子をどの項目に適用するか決めるもので、実際の評価順序とは関係ありません。「i + i + i * i」という式は、優先順位と結合規則により「(i + i) + (i * i)」と解釈され、その後の評価は左から右の順に行われます。

    キャンセル

  • 2018/04/14 11:40

    15.18. Additive Operators
    The additive operators have the same precedence and are syntactically left-associative (they group left-to-right).

    ということで、+や-などのadditive operatorsは同じ優先順位であり左から右へ結合される(要するに演算順序は左の演算子から順に計算される)これが結合規則と呼ばれるものと思います。言語仕様書ではassociative lawについて特に説明してないので数学の結合規則(質問者さんが捉えておられる通り)の意味と思います。

    15.7.3. Evaluation Respects Parentheses and Precedence
    The Java programming language respects the order of evaluation indicated explicitly by parentheses and implicitly by operator precedence.

    演算子の左オペランドから先に計算されるとあります。以上からa + b + c * dを考えると
    *は+より優先順位が上なので2番目の+より*が先に演算されるのは確かなんですがそれはa+bより先には演算されません。15.7.3の規則により2番目の+の左オペランド(a+b)が必ず右側のオペランド(c*d)より先に演算されると決まっているからです。

    キャンセル

  • 2018/04/14 11:44

    http://fujimura2.fiw-web.net/java/lang/precedence.htmlの「左結合・右結合」の項を参照のこと。

    キャンセル

  • 2018/04/15 05:14

    「i + i + i * i」の評価過程

    1、(i + i) + i * i
    結合規則により、2つ目の「i」が左の「+」演算子と結合する。
    2、(i + i)+(i * i)
    優先順位により、3つ目の「i」が右の「*」演算子と結合する。
    3、左から順に評価する(カッコ内が先に計算されるので、2つ目の演算子の計算が最後になる)。
    4、全体の評価が完了する

    「i * i + ++i」の評価過程

    1、(i * i) + ++i
    優先順位により、2つ目の「i」が左の「*」演算子と結合する。
    2、(i * i) + (++i)
    優先順位により、3つ目の「i」が左のの「++」演算子と結合する。
    3、左から順に評価する(カッコ内が先に計算されるので、2つ目の演算子の計算が最後になる)。
    4、全体の評価が完了する

    合っていますか?

    キャンセル

  • 2018/04/19 18:37

    どうでしょう?

    キャンセル

  • 2018/04/19 18:56

    まだ終わってなかったのね

    キャンセル

  • 2018/04/19 19:00

    言葉で伝える自信なくなってきたので、絵を書きました。回答をごらんいただければと思います。

    キャンセル

  • 2018/04/20 23:03

    追記を見まして、僕の考えをまとめなおしました。
    概ねこの通りだと思いますがいかがでしょうか?

    ルール
    1,結合規則と優先順位によってグループ分けをする。
    2,左右のオペランドを評価してから演算子による計算処理を行う。
    3,右よりも左のオペランドを先に評価する。

    「i + i + i * i」の評価過程

    ルール1により「(i + i) + (i * i)」とグループ分けをする。「(i + i)」が結合規則によるもので、「(i * i)」が優先順位によるもの。それぞれひとまとまりとして考える(以降(i + i)をa、(i * i)をbと呼ぶ)。
    次に、ルール2とルール3により「a」、「b」の順に評価した後、最後の「+」演算子の計算処理を行う。
    そして全体の評価が完了する。

    キャンセル

  • 2018/04/20 23:33

    少なくとも私には「あなたがどう理解しているか」「どんな場合でも正しく評価順序が把握できるほどこの仕様を把握したか」正確には保証できません。なんとなればあなたがコメントしておられることは自分の回答に書いたことにほぼ沿った内容にすぎないからです。「それでいいと思います」というのは簡単ですが、正直言うとそういうコメントは気休めのようなものでしかないのでは?

    本回答では「私自身がどう把握しているか」をご紹介しましたが、問題はそれを見たあなたが腑に落ちたかどうかです。自分は完全な知識があるわけでもありませんし、それを他者に分かり易く伝える能力があると過信もしてません。相手に伝わったようならよし、伝わらなかったら自分の説明が下手だったのだろうと思います。コミュニケーションによる知識・知見の伝達というのは得てしてそういうものですよね?

    「人によいといってもらって安心する」のは教育の方法論としては価値があると思いますが、この場は教育の場ではないと自分は考えます。そうではなく「他のプログラマーがどういう知見・知識を持っているかに触れることができる場」だと思っています。それを自分がどうゲットするかどうかは自分自身の問題です。逆に問いかけたいのですがあなたは「納得」したのでしょうか?まだ不安な点があるのでしょうか?不安な点は何でしょうか?

    納得できない点があるならそれを具体的に明記すれば知見をお持ちの方がまた価値あるコメントを付けてくださるかも知れませんよ?

    わたくしが何を申し上げたいかお判りいただけるでしょうか?

    キャンセル

  • 2018/04/21 01:14

    理解できているか不安だったので(日本語が下手であることが原因ではない)まとめを行いました。
    今思えばあまり意味のない質問だったかもしれません。
    とにかく「自分(KSwordさん)の回答に書いたことにほぼ沿った内容」であれば良かったです。
    疑問点や不安に思うことはもう特にありません。
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Java

    13776questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。