如果您的表达式链很长,请使用let
. 长的失控表达式或深度嵌套的表达式在任何语言中都不是特别可读的。这是不好的:
(do-something (map :id (filter #(> (:age %) 19) (fetch-data :people))))
这稍微好一点:
(do-something (map :id
(filter #(> (:age %) 19)
(fetch-data :people))))
但这也很糟糕:
fetch_data(:people).select{|x| x.age > 19}.map{|x| x.id}.do_something
如果我们正在阅读本文,我们需要知道什么?我们正在do_something
调用people
. 这段代码很难阅读,因为第一个和最后一个之间的距离太远,以至于我们在它们之间旅行时忘记了我们在看什么。
在 Ruby 的情况下,do_something
(或任何产生我们最终结果的东西)在行尾迷失了方向,所以很难说出我们在对我们的people
. 在 Clojure 的例子中,我们正在做的事情是显而易见的do-something
,但是如果不通读整个事情的内部,就很难知道我们在做什么。
任何比这个简单示例更复杂的代码都会变得非常痛苦。如果你所有的代码都像这样,你的脖子会累得在所有这些意大利面条上来回扫描。
我更喜欢这样的东西:
(let [people (fetch-data :people)
adults (filter #(> (:age %) 19) people)
ids (map :id adults)]
(do-something ids))
现在很明显:我从 开始people
,我到处乱跑,然后我do-something
对他们。
你可能会逃脱惩罚:
fetch_data(:people).select{|x|
x.age > 19
}.map{|x|
x.id
}.do_something
但至少我可能更愿意这样做:
adults = fetch_data(:people).select{|x| x.age > 19}
do_something( adults.map{|x| x.id} )
let
即使您的中间表达没有好名字,使用它也并非闻所未闻。(这种风格偶尔会用在 Clojure 自己的源代码中,例如 的源代码defmacro
)
(let [x (complex-expr-1 x)
x (complex-expr-2 x)
x (complex-expr-3 x)
...
x (complex-expr-n x)]
(do-something x))
这对调试有很大帮助,因为您可以通过以下方式随时检查事物:
(let [x (complex-expr-1 x)
x (complex-expr-2 x)
_ (prn x)
x (complex-expr-3 x)
...
x (complex-expr-n x)]
(do-something x))