2017-08-25 1 views
0

シンボルとリストを取るCommon Lispマクロを書く必要があります。Common Lispマクロで再帰を管理する方法

((X "foo") (Y "bar") (Z "qwerty")) 

再帰的マクロの作品は、それがリスト内のシンボルを検索し、それがsymbolリターンT、そうでない場合はNILを返すを発見した場合:このように、記号や文字列:リストは二つの要素のリストで構成されており。私はこのコードを書いています:

(defmacro already-exist (symbol my-list) 
    (cond ((null (eval my-list)) NIL) 
     ((eql (caar (eval my-list)) symbol) 
    T) 
    (T `(already-exist symbol ,(cdr (eval my-list)))))) 

しかし問題は再帰的な部分です。実際、最初の要素の一部としてsymbolを持たないリストでマクロを実行しようとすると、エラーが発生します。例:

(defparameter listt '((X "foo") (Y "bar") (Z "qwerty"))) 

(already-exist Y listt) 

私が得るエラーは「不正な関数呼び出し」です。私はマクロが関数呼び出しとしてYを評価しようとするために起こると思います。どうすればこの問題を解決できますか?一般にCommon Lispで再帰マクロを書く最良の方法は何ですか?

+3

これには関数を使用する必要があります。マクロはコードを生成するために使用されます。また、 'ASSOC'(または'(member ...:key# 'first) ')を使ってキーがすでに存在するかどうかを調べることもできます。 – jkiiski

+0

@jkiiskiマクロに引用符で囲まれていないシンボルを渡す必要があります。コードでそのシンボルを評価したくないので、そのために関数の代わりにマクロを書くことにしました。関数内で同じ振る舞いを得る方法はありますか?あなたが知っているなら教えてください。とにかく、私はASSOCを知らなかったので、ありがとう!私は心に留めておきます! – Jim

+2

シンボルを引用するかキーワードを使用するだけです。関数のように見えるフォームの評価がうまくいかないと、コードを読んでいる人が混乱します。 – jkiiski

答えて

3

実際のエラーは何ですか?

((Y "bar") (Z "qwerty"))

CL-USER 3 > (already-exist Y listt) 

Error: Illegal car (Y "bar") in compound form ((Y "bar") (Z "qwerty")). 

だからあなたは、有効なLispコードではありません、この式を、実行しようとしている:

実際のエラーは、このです。

エラー1:

あなたのコードは、あなたが評価され、my-listのシンボルを取得します。次回は評価してはならないリストが得られます。

エラー2:

代わりsymbolの値に、symbolでフォームを作成します。値

  • は、その前にカンマを入れてsymbolの値を渡すと、それはシンボルを持っている場合にのみ評価my-listにその醜い解決策につながる

    • 試みを修復する

      試み

    例:

    CL-USER 2 > (defmacro already-exist (symbol my-list)        
           (when (symbolp my-list)           
           (setf my-list (eval my-list)))         
           (cond ((null my-list)            
            NIL)              
            ((eql (caar my-list) symbol)         
            T)               
            (T `(already-exist ,symbol ,(cdr my-list))))) 
    ALREADY-EXIST 
    
    CL-USER 3 > (defparameter listt '((X "foo") (Y "bar") (Z "qwerty"))) 
    LISTT 
    
    CL-USER 4 > (already-exist Y listt) 
    T 
    
    CL-USER 5 > (already-exist A listt) 
    NIL 
    

    しかしそれはほとんど意味がなく、新しい問題を作り出します。

    evalを呼び出す再帰マクロ呼び出しを記述するのは難しいです。 evalは、レキシカル環境内で評価が行われないため、レキシカルバインディングの値を取得できません。

    使用を意味機能

    CL-USER 10 > (member 'y listt :key #'car) 
    ((Y "bar") (Z "qwerty")) 
    

  • 4

    私はCommon Lispマクロ[...]を書く必要があります。マクロは再帰的に動作します。

    あなたは本当にあなたの値は、実行時にのみ知られている場合は、マクロは、問題を解決することはできません実際にはマクロを、必要はありません。シンボルを未評価のままにしたい場合は、シンボルを引用する必要があります。マクロからevalを呼び出す

    ... (eval my-list) ...

    は大きなコードのにおいです。あなたのマクロはコードで動作していますが、listシンボルはシンボルであることを除いて、この時点では何も意味しません。マクロは、listが意味のある値にバインドされている環境(ヌル語彙環境ではeval)に展開される可能性があります。は、ではありません。

    再帰的な展開を持つことが可能ですが、それだけでマクロは再帰的ではありません:マクロ展開は不動点方法で適用される

    * (defmacro foo (x) (foo x)) 
    ; in: DEFMACRO FOO 
    ;  (FOO X) 
    ; 
    ; caught STYLE-WARNING: 
    ; undefined function: FOO 
    ; 
    ; compilation unit finished 
    ; Undefined function: 
    ;  FOO 
    ; caught 1 STYLE-WARNING condition 
    STYLE-WARNING: 
        FOO is being redefined as a macro when it was previously assumed to be a function. 
    * (foo 3) 
    
    debugger invoked on a UNDEFINED-FUNCTION in thread 
    #<THREAD "main thread" RUNNING {100399C503}>: 
        The function COMMON-LISP-USER::FOO is undefined. 
    

    :X0のマクロ展開として、コードX0、計算X1からスタート何もマクロ展開がなくなるまで続けます。あなたのマクロは自分自身を呼び出すのではなく、マクロ展開機能は各パスの後に必要なだけ頻繁に呼び出します。macroexpandを手動で呼び出さない限り、マクロ展開中に動的バインディングを確立できません。

    あなたのマクロは、マクロを呼び出すコードにマクロ展開することもできます。しかし、あなたは無条件にそうではないことに注意しなければなりません。さもなければ、あなたは無限のマクロ展開をするでしょう。

    listが実行時にのみ既知の値になる場合は、通常の関数を記述する必要があります。基本的に、あなたは何をしたい:

    (member symbol list :key #'first) 
    

    MEMBERMACROEXPANDを参照してください。

    1

    再帰的なマクロは

    はあなたがすることを試みた想像できません..それはそれで(expand (cdr elements))を呼び出すように、マクロ機能がそれをコンパイルしている今

    (defmacro expand (&rest elements) 
        (if (not (null (cdr elements))) 
         `(+ ,(car elements) ,(expand (cdr elements))) 
         (car elements))) 
    

    は、すべてのマクロを展開し

    (defmacro expand (&rest elements) 
        (if (not (null (cdr elements))) 
         `(+ ,(car elements) (+ (cdr elements) (+ (cdr elements) (+ (cdr elements) (+ (cdr elements) ...)))) 
         (car elements))) 
    

    あなたはそれを見ますか?今、あなただけではなく、最初の部分を拡大し、代わりにexpandとシンプルな表現を再帰が、放置しない想像:

    (defmacro expand (&rest elements) 
        (if (not (null (cdr elements))) 
         `(+ ,(car elements) (expand ,@(cdr elements))) 
         (car elements))) 
    

    マクロが直接マクロを使用することはありませんので、これは完全に異なっています。しかし(expand 1 2 3)(+ 1 (expand 2 3))に展開し、Lispは

    再帰関数はマクロでOKです再帰なし(+ 1 (+ 2 3))を残し、何も残っていないまで、マクロを拡大し続けて:

    (defmacro expand (&rest elements) 
        (labels ((recfun (elements) 
          (if (not (null (cdr elements))) 
           `(+ ,(car elements) ,(recfun (cdr elements))) 
           (car elements)))) 
        (recfun elements))) 
    

    それはする必要はありません。局所的に定義された関数のいずれか。通常、Iの関数としての機能のほとんどを実装し、ちょうど関数を呼び出すマクロを残して、いくつかの引数の評価を遅らせるために、マクロを作る:私は(make-env 'cons 'car 'cdr)(make-env '(cons car cdr))を行うにはしたくなかったので

    (defun make-env-fun (names) 
        (mapcar (lambda (name) (cons name (symbol-function name))) names)) 
    
    (defmacro make-env (&rest variables) 
        `(make-env-fun ',variables)) 
    
    (make-env cons car cdr) 
    ; ==> ((cons . #<system-function cons>) 
    ;  (car . #<system-function car>) 
    ;  (cdr . #<system-function cdr>)) 
    

    だから、マクロが存在します。マクロはその問題のみを修正し、関数がまだ行っている実際の作業は修正しません。

    したがって、の代わりに(already-exist symbol ((symbol "bla")))を許可するマクロが必要です。あなた見えますか?

    +0

    '(expand(cdr elements))'や '(expand、(cdr elements))'を意味しますか?私が知っている限り、前者は明確に定義されていません。 SBCLでは、expandへの呼び出しは、最初にマクロ本体の関数とみなされます。次に、名前がマクロにバインドされます(警告が表示されます)。マクロを再定義すると、マクロは展開されますが、結果のフォームはマクロを再定義します。しかし、固定小数点があるかもしれませんが、通常は "再帰マクロ"は*自分自身への呼び出しに*展開するマクロです。 – coredump

    +1

    @coredump最初のコードブロックは意図的に無効であるため、意図的に無効です。どのソリューションもおそらくCLHSを超えており、おそらく大丈夫です。再帰的マクロは不可能です。同じマクロを使用するコードに展開するマクロは実際には再帰的ではありません。これは1つのステップしかないため、マクロ展開が再帰的であるため再帰的な感触を与えます。 3番目のブロックはその例であり、 '(expand、@(cdr elements))' – Sylwester

    関連する問題