我发现我很少let
在 Clojure 中使用。出于某种原因,当我开始学习时,我不喜欢它,并且从那以后一直避免使用它。来的时候,感觉就像流动停止了一样let
。我想知道,你认为我们可以完全没有它吗?
6 回答
让我们提供一些好处。首先,它允许在函数上下文中进行值绑定。其次,它具有可读性优势。因此,虽然从技术上讲,人们可以取消它(从某种意义上说,你仍然可以在没有它的情况下进行编程),但如果没有有价值的工具,这种语言就会变得贫乏。
let 的好处之一是它有助于形式化一种指定计算的通用(数学)方式,在这种方式中,您引入了方便的绑定,然后是一个简化的公式。很明显,绑定仅适用于该“范围”,并且与更数学的公式相关联是有用的,尤其是对于更多功能的程序员。
让块出现在 Haskell 等其他语言中并非巧合。
您可以替换任何出现的(let [a1 b1 a2 b2...] ...)
by((fn [a1 a2 ...] ...) b1 b2 ...)
所以是的,我们可以。虽然我经常使用 let ,但我宁愿不要没有它。
在防止宏中多次执行方面,Let 对我来说是必不可少的:
(defmacro print-and-run [s-exp]
`(do (println "running " (quote ~s-exp) "produced " ~s-exp)
s-exp))
会运行 s-exp 两次,这不是我们想要的:
(defmacro print-and-run [s-exp]
`(let [result# s-exp]
(do (println "running " (quote ~s-exp) "produced " result#)
result#))
通过将表达式的结果绑定到名称并引用该结果两次来解决此问题。
因为宏返回的表达式将成为另一个表达式的一部分(宏是生成 s 表达式的函数),所以它们需要生成本地绑定以防止多次执行并避免符号捕获。
我想我理解你的问题。如果有错请纠正我。有时“let”用于命令式编程风格。例如,
... (let [x (...)
y (...x...)
z (...x...y...)
....x...y...z...] ...
这种模式来自命令式语言:
... { x = ...;
y = ...x...;
...x...y...;} ...
你避免这种风格,这就是为什么你也避免“让”,不是吗?
在某些问题中,命令式风格会减少代码量。此外,有时用 java 或 c 编写效率更高。同样在某些情况下,“let”只保存子表达式的值,而不管评估顺序如何。例如,
(... (let [a (...)
b (...)...]
(...a...b...a...b...) ;; still fp style
-bindings至少有两个重要的用例let
:
首先,let
正确使用可以让你的代码更清晰、更短。如果你有一个多次使用的表达式,将它绑定在 alet
中是非常好的。map
这是使用的标准函数的一部分let
:
...
(let [s1 (seq c1) s2 (seq c2)]
(when (and s1 s2)
(cons (f (first s1) (first s2))
(map f (rest s1) (rest s2)))))))
...
即使您只使用一次表达式,给它一个语义上有意义的名称仍然是有帮助的(对代码的未来读者)。
其次,正如 Arthur 所提到的,如果您想多次使用表达式的值,但只想对其进行一次评估,则不能简单地将整个表达式输入两次:您需要某种绑定。如果您有一个纯表达式,这将只是浪费:
user=> (* (+ 3 2) (+ 3 2))
25
但如果表达式有副作用,实际上会改变程序的含义:
user=> (* (+ 3 (do (println "hi") 2))
(+ 3 (do (println "hi") 2)))
hi
hi
25
user=> (let [x (+ 3 (do (println "hi") 2))]
(* x x))
hi
25
最近偶然发现了这个,所以运行了一些时间:
(testing "Repeat vs Let vs Fn"
(let [start (System/currentTimeMillis)]
(dotimes [x 1000000]
(* (+ 3 2) (+ 3 2)))
(prn (- (System/currentTimeMillis) start)))
(let [start (System/currentTimeMillis)
n (+ 3 2)]
(dotimes [x 1000000]
(* n n))
(prn (- (System/currentTimeMillis) start)))
(let [start (System/currentTimeMillis)]
(dotimes [x 1000000]
((fn [x] (* x x)) (+ 3 2)))
(prn (- (System/currentTimeMillis) start)))))
Output
Testing Repeat vs Let vs Fn
116
18
60
“让”胜过“纯”功能。