代码之家  ›  专栏  ›  技术社区  ›  Belun

defn和defmacro有什么区别?

  •  32
  • Belun  · 技术社区  · 14 年前

    5 回复  |  直到 8 年前
        1
  •  62
  •   sepp2k    14 年前

    defn 定义函数, defmacro 定义宏。

    函数和宏的区别在于,在函数调用时,首先计算函数的参数,然后使用参数计算函数体。

    另一方面,宏描述了从一段代码到另一段代码的转换。任何评估都发生在转换之后。

    or 是宏。如果 如果是函数,则这是不可能的,因为参数总是在函数运行之前进行求值。

    mymacro 以至于 (mymacro (12 23 +)) (+ 23 12) (12 23 +) 单凭这一点就是胡说八道。你不能用函数来做因为 (12 23 +)

    一个小例子来说明不同之处:

    (defmacro twice [e] `(do ~e ~e))
    (twice (println "foo"))
    

    twice 获取列表 (println "foo") 作为论据。然后将其转换为列表 (do (println "foo") (println "foo"))

    (defn twice [e] `(do ~e ~e))
    (twice (println "foo"))
    

    在这里 println "foo" 立即评估。自 println nil ,两次调用 作为它的论据。 两次 现在生成列表 (do nil nil) (不做不做) 不是作为代码计算的,只是作为列表处理。

        2
  •  38
  •   Arthur Ulfeldt    14 年前

    • 功能 变换 值转换为其他值 .
      (reduce + (map inc [1 2 3])) => 9

    • 变换 代码转换为其他代码
      (-> x a b c) => (c (b (a x))))

        3
  •  12
  •   John Lawrence Aspden    14 年前

    宏就像一个学徒程序员,你可以给他写笔记:

    有时候,如果我试着调试一些东西,我喜欢改变一些东西,比如

    (* 3 2)
    

    变成这样:

    (let [a (* 3 2)] (println "dbg: (* 3 2) = " a) a)
    

    刚刚评估过,它的值,以及返回的值作为结果

    这可能非常有用,但它很耗时,而且容易出错。你可以想象

    你可以编写编译器来为你做这些事情,而不是雇佣学徒。

    ;;debugging parts of expressions
    (defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
    

    现在试试:

    (* 4 (dbg (* 3 2)))
    

    作为一台计算机,它为变量选择不可读的名称,而不是我会选择的“a”。

    我们可以问它对一个给定的表达式有什么作用:

    (macroexpand '(dbg (* 3 2)))
    

    这就是它的答案,所以你可以看到它真的在为你重写代码:

    (let* [x__1698__auto__ (* 3 2)]
          (clojure.core/println "dbg:" (quote (* 3 2)) "=" x__1698__auto__)
          x__1698__auto__)
    

    试着写一个函数dbgf做同样的事情,你会有问题,因为(dbgf(*3 2))->(dbgf 6)在dbgf被调用之前,所以不管dbgf做什么,它都不能恢复它需要打印出来的表达式。

    尝试在有副作用和值的表达式上使用它,如

    (dbg (print "hi"))
    

    事实上,宏是如此之好,以至于我们已经准备好使用LISPs的(brackety((syntax)),以便获得它们。(虽然我必须说我喜欢它是因为它本身(但是我有点奇怪)。

    C也有宏,它们的工作方式大致相同,但它们总是出错,为了使它们正确运行,您需要在程序中放入许多括号,使其看起来像LISP!

    实际上,我们建议您不要使用C的宏,因为它们非常容易出错,尽管我看到真正了解它们的人使用它们时效果非常好。

    基础语言非常简单,因此易于实现,然后使用宏构建复杂的上层结构。

    我真希望这有帮助。这比我通常的回答要长,因为你问了一个很深的问题。祝你好运。

        4
  •  3
  •   sleepynate    14 年前

    一个人不发出刺耳的声音,就创造了一个 ,而另一个创建

    (defmacro lazy-cat
      "Expands to code which yields a lazy sequence of the concatenation
      of the supplied colls. Each coll expr is not evaluated until it is
      needed.
    
      (lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))"
      {:added "1.0"}
      [& colls]
      `(concat ~@(map #(list `lazy-seq %) colls)))
    

    实际上会扩展到

    `(concat ~@(map #(list `lazy-seq %) colls)))
    

    其中 lazy-seq 然后将进一步扩展到

    (list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
    

    在实际处理传递给他们的数据之前。

    这里有一个非常可爱的故事,可以帮助解释不同之处(后面是一些有用的例子) Practical Common Lisp: Chapter 8

        5
  •  0
  •   jcubic    14 年前

    defn定义函数,defmacro定义宏。
    宏类似于一个函数,但将它的参数(如果它们是表达式,则将其视为数据)处理,然后返回数据(代码中的符号列表),然后计算返回代码。所以它需要用一个代码替换另一个代码(在编译时)。