1

さて、私はさまざまな数の引数を取り、tryとcatchでそれらを実行することになっているこのマクロを持っています。引数リストが2より大きい場合、リストの最初の要素は、たとえばarg-listこのようにバインディングであると想定しています。[a 0]したがってarg-list、次のようになります([s (FileReader. (File. "text.txt"))] (. s read))

これは私が思いついたものです:

(defmacro safe [& arg-list] (list 'if (list '< (list 'count arg-list) '2)
    (list 'try (list 'eval arg-list) (list 'catch 'Exception 'e 'e))
    (list 'do (list 'eval arg-list) (list 'try (list 'eval (list 'rest arg-list)) (list 'catch 'Exception 'e 'e)))))

私はこれを2日間続けて機能させるのに苦労してきましたが、機能しません。たとえば、このマクロを試してみると、次のようになります。

(safe (+ 2 3))

このエラーが発生します:

ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn  user/eval91 (NO_SOURCE_FILE:100)

私はClojureを使って4日間しか働いていないので、コードが悪い場合はご容赦ください。

4

2 に答える 2

4

さて...まず、clojureのマクロ構文を読んでおくことをお勧めします。ここでは少しプリマーを提供しますが、詳しくは説明しません。

まず最初に、これがあなたのマクロです。

(defmacro safe [bindings? & forms]                                              
  (let [bindings (if (and (even? (count bindings?)) (vector? bindings?))        
                   bindings? nil)                                               
        forms    (if bindings forms (cons bindings? forms))                     
        except  `(catch Exception e# e#)]                                       
    (if bindings                                                                
    `(let ~bindings (try ~@forms ~except))                                      
    `(try ~@forms ~except))))

そして今、ウォークスルーのために。

Clojureの(let)マクロは、偶数の引数を持つベクトルを要求し、destructuringと呼ばれるいくつかの非常に興味深い動作をサポートします。このマクロの目的のために、有効なバインディング引数は最初はベクトルで、次に偶数の長さであると想定しています。(let)の評価はこれと同じチェックを実行しますが、最初のフォームがバインディングではなく評価対象のフォームである可能性があり、その場合は異なる動作を示す可能性があるため、このマクロはそれを実行する必要があります。

マクロ自体に関しては、引数を処理するために(let)を使用します。この記号bindingsは、バインディングの存在を示すことと、バインディングベクトルが存在する場合はそれを取得することの2つの目的を果たします。formsは、引数の最初のバインディング(clojureで実行できます)からbindings、エラーが含まれる環境で実行するフォームシーケンス全体の影響を受ける値に再定義されます。シンボルは実際には必要ありません。except各拡張ケースでその(キャッチ)フォームを再記述するというコードの重複を回避するだけです。

私が使用する記号`(バッククォートまたはバックティックとして知られている)は、ここでは通常のクォート(')と同等ですが、clojureを使用すると、クォートされたフォームではなく、バッククォートされたフォーム内でマクロ展開構文を使用できます。マクロ構文には、〜(引用符で囲まれていない)演算子と〜@(引用符で囲まれていない挿入)uperatorが含まれています。これらの3ビットの表記法を使用して、両方の望ましいケースを定義しました。バインディングフォームと試行するフォームを挿入するバインディングフォームを使用したletと、単純なtryonlyの場合です。

条件付きを削除して生成することができます

(defmacro safe [bindings? & forms]                                              
  (let [bindings (if (and (even? (count bindings?)) (vector? bindings?))        
                   bindings? [])                                               
        forms    (if-not (empty? bindings) 
                   forms (cons bindings? forms))                     
        except  `(catch Exception e# e#)]                                      
    `(let ~bindings (try ~@forms ~except))))

しかし、結合形式がない場合は、余分な(let)があります。

于 2013-01-10T19:42:46.453 に答える
1

これは必要ありませんeval。マクロ拡張の結果はすでに評価されています。必要なことは、マクロ内の構文引用を使用して最も簡単に実現できます。

(defmacro safe [& args]
  (if (< (count args) 2)
    `(try ~@args (catch Exception e# e#))
    `(let ~(first args)
       (try ~@(rest args) (catch Exception e# e#)))))

(safe (+ 2 3)) => 5
(safe [x 3] (+ 2 x)) => 5
(safe (Integer/parseInt "a")) => #<NumberFormatException java.lang.NumberFormatException: For input string: "a">

表示されている例外の理由はarg-list、例ではフォームのリストであり、この場合、リストである単一の項目があるため'(+ 2 5)です。最初の項目がリストであるリストを評価する場合、最初に内側のフォームが評価され、次に外側のフォームが評価されます。

(eval '(+ 2 3)) => 5
(eval '((+ 2 3))) => (eval '(5)) => exception, because 5 is not a function.

マクロは、に変更(list 'eval arg-list)することで修正される場合があります(list 'eval (first arg-list))

于 2013-01-10T19:30:33.773 に答える