2014-09-03 56 views
2

在此博客條目,"CSP and transducers in JavaScript",筆者指出:如何定義映射,過濾器和反轉等操作?

首先,我們必須認識到,許多陣列(或其他集合),如mapfilterreverse操作可以在一個reduce來定義。

我的問題是:如何能像地圖,過濾器和相反的操作可以在一個減少來定義?你能提供Clojure的例子嗎?

+1

這是一個奇怪的地方使用'concat',在減少你選擇你自己的基本情況下,所以選擇一個'conj'行爲需要的。 – noisesmith 2014-09-03 23:03:57

回答

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) 
+0

謝謝你指出。 – noisesmith 2014-09-04 15:24:16

5

編輯承認mapvfilterv


標準reverse在的reduce來定義

(defn reverse [coll] 
    (reduce conj() coll)) 

mapfilter懶惰,所以可以在無限的順序進行操作。沒有辦法與reduce做到這一點。

話雖這麼說,reduce可以實現mapvfiltervmapfilter渴望的類似物。

(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實現。

+0

爲什麼'conj'用於反轉,但'cons'(和外部反轉)用於地圖/過濾器? – user2864740 2014-09-03 23:10:16

+0

@ 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

+0

@ user2864740上次編輯使我的一些回覆無效。 – Thumbnail 2014-09-04 17:38:06

5

如何定義映射,過濾器和反轉等操作可以通過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 

參考文獻:

  1. A tutorial on the universality and expressiveness of fold,格雷厄姆赫頓,1999
  2. Writing foldl using foldr,在這裏。
+0

很榮幸能回答我的問題。 Haskell的所有工作都是爲了接觸Clojure社區而做的。我目前正在通過RWH工作 - 達到第7章。 – hawkeye 2014-09-04 10:29:55

+1

@hawkeye什麼工作,hawkeye?我很好奇。就像赫胥黎是達爾文的鬥牛犬一樣,Clojure Haskell是牛頭犬。 – Thumbnail 2014-09-04 17:46:56

+0

@Thumbnail「..關於純功能代碼的決定,但儘管如此,他在功能性編程的公衆支持中卻是全心全意的......」 - 似乎這種比喻可以奏效;-) – user2864740 2014-09-04 20:19:26