【问题标题】:Clojure def vs defn for a function with no argumentsClojure def vs defn 用于没有参数的函数
【发布时间】:2011-05-26 13:39:00
【问题描述】:

我在 clojure 中编写了一个程序,但有些函数没有参数。将此类函数编码为“def”而不是不带参数的“defn”有什么好处?

【问题讨论】:

  • 您是指(def x (fn [] (something))),还是(def x (something))?它们完全不同,接受的答案只对第二种解释是正确的。
  • 我猜是第二个解释,但很好!

标签: clojure


【解决方案1】:
(def t0 (System/currentTimeMillis))
(defn t1 [] (System/currentTimeMillis))
(t1)
;; => 1318408717941
t0
;; => 1318408644243
t0
;; => 1318408644243
(t1)
;; => 1318408719361

【讨论】:

  • 谢谢。这个代码示例的价值超过了 1000 字的描述。
  • 所以没有人会错过这个关键点:正如 Roman Bataev 的评论所暗示的那样,(def t2 (fn [] (System/currentTimeMillis))) 将使t2 的行为类似于t1
  • def 将值绑定到名称,defn 定义函数并将其绑定到名称。更多详情defn vs fn
【解决方案2】:

defs 只被评估一次,而defns(带或不带参数)在每次调用时都会被评估(执行)。因此,如果您的函数总是返回相同的值,您可以将它们更改为 defs,否则不能。

【讨论】:

  • 啊,我不知道这个,你救了我一些严重的错误!那么如果有一个 def 访问一个引用,它就永远不会被重新计算?如果重新加载文件怎么办?
  • def 在重新加载文件时重新计算。
  • 我说defn 表单被重新评估似乎是错误的。正如其他答案中所指出的,defn 在宏扩展时解析为 def,并且它们确实在文件加载时只被评估一次。
  • @MattFenwick skuro 的观点是 defns 只被评估一次,就像每个 def 一样 - 在这种情况下,它定义了一个函数。但是,定义的函数的 body 可以被评估任意次数。我不认为这是一个有用的区别,但他并没有说任何虚假的话。
  • 我将问题读作(def x (fn [] (do-something-here))),而您似乎将其解释为(defn x [] (do-something-here)。如果 OP 足够具体,可以知道哪种解释是正确的,那就太好了。
【解决方案3】:

(defn name ...) 只是一个变成 (def name (fn ...) 的宏,不管它有多少参数。所以它只是一个快捷方式。有关详细信息,请参阅 (doc defn)。

https://clojure.org/guides/learn/functions#_defn_vs_fn

【讨论】:

  • 这是最简洁的答案!如果有人用谷歌搜索“defn vs def”,我希望他能很快找到这个线程。谢谢!
【解决方案4】:

def 特殊形式创建一个 Var 对象,该对象由作为其第一个参数给出的符号标识。标识是通过将给定符号与名为命名空间的映射中的 Var 相关联来创建的。

Var 持有对某个值的引用,可以表示为(以及其他):

  • 作为一个常量形式,总是计算出自己的值
(def x 1)
x
;; => 1             ; x holds a reference to a number 1
  • 作为一个函数形式,首先被评估为其结果值
(def x (+ 2 2))
x
;; => 4             ; x holds a reference to a number 4
  • 作为一种 Java 方法形式,首先被评估为其结果值
(def x (System/currentTimeMillis))
x
;; => 1417811438904 ; x holds a reference to a number 1417811438904
x
;; => 1417811438904 ; still the same number!
  • 作为 lambda 形式(匿名函数),首先被评估为函数对象
(def x (fn [] (System/currentTimeMillis)))
x
;; => #<user$x user$x@4c2b1826>
(x)                             ; function form, function evaluated
;; => 1417811438904
(x)                             ; function form, function evaluated
;; => 1417812565866

以上所有内容都有一个简单的规则。在def 特殊形式的情况下,作为其第二个参数给出的 S 表达式 递归评估 在创建绑定之前,因此生成的 Var 绑定到 this 的结果评估。

即使fn 之前也被评估过,但其结果值是一个包含代码的函数对象。 每次调用函数时都会执行(和评估)此代码。这就是为什么会有不同的结果。

defn 宏就像def,但在内部它创建一个匿名函数,然后将一个 Var 对象绑定到它。它的第二个参数成为这个函数的一个主体,它不是以“常规”方式评估的。也可以说它是被评估的,但它是一种 lambda 形式——评估的结果是一个函数对象,而不是一些即时计算的结果。

所以写:

(defn fun [] 1)

是同义词:

(def fun (fn [] 1))

【讨论】:

  • 优秀的答案。应该是公认的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-15
  • 1970-01-01
  • 2017-10-09
  • 2017-10-12
  • 2017-04-22
  • 2010-10-11
  • 2013-06-23
相关资源
最近更新 更多