33213


**PHP 7.1 以降で、(ユーザ定義)関数へ渡す引数が少ない場合エラーになることを知り驚きました**

* * *

**Q1.他のプログラミング言語では、関数に渡す引数が少ない場合、一般的にはどういう挙動になるのでしょうか？**  
・これに関して、実装の流行りとかそういうのはあるのでしょうか？

**Q2.「言語に組み込みの関数」と「ユーザ定義関数」の挙動は一般的には異なるのでしょうか？**

* * *

**質問背景**  
・「関数へ渡す引数が少ないことを認めない言語」の方が多い、もしくはそれが普通かどうか、知りたいと思ったので


CC BY-SA 3.0
2017-03-09T04:20:55.350
33204
2017-03-11T06:10:10.500
2017-03-10T09:30:43.533
7886
7886
question
プログラミング言語
「関数へ渡す引数」が少ない場合の挙動


## A1. エラーになる言語もありますが、許容する言語もあります

C 言語では通常はエラーになりコンパイルが通りません。

[code]

    int add(int a, int b) {
      return a + b;
    }
    
    int main() {
      add(0);  // ←エラー
      return 0;
    }
    
[/code]

しかし、可変長引数という仕組みを使うと許容されます。

[code]

    int add(int a, ...) { // ... は２つ目以降を受け取ることを表す
      va_list ap;
      va_start(ap, a);
      int x = a + va_arg(ap, int);
      va_end(ap);
      return x
    }
    
    int main() {
      add(0);  // 実行されますが返ってくる値は不定です
      return 0;
    }
    
[/code]

C++ ではデフォルト引数というものを使うと許容されます。

[code]

    int add(int a, int b = 0) { // 指定しなかった場合の引数を指定できます
      return a + b;
    }
    
    int main() {
      add(0);  // b は 0 として扱われるので 0 が返ってきます
      return 0;
    }
    
[/code]

Javascript では実行されます。

[code]

    function add(a, b) {
      return a + b;
    }
    
    add(0); // 動作します。b は undefined という指示されていないという意味の値になり、 NaN を返します
    
[/code]

デフォルト引数と可変長引数どちらも言語特有ではなく多くの言語にある仕組みです。

> 「関数へ渡す引数が少ないことを認めない言語」の方が多い、もしくはそれが普通

JavaScript
のようなどんな数の引数でも認める言語は少ないと思いますが、多くの言語はデフォルト引数と可変長引数があるので、そういう意味では引数が少ないことを認める言語のほうが多いと思います。

## A2. 関数がどのように定義してあるかによります

PHP にもデフォルト引数、可変長引数があるので、それを使っている関数の場合は引数を少なくすることができます。


CC BY-SA 3.0
2017-03-09T09:35:47.377
33213
2017-03-09T09:35:47.377
8771
33204
answer


@ryohey さんの回答にちょっと補足いたします。

C言語の関数の呼び出し時に、引数はパラメーターより少なくでも、コンパイルできる場合もあります。例えば

[code]

    #include <stdio.h>
    
    int main()
    {
        int x = foo(2); // 引数が1つ
        printf("x=%d\n", x);
        return 0;
    }
    
    int foo(int p, int q) // パラメーターが２つ
    {
        return p + q;
    }
    
[/code]

gcc 4.9 でコンパイルすると、`-Wimplicit-function-declaration` をつけなければ、警告もエラーもありません。

当然、これは正確な仕上げ方ではありません。こうして出来たプログラムを実行すると、未定義の動作となりますので、いかにも危険です。

これはC言語の歴史的な問題です。以前、宣言機能がつけられていなかったので、コンパイラーが定義されていない関数の呼び出しを見ると、「この関数が任意の数の
`int` 型のパラメーターを必要とされ、戻り型も `int`
である」っと判定します。ゆえに、引数とパラメーターの数が違っても、コンパイルする時にエラーがありません。


CC BY-SA 3.0
2017-03-10T14:38:28.083
33252
2017-03-10T23:26:38.210
2017-03-10T23:26:38.210
18621
18621
33204
answer


補足です。

関数に[多重定義、オーバーロード](https://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E5%AE%9A%E7%BE%A9)という概念を持つ言語があります。（C++、C#、Javaなど）  
これは引数の異なる同名の関数を定義することを認めるもので、関数呼び出しの際に引数からどのバージョンを呼び出すかを判別します。

[code]

    // ２引数バージョン
    int add(int a, int b){
        return a + b;
    }
    
    // ３引数バージョン
    int add(int a, int b, int c){
        return a + b + c;
    }
    
    int main(){
        int sum1 = add(1, 2);     // ２引数バージョンを呼び出す
        int sum2 = add(1, 2, 3);  // ３引数バージョンを呼び出す
        int sum3 = add(1);        // コンパイルエラー
    }
    
[/code]

最後の`add(1)`に関しては、２引数バージョン・３引数バージョンいずれにも引数が適合しないため、呼び出すことができずコンパイルエラーとなります。

このことは裏返せば、 **関数に渡す引数が少ないことを許容する言語** は関数オーバーロードを実現できないことになります。


CC BY-SA 3.0
2017-03-10T21:58:04.440
33255
2017-03-10T21:58:04.440
4236
33204
answer


話題に出ていない言語かつ少ない引数を受け取れるものだけをピックアップしました。

* * *

Haskellでは、引数が少ないと渡されたところだけ部分適用された関数を返します。

[code]

    add a b = a + b
    x = add 1 -- 引数を一つ取る関数が返る
    y = x 2   -- 3が返る
    main = print y -- 3を表示
    
[/code]

全てがカリー化されている他の言語や、カリー化している関数はすべてこのような動作になるでしょう。

* * *

Rubyでは、通常のメソッドは引数の数が合わないとエラーですが、Procはあわなくてもnilが補充して渡されてエラーになりません。しかし、似たような形のラムダ式はエラーになります。

[code]

    # nilを+するとエラーになるため、エラーにならないようにしています。
    def add(a, b)
      a.to_i + b.to_i
    end
    add_p = proc { |a, b| a.to_i + b.to_i }
    add_l = -> (a, b) { a.to_i + b.to_i }
    add(0) # ArgumentError 引数の数が合わないのでエラー
    add_p.call(0) # 0, nil がわたされ、0が返る
    add_l.call(0) # ArgumentError 引数の数が合わないのでエラー
    
[/code]


CC BY-SA 3.0
2017-03-11T06:10:10.500
33259
2017-03-11T06:10:10.500
7347
33204
answer