#:g1: S式は前置記法でなくても良い

Posted 2020-07-28 21:59:39 GMT

S式といえば、逆ポーランド記法(前置記法)という印象がありますが、肝要なのはS式というデータでコードを記述することなので、特に前置でなければいけないということもない筈です。

そもそも、点対リストの記法は中置じゃないかと思うのですが、どうなのでしょう。

(a . d)

前置であれば、

(. a d)

となりそうですが、これでも特に問題はなさそうに思えます。

ちなみにPrologでは、[a|d]というリスト表記は、'.'(a,d)の糖衣構文らしく'.'は前置記法ですが、Lispに由来したものなのかどうなのか。

% az-prolog
%
| ?-'.'(a,d)=[a|d].
yes

点対リストの発展形

点対リストのドットは中置ではないかと書きましたが、Plasma(1974)には、この点対リストの記法を発展させたような記法(x op y)をメインに使用します。

なお、私個人の解釈では、Hewitt先生は、点対リストのドットをオペレーターとして解釈し、点対リスト記法を発展させているように見えるのですが、Plasmaの文献を眺めていてもドットを発展させたという記述は見付けられていないので、完全に独自解釈かもしれません。予めご了承下さい……。
少なくとも、リストの二番目に特別な記号があれば○○するというような構文の作り方ではない気がするのですが。

メッセージ送信

Plasmaではメッセージ送信は、(A <= M)と記述し、Lispでいう関数呼び出しに相当します。
矢印は逆転して記述することも可能で、(M => A)でも可です。
また、(A <= [x y z])の省略形はLispの関数フォームのように、(A x y z)と書けます。 この矢印がLispの点対リストの.に相当します。
なお、[x y z]は配列です。

四則演算

四則演算の+,-,*,/等もまた特別扱いされます。

(1 + 1) 
→ 2

Common Lispで()を再定義するとしたらこんな感じでしょうか

(progn
  (flet ((rdseq (srm chr)
           (let ((xpr (read-delimited-list #\] srm T)))
             (if (= 3 (length xpr))
                 (let ((op (cadr xpr))
                       (x (car xpr))
                       (y (caddr xpr)))
                   (case op 
                     ((list 'define x y))
                     ((=) 
                      (list x y))
                     (otherwise (coerce xpr 'vector))))
               (coerce xpr 'vector)))))
    (set-macro-character #\[ #'rdseq))
  (set-syntax-from-char #\] #\))
  ;;;
  (flet ((rdparen (srm chr)
             (declare (ignore chr))
             (let ((xpr (read-delimited-list #\) srm T)))
               (if (= 3 (length xpr))
                   (let ((op (cadr xpr))
                         (x (car xpr))
                         (y (caddr xpr)))
                     (case op 
                       (<= (cons x (coerce y 'list)))
                       (=> (cons y (coerce x 'list)))
                       ((+ - * / < > =< >=) 
                        (list op x y))
                       (otherwise xpr)))
                 xpr))))
      (set-macro-character #\( #'rdparen)))

(list 
 (list <= [42])
 (list 42)
 (list 0 1 2 3)
 ([0 1 2 3] => list)
 ([(42 + 69) (42 - 69) (42 * 69) (42 / 69)] => list))((42) (42) (0 1 2 3) (0 1 2 3) (111 -27 2898 14/23))

ちなみに、(の再定義は危険なので、全角括弧ででも試した方が良いかもしれません……。

定義構文

関数(アクタ)定義は、配列+≡の中置です。
Schemeのように、(define fcn (lambda (arg ...) ...))パタンと、左辺?に引数も記述する(define (fcn arg ...) ...)パタンがあります。
思えば、Lisp 1.5の頃からこの二種類は存在するようなのですが、大元はLisp 1.5なのでしょうか。

Plasmaでは下記のように書けます。 (なお、Plasmaにlambdaはありません)

[tak ≡ (lambda (x y z)
         (if (not (x > y))
             z
             ([([(x - 1) y z] => tak)
               ([(y - 1) z x] => tak)
               ([(z - 1) x y] => tak)] => tak)))]

[(tak x y z)(if (not (x > y)) z ([(tak (x - 1) y z) (tak (y - 1) z x) (tak (z - 1) x y)] => tak))]

([18 12 6] => tak) → 7

Common Lispで再現してみるなら、を中置のdefineと考え、二種のパタンそれぞれに展開するマクロにでもなりそうです。

(defmacro define (name expr)
  (etypecase name
    (cons `(defun ,(car name) (,@(cdr name))
             ,expr))
    (symbol 
     `(progn
        (declaim (function ,name))
        (setf (fdefinition ',name) ,expr)))))

まとめ

点対リストの記法の発展形をPlasmaを源流と捉えてつらつら書いてみましたが、点対リストの形式(x op y)には、未だ開拓されていない可能性があるようなないような。

(x op y)と書けると一体何が嬉しいのか、という気もしますが、ではLispがこれまで(a . d)と書いてきて一体何が嬉しかったのか、と思わないでもないです。

Hewitt先生の記法のアイデアは中置のS式に限らず結構面白いものが多いので、今後もちょこちょこ紹介していきたいと思います。
(絵文字や上付き/下付きのS式等々……)

参考


HTML generated by 3bmd in LispWorks Personal Edition 7.1.2

comments powered by Disqus