我发现在Clojure中我很少使用
let
。不知道为什么,在我开始学习时就对它产生了反感,并一直避免使用它。当let
出现时,感觉流程停滞了。我想知道,你觉得我们能否完全不使用它?let
。不知道为什么,在我开始学习时就对它产生了反感,并一直避免使用它。当let
出现时,感觉流程停滞了。我想知道,你觉得我们能否完全不使用它?let 有几个好处。首先,它允许在函数上下文中进行值绑定。其次,它提供了可读性的好处。因此,虽然从技术上讲,你可以不使用它(即你仍然可以编写程序而不使用它),但如果没有这个有价值的工具,该语言将变得贫乏。
let 的一个好处是,它帮助规范了一种常见的(数学)指定计算的方式,其中您引入方便的绑定,然后作为结果给出简化的公式。显然,这些绑定只适用于该“范围”,并且它与更数学的公式的结合非常有用,特别是对于更多的函数式程序员。
let 块在其他语言如 Haskell 中出现也并非巧合。
您可以用((fn [a1 a2 ...] ...) b1 b2 ...)
替换任何出现的(let [a1 b1 a2 b2...] ...)
,所以是的,我们可以这样做。虽然我经常使用let,但我不想没有它。
(let [a1 b1 a2 b2] ...)
最好通过(((fn [a1] (fn [a2] ...)) b1) b2)
进行解释。我的意思是,您可以在后续的绑定中引用先前的绑定。_编辑:括号,我们都喜欢它们。 - Jan在宏中防止多次执行方面,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...;} ...
(... (let [a (...)
b (...)...]
(...a...b...a...b...) ;; still fp style
(let [a1 b1 a2 b2] (someexpression a1 a2))
这样的东西有点命令式。我不同意这一点。由于let在某种程度上等同于(let [a1 b1] ...) by ((fn [a1] ...) b1)
(参见cubics的回答),因此应该清楚地看到let是一个函数式的概念。你似乎引用了“命令式”编程的“串行评估”定义,但是let和嵌套函数调用一样是串行调用的。当然,我也看到了对于FP新手来说,他们倾向于使用let-heavy风格的观点。 - wirrbel至少有两个重要的用例需要使用let
绑定:
首先,正确使用let
可以使您的代码更清晰、更简短。如果您有一个表达式使用超过一次,将其绑定在let
中非常好。这是标准函数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)))))))
...
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
let' 胜过 'pure' 函数式。
'
let
的方法。 - sloth