Posted 2018-11-12 19:54:32 GMT
Goの多値についての記事が人気のようで、この数日Twitterで多値の話題が賑わっています。
多値が話題になることなど、そうそうないですが、多値といえばやっぱりCommon Lispでしょう!、ということでLispと多値について書いてみます。
多値といえばCommon Lispですが、最初の仕様のCommon Lisp(1984)でも標準の言語機能になっています。
Common Lispの人達は、普段から普通に便利に使っていますが、多値周りはシンプルなデザインなので使い方で混乱する、ということも特にないでしょう。
典型的な使われ方には下記のようなものがあります。
しかし、その多値機能ですが、Common Lispで初導入という訳ではなく、直接の祖先であるLisp Machine Lispにから輸入したものです。
ということで、Lisp Machine Lispの歴史を遡ってみましたが、私が調べた限りでは、多値が導入されたのは、1976年辺りのMIT LispマシンのCONSのようです。
LISP Machine Progress Report(1977)では、
A traditional weakness of Lisp has been that functions have to
take a fixed number of arguments. Various implementations have added
kludges to allow variable numbers of arguments; these, however, tend
either to slow down the function-calling mechanism, even when the
feature is not used, or to force peculiar programming styles.
Lisp-machine Lisp allows functions to have optional parameters with
automatic user-controlled defaulting to an arbitrary expression in the
case where a corresponding argument is not supplied. It is also
possible to have a "rest" parameter, which is bound to a list of the
arguments not bound to previous parameters. This is frequently
important to simplify system programs and their interfaces. A similar problem with Lisp function calling occurs when one
wants to return more than one value. Traditionally one either returns
a list or stores some of the values into global variables. In Lisp
machine Lisp, there is a multiple-value-return feature which allows
multiple values to be returned without going through either of the
above subterfuges.
という風に、入力側のオプショナル引数と、出力側の多値の対で語られています。
複数の値を返したい、というニーズがそんなにあったのかは不明ですが、リストで返したり、大域変数経由で渡すところを多値機構として、すっきり纏めたという話は、それはそれで説得力があるという所でしょうか。
MIT LispマシンはSECDマシンに非常に近い構成らしいので、専用マシンは多値をハードウェア支援で高速に実現できますよ!的な記述も探してみましたが、そういう話はみつけられませんでした。残念。
興味のある方向にCONS時代のLispのマニュアルを置いておきます。
このマニュアルでは、%CALL-MULT-VALUE
や、%RETURN-N
等のプリミティブが定義されていることが判ります。
ちなみに、Scheme方面では、恐らく1980年代前半にT
が最初に導入し、その後紆余曲折を経てR5RSで仕様化されたようです。
Schemeも元を辿れば、Lisp Machine Lisp由来かなと思います。
多値の扱いを考える場合、N個の値を返す場合と、受けとる場合にどうなるかを考慮する必要がありますが、Common Lispでは下記のようになります。
nil
を受け取る詳細は、Common Lispの仕様を参照してください。
スペシャルオペレーター/標準マクロの個別の規定ですが、過剰であれば無視され、足りなければ、nil
が補われる動作と考えて問題ないと思います(multiple-value-call
以外は)。
ただこの挙動をもって、Common Lispの多値機構の挙動と見做せるかというと、そうではなく、そういう風にフォーム構成されているだけという点に注意が必要かなと思います。
受取り側が期待した個数より少なければnil
で補填され、敷衍して0個の場合はnil
となるのがCommon Lispの多値機構、という訳ではありません。
例えば、multiple-value-bind
は足りない場所はnil
を補い、過剰な場合は捨てますが、
(multiple-value-bind (q r s)
(floor 1 2)
(list q r s))
;=> (0 1 nil)
それは、多値を引数リストにする時にオプショナル引数のように処理しているからで、入出力の多値の個数が一致していなければ、エラーにすることも可能です。
また、nil
以外のデフォルト値を設定することも可能でしょう。
(defmacro strict-multiple-value-bind ((&rest vars) mv-form &body body)
`(multiple-value-call (lambda (,@vars) ,@body) ,mv-form))(strict-multiple-value-bind (q r s)
(floor 1 2)
(list q r))
;error> got 2 args, wanted at least 3.
以上ですが、Common Lispでは、継続と多値があまり連続した議論になっていないのがお判りでしょうか。
このまま書き進んで行こうと思いましたが、長くなったので、続きは別の回にしたいと思います。
■
HTML generated by 3bmd in LispWorks 7.0.0