为什么人们更喜欢像
(for [x '(1 2 3)] (* 2 x))
这样的列表推导式,而不是(map #(* %1 2) '(1 2 3))
?
这种编程方式有什么好处吗?
1. 是否更易读?
2. 在某些情况下是否更快?
3. 是否更适合某些操作和数据结构?
为什么人们更喜欢像
(for [x '(1 2 3)] (* 2 x))
这样的列表推导式,而不是(map #(* %1 2) '(1 2 3))
?
这种编程方式有什么好处吗?
1. 是否更易读?
2. 在某些情况下是否更快?
3. 是否更适合某些操作和数据结构?
对于你给出的例子,没有任何好处;但是通常情况下,for
在连接两个(或多个)序列或进行过滤时非常有用 - 带有 :let
和 :when
的 for
通常比嵌套的 map
和 filter
更易读。
首先,列表推导只是语法糖,因此除了可读性之外,不应该有任何固有的区别。
但请注意,列表推导实际上将至少三个高阶函数(即map
、filter
和concatMap
)统一为一个语法。因此,在需要这些函数的复杂组合的非平凡情况下,该语法可以具有很大的可读性优势。
此外,我们可以方便地使用值绑定来存储结果并使事情更加清晰:
[ (x, y) | x <- [1..10],
y <- [1..10],
let dist = (x - 5)² + (y - 5)²,
dist < 10² ]
无论如何,列表推导式可以比仅处理列表更加通用。基本上,它们可以使用统一和方便的语法处理任何Monad
。例如,F#甚至将其扩展到控制任意程序流。
其次,比通常的高阶函数更抽象,列表推导式实际上可能会更快,因为编译器可以利用优化规则,否则需要程序员考虑。
[ x + 1 | x <- numbers, even x ]
直接的翻译是
map (\x -> x + 1) (filter even x)
在这里,列表被迭代了两次,产生了一个毫无意义的中间结果。
然而,编译器可以识别并优化上述模式,并生成直接的、优化的filterMap
版本。
列表推导式用于需要以更复杂的方式迭代序列的情况。在您的示例中,它们是相同的,因此我建议使用map,因为它更易于阅读。
对于这个例子,使用map会变得很丑陋。
(for [x '(1 2 3) y '(3 2 1)] (* 2 x y))
我知道这是一个Clojure问题,但是马丁·奥德斯基等人的书《Scala编程》对for
推导式如何映射到map
和filter
函数有一个不错的讨论。
map
无法做到,但mapcat
可以。一般来说,可以用map
、mapcat
、filter
和take-while
的某种组合来替换列表推导式。对于更复杂的情况,结果可能很难阅读。 - Pavel Minaev