22861


clojure初心者です。よろしくお願いします。

話を簡単にするため、以下のマクロを試に作ってみたとします。

[code]

    (defmacro aiueo [abc] (list 'defn (symbol abc) [] (list 'str "aiueo-------")))
    
[/code]

これで

[code]

     (aiueo "hoge")
    
[/code]

とすると

`hoge` という関数が動的に生成されます。

[code]

    (hoge "xyz")
    ;; -> "aiueo-------xyz"
    
[/code]

この調子で、関数名だけを変えて動的に関数を作ろうと試してみました。

[code]

    (map #(aiueo %) ["abc" "def" "ghi"])
    ;; -> (#'user/p1__97# #'user/p1__97# #'user/p1__97#)
    
[/code]

このあと、期待通りであれば

[code]

    (abc "aaa")
    ;; -> "aiueo-------aaa"
    
[/code]

となるはずなのですが、実際には

[code]

    (abc "aaa")
    ;; -> CompilerException java.lang.RuntimeException: Unable to resolve symbol: abc in this context, compiling:(NO_SOURCE_PATH:57:1)
    
[/code]

となってしまいました。

clojureを学んで3日目、マクロは今日初めて見たばかりなので当然自分が何かを間違っていると考えていますが、数時間考えても理由が分かりません。  
御存知の方ご教示お願いします。

動作環境は  
clojure: 1.8.0  
OS: Windows7

です。


CC BY-SA 3.0
2016-03-06T08:37:17.393
22851
2016-03-07T03:17:27.203
9403
question
clojure
配列(vector)に対してマクロ実行を指定したmapが期待通りに動かない


まず、`aiueo`の定義ですが、

[code]

    (defmacro aiueo [abc] (list 'defn (symbol abc) [] (list 'str "aiueo-------")))
    
[/code]

ではなく

[code]

    (defmacro aiueo [abc] (list 'defn (symbol abc) '[name] (list 'str "aiueo-------" 'name)))
    
[/code]

ではないでしょうか。以下この定義を使いますが、

[code]

    (map #(aiueo %) ["abc" "def" "ghi"])
    
[/code]

の部分を

[code]

    (map (fn [x] (aiueo x)) ["abc" "def" "ghi"])
    ;=> (#'user/x #'user/x #'user/x)
    
[/code]

としてみると分かりやすいですが、`aiueo`に渡っているのは全部`x`というシンボルです。
関数と違ってマクロは引数を評価しませんが、それがマクロの特徴であり、この性質を利用してマクロを書くことになります。  
ですので、意図したように動かすには、式を作成して、それを強制的に`eval`で評価するようなことになると思います。

[code]

    (map #(eval `(aiueo ~%)) ["abc" "def" "ghi"])
    ;=> (#'user/abc #'user/def #'user/ghi)
    
[/code]

つまりマクロの特徴をわざわざ潰していますので、こういう目的には通常マクロは使いません。

動的(実行時)に定義したい、とのことですが、マクロを利用して動的に定義をするとなると、最も動的でもマクロ展開時までの動的さを利用することになります。

[code]

    (defmacro defaiueos [names args & body]
      `(do ~@(map (fn [name] `(defn ~(symbol name) ~args (str "aiueo-------" ~@args)))
              names)))
    
[/code]

と定義マクロを定義して

[code]

    (defaiueos ["foo" "bar"] [name] (str "aiueo-------" name))
    
    (foo 8)
    ;=> "aiueo-------8"
    
[/code]

のように利用する等々

また、実行時でなければならない、ということであれば、マクロは使わず、ハッシュテーブル等に名前と関数を登録して呼び出したりする等の方が直接的かなと思います。

[code]

    (def *fns* (reduce (fn [res x]
                         (assoc res x (fn [name] (str "aiueo-------" name))))
                   {}
                   ["abc" "def" "ghi"]))
    
    (map #((*fns* %1) %2)
         ["abc" "def" "ghi"]
         [0 1 2])
    ;=> ("aiueo-------0" "aiueo-------1" "aiueo-------2")
    
[/code]


CC BY-SA 3.0
2016-03-06T13:38:57.793
22861
2016-03-06T13:38:57.793
3510
22851
answer


`map`から関数定義するなら関数にするのがよいのではないかと思います。

[code]

    (defn aiueo [abc] (intern 'user (symbol abc) (fn [] (str "aiueo----"))))
    
[/code]

余談ですが、`map`で定義している`def`という関数名は危険かも。

**追記：**

後から気付きましたが、`map`は遅延シーケンスを返すので、replではなくコード中の場合は`aiueo`が実行されないことがある（戻り値を参照していない場合）という問題もあります。`map`ではなく`doseq`を使うほうが目的にかなうのではないでしょうか。


CC BY-SA 3.0
2016-03-06T13:42:57.710
22862
2016-03-07T03:17:27.203
2016-03-07T03:17:27.203
4010
4010
22851
answer