Recursion 如何根据reduce定义map、filter和reverse等操作?

Recursion 如何根据reduce定义map、filter和reverse等操作?,recursion,clojure,functional-programming,reduce,fold,Recursion,Clojure,Functional Programming,Reduce,Fold,在本博客中,作者陈述: 首先,我们必须认识到许多数组(或其他集合)操作,如map、filter和reverse可以根据reduce定义 我的问题是:如何根据reduce定义map、filter和reverse等操作?您能提供Clojure中的示例吗?如果我们不关心懒惰,这是真的。在Clojure中,map和filter是懒惰的,而reduce是急切的。不仅reverse不懒惰,而且标准定义使用reduce。以惰性为模,我们可以得到其他的等效结果: user> (defn eager-map

在本博客中,作者陈述:

首先,我们必须认识到许多数组(或其他集合)操作,如
map
filter
reverse
可以根据
reduce
定义


我的问题是:如何根据reduce定义map、filter和reverse等操作?您能提供Clojure中的示例吗?

如果我们不关心懒惰,这是真的。在Clojure中,
map
filter
是懒惰的,而reduce是急切的。不仅
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)

编辑以识别
mapv
filterv


标准<代码>反向是根据<代码>减少定义的:

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

这就是说,
reduce
可以实现和,
map
filter
的热切类比

(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))))

如果我们在向量中累积,我们可以不使用
反向
s和
向量
s:

(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
的实现方式

如何根据reduce定义map、filter和reverse等操作

这就是我们所知道的<代码>折叠以下是自然折叠(foldr):

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

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 :: (a -> Bool) -> ([a] -> [a])
filter p = fold (\x xs -> if p x then x : xs else xs) []
甚至
向左折叠

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

参考资料:

  • ,格雷厄姆·赫顿,1999年
  • ,在这里

  • 这是一个奇怪的地方使用
    concat
    ,为了减少您选择自己的基本情况,所以选择一个具有所需
    conj
    行为的。为什么
    conj
    用于反转,而
    cons
    (和外部反转)用于映射/过滤器?@user2864740 in
    reverse
    (defn conj[coll x]…)
    的参数顺序正确,适用于
    reduce
    cons
    需要包装在一个函数中,并反转其参数:
    #(cons%2%1)
    。在
    map
    中,
    reduce
    中的
    cons
    会叠加结果,从而反转结果,因此需要再次反转。这同样适用于
    过滤器
    。Clojure的习惯用法是在这种情况下使用向量-避免需要反转产品。我已经附加了这样做的版本。请注意,如果有人对它们使用
    conj
    disj
    ,这些将以序列形式返回。@user2864740上次编辑使我的部分答复无效。很荣幸您能回答我的问题。Haskell的人为了接触Clojure社区所做的所有工作都很棒。我现在正在通过RWH工作,直到第7通道。@hawkeye什么工作,hawkeye?我很好奇。是Clojure Haskell的斗牛犬吗?就像赫胥黎是达尔文的斗牛犬一样。@Thumbnail.“.对于纯函数代码还没有决定,但尽管如此,他还是全心全意地支持函数编程…”似乎这个类比是可行的;-)