#:g1: 暗黙のprognならぬ暗黙のlet

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では値を返すスタイルが古くから基本となっていますが、副作用目的で複数の式をまとめる記述としてprogprog2というフォームも古くから存在しました。

任意の複数の式をまとめるフォームということで落ち着いたのが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

comments powered by Disqus