我写了两个像这样的函数,但正如你所看到的那样大部分是相同的,所以我想编写一个宏来简化它们。
我理解教科书中的简单宏示例,但我不知道如何编写自己的。
这是我的代码:
(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,拖曳功能正常。我不关心变量这是代码的一部分。答案 0 :(得分:5)
我会使用let块而不是def。使用def将绑定var并在命名空间中定义vvv。实际上并不需要宏。您可以像这样简化代码:
(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))))))
并像这样调用它
(load-from "myfile.txt" #(add- dict (apply gen-word (take 3 %))))
(load-from "myfile.txt" #(add- article %))
答案 1 :(得分:3)
user1944838在这里是正确的,因为你不需要宏,并且由于宏生成不需要它们的代码,在某些情况下稍微难以处理(你不能将它传递给map
或者例如apply
),在实践中优选使用函数。然而,了解如何正确编写宏非常重要。
我会把它写成一个模板宏,它将你传给它的名字绑定到每个单词,然后调用传递给宏的主体,然后通过符号名称使用该单词。
(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
,除了这里我只采用一种绑定形式来保持代码更小。 rdr#
和line#
符号是语法报价的一个特征,称为“自动gensyms”,导致以#结尾的语法引号中的任何符号被唯一替换,虽然在结果表达式中使用了一致的符号。~@
是语法引用的拼接 - 非引用功能,在这种情况下会导致插入body
而不会在其周围添加( )
。 我们可以看到macroexpand-1
和pprint
提示如何扩展:(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]