53310


`f( i - 1) + i`はどちらが先に処理？してるのでしょうか？  
演算子の優先順位を見ると()が先で+のほうが後ですがこの場合は関数の()が先だと思うのですがその場合後の+はどこで使われているのでしょうか？  
質問ですが関数の()と計算の()同じですか？それと言語によって演算子の優先順位は変わりますか？

[code]

    static int f(int i)
    {
        Console.WriteLine(i);
    
        if( i <= 0)
        {
            return 0;
        }
        else
        {
            return f(i -1) + i;
        }
    }
    static void Main(string[] args)
    {
        //int a = 5;
        int t = f(5);
    
        Console.ReadKey();
    }
    
[/code]


CC BY-SA 4.0
2019-03-08T10:56:50.287
53297
2019-03-09T08:15:59.860
2019-03-09T07:30:19.013
76
question
c#
演算子の優先順位でこの場合はどのように処理されるのかが知りたい。


理解にお困りの`return`文が返す値を計算している式を見ると、

`f(i - 1) + i`という計算を行っていることはお解り頂けると思います。  
これを、説明の都合上、`j`, `k`という整数変数を使って書き直すと、

[code]

    int j = i - 1;    // 引き算
    int k = f(j);     // 関数呼び出し
    return k + i;     // 足し算の結果をリターン
    
[/code]

と、いう風に書き直せます。

確かに四則演算には優先順位があり、優先順位を表現上やむを得なく変更するために`()`を使ってその中に記述された部分式を優先的に計算するというルールはありますが、もう一つ、

> 関数呼び出しを含む計算式は、関数呼び出しが優先され、呼ばれた関数の計算結果が帰ってくるまで式の計算は据え置かれる

というルールがあるというか、そういう振る舞いをします（あたり前の事なんですが）。

それを踏まえて上のプログラムがどう動くか追ってみると、  
（説明の都合上`f()`が呼ばれる度に文字が一つインデントが深くなると思ってください）

[code]

    f(5)を計算するためには、f(5 - 1)を計算する（呼ぶ）
     f(5 - 1) つまり f(4)を呼ぶ
      f(4 - 1) つまり f(3)を呼ぶ
       f(3 - 1) つまり f(2)を呼ぶ
        f(2 - 1) つまり f(1)を呼ぶ
         f(1 - 1) つまり f(0)を呼ぶ
          f(0) は 0 を返す
         f(0) が 0 を返したので 0 + 1 (=1) を返す
       f(1) が 1 を返したので 1 + 2 (=3)を返す
      f(2) が 3 を返したので 3 + 3 (=6)を返す
     f(3) が 6 を返したので 6 + 4 (=10)を返す
    f(4) が 10 を返したので 10 + 5 (=15)を返す
    
[/code]

今回の問題点は関数を`f()`というどんな処理をするかわからない上に短い関数名にしてしまったため、関数呼び出しの引数を指定するための`()`と四則演算の優先順位を意図的に変更する`()`を取り違えたのも原因だと思います。  
上の振る舞いを見ると、0から引数までの総和を求める関数の様なので、僕でしたら、

[code]

    int f(int i)
    
[/code]

というなにをしたいのかわからない名前ではなく

[code]

    int sumOfZeroTo(int i)
    
[/code]

の様な関数名にしたと思います。

この様に`for`や`while`などのループを使わずに、関数`f()`の中で自分自身（関数`f()`）を呼ぶことで目的の計算結果を得る手法が当初の質問タイトルにあった
**再帰**
と呼ばれる手法ですが、脱出条件を間違えると簡単に無限ループになるなどデメリットもそれなりなので、手法として覚えておくだけで、ループで済む計算ならループで処理してしまった方が、ビギナーにはバグを作り込むリスクを減らせて良いと僕は思います。

最後になりますが、matsuzawaさんは言語の「ある機能」の確認のためのコード素片を書いてプログラム言語の勉強をされているようですが、そろそろ数百行規模のプログラムを書いてみて、それに必要な機能は何で、それはどうやって記述すれば良いのだろうか？という勉強方法に切り替えて行かれた方が良い気がします。  
プログラムは、「やりたいこと」があって、それをどういう手続きに分割すれば出来るか？（大まかな抽象化）した関数やクラス  
の中ででは、それは具体的には何をすれば良いのか？・・・と言うことを繰り返して行く作業ですが、今の勉強方法では、言語の機能は覚えられても、やりたいこと（例えばこういう入力から、こういう出力を得たい）というプログラム全体の流れを（徐々に）細分化してやりたいことをプログラムを書き、動かすことで達成するという勉強が出来ないと思います。

言語の機能を覚えるだけでプログラムを書きたいわけではないのでしたら今のままで構わないと思いますが、プログラミングというのは「手段」であって、「目的」は「プログラムを作る（書く）ことで、処理させたいことが何度行っても間違い無く行える様になる」事だと思います。この点を頭の片隅にでも留め置きながら今後の勉強をされること良いのではないかと思います。


CC BY-SA 4.0
2019-03-09T01:09:54.363
53310
2019-03-09T02:13:14.107
2019-03-09T02:13:14.107
14745
14745
53297
answer


演算子の優先順位と式の各項の評価順は別の話です。例えば、

> func1(a) + func2(b) * func3(c) // int funcn(int)とする

という式があったとして、`func2(b)`の戻り値と`func3(b)`の戻り値の乗算が行われ、それに`func1(a)`の戻り値が加算されるのは、演算子の優先順位の話です。`func1`
`func2` `func3`がどの順で実行されるかは、それとは別に定められている可能性があります。

  * 演算子の優先順位に従う
  * 左から右に決まっている(C#はこれらしい)
  * 決まってない(実装依存)(C/C++)

決まっていたとしても最適化の都合で順序は変わる可能性がありますし、副作用の有無でも変わります。

ポイントは、この順序に依存するようなコードは書くべきではない、ということです。

* * *

> 関数の()が先だと思うのですがその場合後の+はどこで使われているのでしょうか？
>
> return f(i - 1) + i

において、`return f(i - 1)`が処理されて + i がどっかに行ってしまう、という想像をされているのでしょうか。

`return`は後続の式(`f(i - 1) + i`)全体にかかるので、式の中の優先順にとは無関係です。

> 関数の()と計算の()同じですか？

計算の`()`とは例えば`(1+2)*3`の括弧だと思いますが、違います。そもそもこれは演算子ではありません。

> 言語によって演算子の優先順位は変わりますか？

おおむね似たり寄ったりですが(あまり違うと混乱するので)、正確には言語次第です。そもそも演算子の有る無しがあります。


CC BY-SA 4.0
2019-03-09T08:15:59.860
53316
2019-03-09T08:15:59.860
2020-06-17T08:14:45.997
-1
5793
53297
answer