1

我写了两个这样的函数,但正如你所见,它们大部分是相同的,所以我想写一个宏来简化它们。

我了解教科书中的简单宏示例,但我不知道如何编写自己的。

这是我的代码:

 (defn load-dict
       ;  "Load database from a txt file previous saved"
       [fname]
       (with-open [rdr (io/reader fname)]
                  (doseq [line (line-seq rdr)]
                         (println line)
                         (def vvv (clojure.string/split line #"\s"))
                         ;(println (str "count of vvv is " (count vvv)))
                         (if (< 1 (count vvv))
                           (add- dict (gen-word (nth vvv 0) (nth vvv 2) (nth vvv 1))))
                         )))
 (defn load-article
       ;  "Load article from a txt file"
       [fname]
       (with-open [rdr (io/reader fname)]
                  (doseq [line (line-seq rdr)]
                         (println line)
                         (def vvv (clojure.string/split line #"\s"))
                         ;(println (str "count of vvv is " (count vvv)))
                         (if (< 1 (count vvv))
                           (add- article vvv ))
                         )))

我应该写一个像这样的宏:

(defmacro load- [target fname &expr)
  `(... 
    (add- ~target expr)))

我实际上不知道如何编写这样的宏。我只是讨厌重复的代码。

PS,拖曳功能工作正常。我不关心变量这是代码的一部分。

4

2 回答 2

5

I would use a let block instead of def. Using def will bind a var and define vvv in your namespace. A macro is not really needed. You could simplify your code like this:

(defn load-from
   "Load database from a txt file previous saved"
   [fname load-fn]
   (with-open [rdr (io/reader fname)]
              (doseq [line (line-seq rdr)]
                     (println line)
                     (let [vvv (clojure.string/split line #"\s")]
                       (when (< 1 (count vvv))
                         (load-fn vvv))))))

And invoke it like this

(load-from "myfile.txt" #(add- dict (apply gen-word (take 3 %))))
(load-from "myfile.txt" #(add- article %))
于 2013-07-30T15:41:48.980 回答
3

user1944838 在这里是正确的,因为您不需要宏,并且由于宏会生成不需要它们的代码,因此在某些情况下使用起来稍微困难一些(例如,您不能将其传递给mapapply),因此使用函数是在实践中更可取。然而,了解如何正确编写宏非常重要。

我会把它写成一个模板宏,它将你传递给每个单词的名称绑定,然后调用你传递给宏的主体,宏又会通过符号名称使用该单词。

(defmacro with-loaded-article                                                                                                                                             
  [[name-for-line fname] & body]                                                                                                                                          
  `(with-open [rdr# (io/reader ~fname)]                                                                                                                                   
     (doseq [line# (line-seq rdr#)]                                                                                                                                       
       (println line#)                                                                                                                                                    
       (let [~name-for-line (clojure.string/split line# #"\s")]                                                                                                           
         ~@body)))) 
  • [name-for-line fname]表达式将第一个参数解构为单个“绑定形式”,该形式将用于生成符号及其解析为的值。这种格式在“with-*”宏中很常见,例如with-open除了这里我只采用一种绑定形式来保持代码更小。
  • syntax-quote 中的rdr#andline#符号是语法引用的一个特性,称为“auto gensyms”,它会导致语法引用中以 # 结尾的任何符号替换为结果表达式中唯一但一致的符号。
  • ~@是语法引号的拼接取消引用功能,body在这种情况下导致插入而不( )在其周围添加。

我们可以看到它如何扩展macroexpand-1pprint提示:(use 'clojure.pprint)

hello.core> (pprint (macroexpand-1 
                      `(with-loaded-article [line "tmp.txt"] (println line))))                                                                                 
(clojure.core/with-open                                                                                                                                                   
 [rdr__6337__auto__ (clojure.java.io/reader "tmp.txt")]                                                                                                                   
 (clojure.core/doseq                                                                                                                                                      
  [line__6338__auto__ (clojure.core/line-seq rdr__6337__auto__)]                                                                                                          
  (clojure.core/println line__6338__auto__)                                                                                                                               
  (clojure.core/let                                                                                                                                                       
   [hello.core/line (clojure.string/split line__6338__auto__ #"\s")]                                                                                                      
   (clojure.core/println hello.core/line))))                                                                                                                              

当我们运行生成的代码时,我们将文件的行作为序列获取:

hello.core> (with-loaded-article [line "tmp.txt"] (println line))                                                                                                         
hello world                                                                                                                                                               
[hello world]                                                                                                                                                             
world hello                                                                                                                                                               
[world hello]                                                                                                                                                             
and internet hello as well                                                                                                                                                
[and internet hello as well]
于 2013-07-30T17:04:44.393 回答