关于Lisp:Clojure中的Let vs def

let vs def in clojure

我想在clojure程序中创建Java Scanner类的本地实例。 为什么这不起作用:

1
2
; gives me:  count not supported on this type: Symbol
(let s (new Scanner"a b c"))

但这会让我创建一个如下的全局实例:

1
(def s (new Scanner"a b c"))

我的印象是唯一的区别是范围,但显然不是。 letdef有什么区别?


问题是您对let的使用是错误的。

让我们这样工作:

1
(let [identifier (expr)])

因此,您的示例应如下所示:

1
2
(let [s (Scanner."a b c")]
  (exprs))

您只能在let(开始和结束括号)的范围内使用let进行的词法绑定。让我们只创建一组词法绑定。我使用def进行全局绑定,并仅在let的范围内绑定我想要的东西,因为它可以使事物保持整洁。它们都有用途。

注意:(类)与(新类)相同,只是语法糖。


LET不是"在当前范围内建立词法绑定",而是"使用以下绑定来建立新的词法范围"。

1
2
3
4
(let [s (foo whatever)]
  ;; s is bound here
  )
;; but not here
1
2
(def s (foo whatever))
;; s is bound here


简化:def用于全局常量,let用于局部变量。


正确的语法:

1
(let [s (Scanner."a b c")] ...)

即使它们的含义相关,它们的语法也不同。

let获取绑定列表(名称/值对),后跟要在绑定上下文中求值的表达式。

def仅接受一个绑定而不是列表,并将其添加到全局上下文中。


您可以将let视为语法糖,用于使用fn创建新的词法作用域,然后立即应用它:

1
2
3
(let [a 3 b 7] (* a b))  ; 21
; vs.
((fn [a b] (* a b)) 3 7) ; 21

因此,您可以使用简单的宏和fn实现let

1
2
3
4
5
6
(defmacro fnlet [bindings & body]
  ((fn [pairs]
    `((fn [~@(map first pairs)] ~@body) ~@(map last pairs)))
   (partition 2 bindings)))

(fnlet [a 3 b 7] (* a b)) ; 21