2012-04-27 63 views
1

我讀了很多關於Clojure的文檔(並且需要再次閱讀它),並閱讀了幾篇關於這個Clojure的問題以獲得對該語言的「感覺」。除了elisp中的一些小函數之外,我從來沒有用過任何Lisp語言編寫過。我在Clojure編寫了我的第一個項目Euler解決方案,在進一步深入之前,我想更好地瞭解一下地圖減少什麼在使用地圖時返回一個集合

使用Lambda,我結束了以下(總結3或5或同時1和1000(含)之間的所有倍數):

(reduce + (map #(if (or (= 0 (mod %1 3)) (= 0 (mod %1 5))) %1 0) (range 1 1000))) 

我把它放在同一行,因爲我寫的在REPL上(它給出了正確的解決方案)。

沒有拉姆達,我寫了這個:

(defn val [x] (if (or (= 0 (mod x 3)) (= 0 (mod x 5))) x 0)) 

然後我計算解決方案這樣做:

(reduce + (map val (range 1 1000))) 

在這兩種情況下,我的問題是關於地圖做之前應該返回什麼, 減少。在做完地圖後,我注意到我最後列出了一個如下所示的列表:(0 0 3 0 5 6 ...)

我試圖在VAL定義的末尾去除「0」但我接收由列表(零零零3 5 6等)。我不知道是否有問題。無論如何,我發現我會在做一個摺疊的時候總結一下,所以零不是真正的問題。

但仍然:什麼是一個合理的地圖返回? (0 0 3 0 5 6 ...)或(無零3無5 6 ...)或(3 5 6 ...)(我將如何處理這最後一個?)或其他?

我應該「過濾」零點/零點,如果是的話如何?

我知道我在問一個基本問題,但map/reduce顯然是我會用的很多,所以任何幫助都是值得歡迎的。

回答

2

這聽起來像你已經有一個直覺的undetermined需要單獨映射的關注形式減少有一個非常自然的地圖產生的數據不被減少使用。事實上使用零是添加的身份值的事實使這更加優雅。

  • 映射工作是產生新的數據(在這種情況下,3 5或「忽略」)
  • 減少工作是決定如何處理包括併產生最終的結果。

你開始如何處理是慣用的Clojure,沒有必要給它的任何更復雜的是, 所以接下來的例子只是爲了說明其映射決定如何包括點:

(reduce #(if-not (zero? %1) (+ %1 %2) %2) (map val (range 10))) 

在這個人爲設計的例子中,reduce函數忽略了零點。在典型的現實世界中的代碼,如果當時的想法是爲過濾掉一些值,那麼人們往往只使用filter功能

(reduce + (filter #(not (zero? %)) (map val (range 10)))) 

你也可以開始過濾器和跳過地圖一樣簡單:

(reduce + (filter #(or (zero? (rem % 3)) (zero? (rem % 5))) (range 10))) 
+0

+1 ......我不知道直觀:)我只是想多讀文檔,盡我所能,但隨後的問題是,我知道我已經忘了幾個事情,我讀:)讓我們回到映射產生的數據,如果我有一個情況,我不希望零(在這種情況下罰款),也不在輸出零?在ouptut集合中「過濾」不需要的元素的常見Clojure方法是什麼?只需使用* filter *? – 2012-04-27 20:43:54

+0

是的,如果發現你自己說,喔,我可以「只使用X」你可能在正確的軌道 – 2012-04-27 20:53:58

+0

@ArthurUlfeldt在何種意義上減少「決定如何處理包括」? – Thumbnail 2014-04-06 09:31:51

0

在你的情況我會使用keep代替map。它與map類似,但它只保留非零值。

+0

我認爲['filter'](http://clojuredocs.org/clojure_core/clojure.core/filter)將更好地工作,因爲'(或(= 0(MOD×3))(= 0(MOD×5) ))''會在'nil'失敗時返回'false'。 – huon 2012-04-28 12:04:33

+0

@dbaupp切換「else」--condition返回'nil'將啓用我的解決方案或從'if'切換到'when'。 – ponzao 2012-04-28 12:50:19

+1

但它不允許像'(filter#(or(= 0(mod%3))(= 0(mod%5)))...)'這樣輕鬆地寫出它。 (我想說的是'keep'有它的用途,但'filter'是適合在這裏使用的函數:)) – huon 2012-04-28 12:52:41

1

的口號是清晰度。

  • 使用filter而不是map。那麼你不必選擇一個你以後不得不決定不採取行動的空值。
  • 命名過濾/映射函數可以提供幫助。與letletfn,不defn這樣做,除非你有其他地方的功能使用。

代理此建議給我們帶來...

(let [divides-by-3-or-5? (fn [n] (or (zero? (mod n 3)) (zero? (mod n 5))))] 
    (reduce + (filter divides-by-3-or-5? (range 1 1000)))) 

您可能要停在這裏現在。

這讀起來很好,但divides-by-3-or-5?功能粘在喉嚨裏。改變因素,我們需要一個全新的功能。那個重複的短語(zero? (mod n ...))罐子。所以...

我們想要一個函數,給出一個可能因素的列表(或其他集合) - 告訴我們它們中的任何一個是否適用於給定的數字。換句話說,我們希望

  • 數字集合的功能 - 可能的因素 - ...
  • 返回一個功能一個數字 - 候選人 - ...
  • 那告訴我們候選人是否可以被任何可能的因素整除。

一個功能是

(fn [ns] (fn [n] (some (fn [x] (zero? (mod n x))) ns))) 

...我們可以利用這樣

(let [divides-by-any? (fn [ns] (fn [n] (some (fn [x] (zero? (mod n x))) ns)))] 
    (reduce + (filter (divides-by-any? [3 5]) (range 1 1000)))) 

  • 這種 「改進」 取得了一點程序慢。
  • divides-by-any?可能證明是足夠有用的提升爲 defn
  • 如果操作是至關重要的,你可以考慮剝離出來 多餘的因素。例如,[2 3 6]可以縮減爲[6]
  • 如果操作是真的關鍵,因素提供 常數,你可以考慮用 宏回到使用or創建過濾器功能。

這是有點shaggy-dog story,但它敘述了你所指的問題提示的想法。

相關問題