Posted 2020-07-05 21:02:45 GMT
古くからボディ部に複数の式を取れることを暗黙のprogn
といいますが、別にlet
でも良いんじゃないかなと思って試してみました。
(defpackage let
(:use)
(:export cond lambda))(defun implicit-let-form-p (form)
(member (car form) '(let let*)
:test #'string-equal
:key #'princ-to-string))
(defmacro let:cond (&rest clauses)
`(cond ,@(mapcar (lambda (c)
(if (implicit-let-form-p (cdr c))
`(,(car c) ,(cdr c))
c))
clauses)))
(defmacro let:lambda ((&rest args) &body clauses)
`(lambda (,@args)
,@(if (implicit-let-form-p clauses)
`(,clauses)
clauses)))
(defun fib (n)
(let:cond ((< n 2) let ((f n)) f)
(T let ((f1 (fib (1- n)))
(f2 (fib (- n 2))))
(+ f1 f2))))
(fib 10)
→ 55
(mapcar (let:lambda (x)
let ((y (* 2 x)))
y)
'(0 1 2 3))
→ (0 2 4 6)
ネストが一つ減らせる位しか御利益がないですが、大抵の言語のブロックは、変数のスコープと複数フォームを纏める機能が合体しているので、progn
まで分解されずに、let
がビルディングブロックん基本なのかなと思ったり思わなかったりです。
ちなみに、どこかでみたことがある気がしましたが、Conniverのcdefun
のボディ部での“AUX”という記述が今回の暗黙のletそのままでした。
(完全に忘却していた……)
("AUX" (x y z) ...)
のように単体フォームでも使えるようですが、詳細は調べきれていません。
もしかしたら、Conniverは暗黙のprognから進んで、暗黙のletだったのかも?
更新:※Conniverのマニュアルで確認してみたところ、フォームの第二要素が予約語“AUX”であった場合、第三要素はprog
変数宣言となる、ということみたいです。
つまり暗黙のprog
ということみたいですが、暗黙のlet
みたいなものといえるでしょう。
Conniverの“AUX”は、MDLが由来のようですが、受け継いたCommon Lispのように引数部に記述するのではなく、ボディ部に記述するというのが面白いですね。
progn
とはLispでは値を返すスタイルが古くから基本となっていますが、副作用目的で複数の式をまとめる記述としてprog
やprog2
というフォームも古くから存在しました。
任意の複数の式をまとめるフォームということで落ち着いたのがprogn
ですが、SDS 930 LISPあたりが最初のようです。
progn
は便利だったのか、ついでにcond
や、lambda
の既存のフォームのボディ部で、progn
のように複数の式を取れるように拡張されました。
これを暗黙のprogn
と呼びますが、元は1つの式しか記述することができなかったため、暗黙のprogn
という言葉がうまれ後世まで伝わってしまったのでしょう。
(lambda (x) x)
↓
(lamba (x) (progn x x x))
↓
(lamba (x) x x x)
今となっては何故1つの式しか元は記述することができなかったのかと思ったりもしますが、複数の式を含むということは、値を返さない式を含む(副作用目的の式を含む)ということになるので、元々のLISPは純粋な関数を指向していたともいえます。
もちろん手続的に記述するprog
もあったりはするのですが、元々はsetq
等の代入もprog
の中でしか使えませんでした。
■
HTML generated by 3bmd in LispWorks Personal Edition 7.1.2