为什么人们更喜欢列表推导式
(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
当您连接两个(或更多)序列时,或者当您需要进行一些过滤时,它很有用 - for
with :let
and:when
通常比嵌套的链更具可读性map
and filter
。
列表推导只是标准函数程序的“语法糖”,但它们可以直观地阅读列表上的常见操作。——盖伊·拉帕尔梅
他们唯一的目的是更好的可读性。不要期望通过使用它们来显着提高性能。
本文对在类似 Lisp 的语言中实现列表推导提供了一些见解。
首先,列表推导式只是语法糖,所以除了可读性之外应该没有内在的区别。
但请注意,列表推导式实际上将至少三个高阶函数,即,和统一为一种语法。因此,在需要这些复杂组合的重要情况下,语法可以具有很大的可读性优势。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
版本。
列表推导式用于需要以更复杂的方式遍历序列的情况。在您的示例中,它们是相同的,因此我会推荐地图,因为它更易于阅读。
地图不能因为这样做而变得丑陋,例如:
(for [x '(1 2 3) y '(3 2 1)] (* 2 x y))
for
我知道这是一个 Clojure 问题,但 Martin Odersky 等人的“Scala 编程”一书对理解如何映射到map
和filter
函数进行了很好的讨论。