たいていのプログラミング言語が、「関数」、「メソッド」、「手続き」などと呼ばれる言語構成要素を持っています。ここ最近のエントリー内で僕は、それらをひっくるめて(少し低水準な観点から)「サブルーチン」と呼びました。
このサブルーチン概念に関していえば、どの言語も大差ない印象を持ちます。いくつかの引数をもらって何かして1つの値を返しますよね。(値を返さないこともあるけど、ありゃ、暗黙に特定の値を返していると見てさしつかない。)
なんで引数はn個なのに、戻り値は1個なんでしょう? 多値関数をサポートしている言語もあるけど(例:Common Lisp)、なんかオマケみたいな扱い。言語の中核的仕様として多値を採用している例を僕は知りません(あったら、教えて)。
それはコンピュータのせいでしょうか
「プログラマの常識関連」の文脈で言及した、スタックやレジスタが原因かもしれません。
普通(“普通”の解釈が問題なのは承知で使います:-))引数はスタックに積みますが、戻り値はレジスタに返したい。戻り値が複数はイヤですよね。複数の値が結果のときでも、配列やリストを作って、それへの参照(あー、参照が出てきちゃったよ)を1つだけ戻せばいいですし。
でも、スタックやレジスタという機構的制限は実はたいした問題じゃありません。引数と戻り値だけを扱うスタックを準備すればいいだけです。例えば、割り算の商と余りを同時に返す多値関数divは、スタック[7, 3, --](左がスタックトップ、「--」は「なんかあるかも」の意味)で呼ばれて、[2, 1, --]で戻ればOK。(Forthがこんな感じだったかな。)
その気になれば、どんな仮想機械だってデッチ上げられるんですから、「ハードウェア機構が多値関数の実現を阻害している」とは考えにくいですね。
それは理論のせいでしょうか
多値関数の計算体系はあるのでしょうか? 僕は知りませんが、それを作れと言われれば、なんとかなるような気がします。
通常の関数概念をベースにするとして、リスト構成記号(仮に`['と']')を構文プリミティブに追加します。e1、e2が通常の(単値の)式だとして、[e1, e2]という形式は2値の式になります。式[e1, e2]を使って関数fを定義すれば、fは2値関数となります。
通常のプログラミング言語に置き換えて言えば、return文の構文を、次のどれでもいいとするわけです。
return; return 式; return 式1, ..., 式n;
と、まー、そんなこんなで(どんなだ?)やっていけば、多値計算体系ができそうでしょう。
それは人間のせいでしょうか
僕は先に「通常の関数概念をベースにするとして…」と書きました。いきなり「多値関数ありき」から出発することもできるでしょうが、考えにくい。
どうも僕らは、単値関数に慣らされているせいか、多値関数を扱うことに困難を感じます。多値関数って、そもそも人間にとって不自然な概念なのかもしれません。
でもなんで「人間にとって不自然」なのでしょう。なんか深遠な事情でもあるのしょうか。…… 考えたのだけど、なにも深遠なことは思いつかないわ。
それで浅薄なことを言いますが: 人間が“ものを書く”とき、行に沿って書きますよね、それが影響しているんじゃないのかな。関数(の記号)がf、値を変数xに受けるとします。x = f();
、x = f(a);
、x = f(a, b);
と引数を増やしても、それは行方向に長くなるだけなので、書くのが特に難しくなることはありません。
ところが、fが2値関数だとして、値を変数x1, x2で受けるとき、次のように書くのが分かりやすいでしょう。
x1 = f1(a, b); x2 = f2(a, b);
つまり、値がm個なら、m行を使って書きたくなってしまう。「引数の増加→行が長くなる;値個数の増加→行が増える」という感じ。しかも、そうやって分けて書くと、多値関数fを、複数の単値関数f1, f2に分けてしまうことにもなるんで、結局単値関数に還元して扱っていることになっちゃってるしぃ。
もう、なんだか考えるのめんどくさくなってしまったよ。そう、多値関数はめんどくさいから人気が出ないのだよ、たぶん。