在此博客條目,"CSP and transducers in JavaScript",筆者指出:如何定義映射,過濾器和反轉等操作?
首先,我們必須認識到,許多陣列(或其他集合),如
map
,filter
和reverse
操作可以在一個reduce
來定義。
我的問題是:如何能像地圖,過濾器和相反的操作可以在一個減少來定義?你能提供Clojure的例子嗎?
在此博客條目,"CSP and transducers in JavaScript",筆者指出:如何定義映射,過濾器和反轉等操作?
首先,我們必須認識到,許多陣列(或其他集合),如
map
,filter
和reverse
操作可以在一個reduce
來定義。
我的問題是:如何能像地圖,過濾器和相反的操作可以在一個減少來定義?你能提供Clojure的例子嗎?
這是真的,如果我們不關心懶惰。在Clojure中,map
和filter
是懶惰的,但減少是渴望的。 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)
謝謝你指出。 – noisesmith 2014-09-04 15:24:16
編輯承認mapv
和filterv
。
標準reverse
在的reduce
來定義:
(defn reverse [coll]
(reduce conj() coll))
map
和filter
懶惰,所以可以在無限的順序進行操作。沒有辦法與reduce
做到這一點。
話雖這麼說,reduce
可以實現mapv
和filterv
的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))))
如果我們在矢量積累,我們可以不reverse
S和vec
就做:
(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
實現。
爲什麼'conj'用於反轉,但'cons'(和外部反轉)用於地圖/過濾器? – user2864740 2014-09-03 23:10:16
@ user2864740在'reverse'中,'(defn conj [coll x] ...)'有'reduce'的正確順序。 'cons'需要被包裝在一個反轉其參數的函數中:'#(cons%2%1)'。在'map'中,'reduce'內部的'cons'疊加 - 因此反轉 - 結果,所以需要再次顛倒。 'filter'也是如此。 Clojure習慣用法是在這種情況下使用向量 - 避免需要扭轉產品。我附加了這樣做的版本。請注意,這些會作爲序列返回,以防有人對它們使用「conj」或「disj」。 – Thumbnail 2014-09-03 23:32:51
@ user2864740上次編輯使我的一些回覆無效。 – Thumbnail 2014-09-04 17:38:06
如何定義映射,過濾器和反轉等操作可以通過reduce來定義?
這就是所謂的"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
參考文獻:
很榮幸能回答我的問題。 Haskell的所有工作都是爲了接觸Clojure社區而做的。我目前正在通過RWH工作 - 達到第7章。 – hawkeye 2014-09-04 10:29:55
@hawkeye什麼工作,hawkeye?我很好奇。就像赫胥黎是達爾文的鬥牛犬一樣,Clojure Haskell是牛頭犬。 – Thumbnail 2014-09-04 17:46:56
@Thumbnail「..關於純功能代碼的決定,但儘管如此,他在功能性編程的公衆支持中卻是全心全意的......」 - 似乎這種比喻可以奏效;-) – user2864740 2014-09-04 20:19:26
這是一個奇怪的地方使用'concat',在減少你選擇你自己的基本情況下,所以選擇一個'conj'行爲需要的。 – noisesmith 2014-09-03 23:03:57