如何通过减少来定义map,filter和reverse等操作?

时间:2014-09-03 22:36:39

标签: recursion clojure functional-programming reduce fold

在此博客文章"CSP and transducers in JavaScript"中,作者声明:

  

首先,我们必须意识到,mapfilterreverse等许多数组(或其他集合)操作都可以用reduce来定义。< / p>

我的问题是:如何通过缩减来定义地图,过滤器和反向等操作?您能否在Clojure中提供示例?

3 个答案:

答案 0 :(得分:5)

已修改以识别mapvfilterv


标准reverse 按<{1}}定义

reduce

(defn reverse [coll] (reduce conj () coll)) map是懒惰的,因此可以对无限序列进行操作。使用filter无法执行此操作。

话虽如此,reduce可以实现reducemapv,这是mapfilter的热切类比。

(defn mapv [f coll]
  (vec (reverse (reduce (fn [acc x] (cons (f x) acc)) () coll))))

(defn filterv [pred coll]
  (vec (reverse (reduce (fn [acc x] (if (pred x) (cons x acc) acc)) () coll))))

如果我们在向量中累积,我们可以不使用reversevec

(defn mapv [f coll]
  (reduce (fn [acc x] (conj acc (f x))) [] coll))

(defn filterv [pred coll]
  (reduce (fn [acc x] (if (pred x) (conj acc x) acc)) [] coll))

这最后几乎是标准filterv的实现方式。

答案 1 :(得分:5)

  

如何根据reduce?

来定义map,filter和reverse等操作

这被称为"universality of fold"。下面fold是自然折叠(foldr):

显然,可以通过折叠来描述各种减少:

sum :: [Int] -> Int           product :: [Int] -> Int
sum = fold (+) 0              product = fold (*) 1

and :: [Bool] -> Bool         or :: [Bool] -> Bool
and = fold (&&) True          or = fold (||) False

但我们也可以写出非明显的减少:

-- appending a list
(++) :: [a] -> [a] -> [a]
(++ ys) = fold (:) ys

-- reversing a list
reverse :: [a] -> [a]
reverse = fold (\x xs -> xs ++[x]) []

map一般:

map :: (a -> b) -> ([a] -> [b])
map f = fold (\x xs -> f x : xs) []

filter

filter :: (a -> Bool) -> ([a] -> [a])
filter p = fold (\x xs -> if p x then x : xs else xs) []

甚至fold left

foldl f v xs = fold (\x g -> (\a -> g (f a x))) id xs v

参考文献:

  1. A tutorial on the universality and expressiveness of fold,Graham Hutton,1999。
  2. Writing foldl using foldr,此处。

答案 2 :(得分:3)

如果我们不关心懒惰,这是事实。在Clojure中,mapfilter是懒惰的,但减少是渴望的。 reverse不仅不是懒惰,而且标准定义使用reduce。模数懒惰,我们可以得到与其他人相同的结果:

user> (defn eager-map [f coll]
        (reduce (fn [acc v] (conj acc (f v)))
        []
        coll))
#'user/eager-map
user> (eager-map inc (range 10))
[1 2 3 4 5 6 7 8 9 10]

user> (defn eager-filter [f coll]
         (reduce (fn [acc v] (if (f v) (conj acc v) acc))
                 []
                 coll))
#'user/eager-filter
user> (eager-filter even? (range 10))
[0 2 4 6 8]

user> (defn eager-reverse [coll]
         (reduce conj () coll))
#'user/eager-reverse
user> (eager-reverse (range 10))
(9 8 7 6 5 4 3 2 1 0)