2016-05-30 2 views
0

私はlispを学ぶためにlispのためにl99を使っています。このlispマクロをどのように構造化しますか?

これはhereからです。((x) (x (evaluate-boolean left bindings) (evaluate-boolean right bindings)))をすべてマクロに書き込むために、練習用にマクロを適用したいと考えています。

(defun evaluate-boolean (expression bindings) 
    "Evaluates the boolean expression. Returns t or nil 

expression := variable 
      | constant 
      | '(' operator expression expression ')' 
      | '(' not expression ')' 
      . 
constant := 'true' | 'fail' . 
variable := symbol . 
operator := 'and' | 'or' | 'nand' | 'nor' | 'xor' | 'impl' | 'equ' . 

bindings is a list of pairs (variable . constant) 
" 
    (cond ((eq expression 'true) t) 
     ((eq expression 'fail) nil) 
     ((symbolp expression) 
     (let ((pair (assoc expression bindings))) 
      (if pair 
       (progn 
       (assert (member (cdr pair) '(true fail))) 
       (eql 'true (cdr pair))) 
       (error "No variable named ~A in the bindings." expression)))) 
     ((atom expression) (error "Invalid atom ~A in the expression." expression)) 
     (t (case (length expression) 
      ((2) (destructuring-bind (op subexpression) expression 
        (case op 
         ((not) (not (evaluate-boolean subexpression bindings))) 
         (otherwise (error "Invalid operator ~A in ~A" op expression))))) 
      ((3) (destructuring-bind (op left right) expression 
        (case op 
         ((and) (and (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         ((or) (or (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         ((nand) (nand (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         ((nor) (nor (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         ((xor) (xor (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         ((impl) (impl (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         ((equ) (equ (evaluate-boolean left bindings) (evaluate-boolean right bindings))) 
         (otherwise (error "Invalid operator ~A" op))))) 
      (otherwise (error "Invalid expression ~A" expression)))))) 

私はいくつかのことを試してみたが、それらすべてが不足している変数を報告するエラーを与えているようです。

私は defmacroとしてマクロ

  • を実装する方法を

    、または

  • evaluate-boolean関数内で、macroletを使用していますか?その後、

I defunまたはdefmacro最初と通常のテストのものうち、fletとそれを交換してください。これに関するアドバイスは?

答えて

4

あなたが試したことを言っていないので、間違ったことは分かりませんが、おそらくCASEの個々のケースをマクロ呼び出しに置き換えようとしたのでしょうか?これは外側のマクロ(CASE)が内側のマクロの前に展開されているために動作しません。外側マクロの構文を生成するために内部マクロを使用することはできません。この場合のケース)。

解決策は、CASEを生成するマクロを作成することです。何かのように:

(macrolet ((ops-case (op-sym (&rest ops)) 
      `(case ,op-sym 
       ,@(loop for op in ops 
         collect `((,op) (,op (evaluate-boolean left bindings) 
              (evaluate-boolean right bindings)))) 
       (otherwise (error "Invalid operator ~A" ,op-sym))))) 
    (ops-case op (and or nand nor xor impl equ))) 

私は確かにこれは本当に良いアイデアです。このようなマクロは、コードを理解しにくくする傾向があり、コードを大幅に短縮することもありません。通常は、コード内に複数回現れるパターンを抽象化するためにマクロを使用したいと考えています。

(defmacro ecase-template (keyform template &body cases) 
    `(ecase ,keyform 
    ,@(loop for case in cases 
      collect (sublis `((_ . ,case)) template)))) 

これはケースからの値でtempateにアンダースコアを代入した場合の表現を生成します。

より一般的なアプローチは、このようなものかもしれません。たとえば:

CL-USER> (macroexpand-1 '(ecase-template op 
          ((_) (_ (evaluate-boolean left bindings) 
            (evaluate-boolean right bindings))) 
          and or nand nor xor impl equ)) 
(ECASE OP 
    ((AND) 
    (AND (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))) 
    ((OR) 
    (OR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))) 
    ((NAND) 
    (NAND (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))) 
    ((NOR) 
    (NOR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))) 
    ((XOR) 
    (XOR (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))) 
    ((IMPL) 
    (IMPL (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS))) 
    ((EQU) 
    (EQU (EVALUATE-BOOLEAN LEFT BINDINGS) (EVALUATE-BOOLEAN RIGHT BINDINGS)))) 
+0

内側/外側のマクロのことを説明してくれてありがとう! – ackerleytng

1

これは、あなたが後にしている、非常にどのようなことではないかもしれないが、CLOSは、派遣・オン・シンボル評価のこの種のためのちょうど素晴らしいです。ここで

は、あなたが「直接」を定義することができますマクロと一緒に(もちろん、ある、あなたの小さな言語のための本当に evalapply)汎用関数のペアを使用して、評価者の(非常に最小限-テスト)の実装でありますジェネリック関数のメソッド。 「直接的な」方法とは、サブレート言語で同じ名前の演算子を使用する形式に簡単に変換する方法です(これは基本的に、コード内のすべての大きなネストされた caseをカバーします)。

(変数バインディングが見つかったら、特別なケースを追加するのではなく、その値でエバリュエーターに戻ることができます)。)

(defgeneric evaluate-boolean (expression bindings) 
    (:documentation 
    "Evaluates the boolean expression. Returns t or nil 

expression := variable 
      | constant 
      | '(' operator expression expression ')' 
      | '(' not expression ')' 
      . 
constant := 'true' | 'fail' . 
variable := symbol . 
operator := 'and' | 'or' | 'nand' | 'nor' | 'xor' | 'impl' | 'equ' . 

bindings is a list of pairs (variable . constant) 
") 
    (:method ((expression (eql 'true)) bindings) 
    (declare (ignore bindings)) 
    t) 
    (:method ((expression (eql 'false)) bindings) 
    (declare (ignore bindings)) 
    nil) 
    (:method ((expression symbol) bindings) 
    (let ((binding (assoc expression bindings))) 
    (if binding 
     (evaluate-boolean (cdr binding) bindings) 
     (error "no binding for ~A" expression)))) 
    (:method ((expression cons) bindings) 
    (apply-boolean-operator (car expression) (cdr expression) bindings)) 
    (:method (expression bindings) 
    (error "malformed expression ~A" expression))) 

(defgeneric apply-boolean-operator (op args bindings) 
    (:documentation "apply an operator to some arguments with some bindings") 
    (:method (op args bindings) 
    (error "unknown operator ~A" op))) 

(defmacro define-direct-boolean-operator (op-name arg-names) 
    (unless (and (symbolp op-name) (list arg-names) (every #'symbolp arg-names)) 
    ;; not even worth trying 
    (error "mutant boolean operator definition")) 
    `(defmethod apply-boolean-operator ((op (eql ',op-name)) 
             args bindings) 
    ;; this smells unhygenic but I think it is actually fine 
    (let ((la (length args)) 
      (lr ,(length arg-names))) 
     (unless (= la lr) 
     (error "~A wanted ~D argument~P but got ~D" op lr lr la))) 
    (destructuring-bind ,arg-names args 
     (,op-name ,@(mapcar (lambda (a) 
          `(evaluate-boolean ,a bindings)) 
          arg-names))))) 

(define-direct-boolean-operator not (x)) 
(define-direct-boolean-operator and (x y)) 
(define-direct-boolean-operator or (x y)) 
(define-direct-boolean-operator nand (x y)) 
(define-direct-boolean-operator xor (x y)) 
(define-direct-boolean-operator impl (x y)) 
(define-direct-boolean-operator equ (x y)) 
関連する問題