2011-07-31 72 views
5

我是Clojure的新手,我正在做一些labrepl的基本工作,現在我想寫一個函數,用其他字母替換某些字母,例如:elosska→elößkä。通過doseq迭代地圖

我寫了這個:

(ns student.dialect (:require [clojure.string :as str])) 
(defn germanize 
    [sentence] 
    (def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) 
    (doseq [[original-letter new-letter] german-letters] 
    (str/replace sentence original-letter new-letter))) 

但我希望這是行不通的。請問你能幫幫我嗎?

回答

16

這裏是我取,

 

(def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) 

(defn germanize [s] 
    (reduce (fn[sentence [match replacement]] 
      (str/replace sentence match replacement)) s german-letters)) 

 
(germanize "elosska") 
+0

是的,這比我的更甜。非常好:-) – Scott

+0

是的,'reduce'功能更強大,功能更強大,我只是想顯示出現問題的地方... –

+0

哇,我花了一點時間來討論這個問題,這是對reduce和參數解構的巧妙運用!我可以立即在我自己的代碼中使用該技術。真的希望有一本書有這樣的功能'模式'。 – NielsK

6

這裏有2個問題:

  1. doseq不保留通過其評估創建列表的頭,所以你不會得到任何結果的文本的單獨副本
  2. str/replace作品,產生4個不同的結果 - 你可以通過用for代替doseq來檢查,你會得到4個條目的列表。

你的代碼可以下列方式改寫:

(def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) 
(defn germanize [sentence] 
    (loop [text sentence 
     letters german-letters] 
    (if (empty? letters) 
     text 
     (let [[original-letter new-letter] (first letters)] 
     (recur (str/replace text original-letter new-letter) 
       (rest letters)))))) 

在這種情況下,中間結果被收集,因此,所有的替代品應用於相同的字符串,產生正確的字符串:

user> (germanize "elosska") 
"elößkä" 

PS也不建議在功能中使用def - 最好是將它用於頂級窗體

+1

哈薩·耶卡亞函數式提供瞭解決方案 - 'reduce'是非常方便的,當你需要收集中間結果... –

6

Alex當然已經正確回答了關於使用doseq的原始問題的問題...但是我發現問題有趣並且想要看看更「功能性」的解決方案是什麼樣子。我的意思是不使用循環。

我想出了這一點:

(ns student.dialect (:require [clojure.string :as str])) 

(defn germanize [sentence] 
    (let [letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"} 
     regex (re-pattern (apply str (interpose \| (keys letters))))] 
    (str/replace sentence regex letters))) 

其產生相同的結果:

student.dialect=> (germanize "elosska") 
"elößkä" 

regex (re-pattern...行只計算爲#"ss|a|o|u",這將是更清潔和更簡單的閱讀,如果作爲明確的字符串輸入,但我認爲最好只有德國字母的一個定義。

+0

我會說,這是要走的路,特別是如果表現令人擔憂的話。它只執行一次替換並構建一個單一字符串(使用'StringBuffer'),而替換映射上的'loop'或'reduce'將爲每個替換構造一個新字符串,並且總是遍歷整個字符串從上一步開始。此外,由於所有解決方案(正確)都使用'clojure.string/replace',它是一個可以在一次調用中處理整個操作的內置函數,應該允許它處理它。另外,這基本上就像問題陳述一樣。 +1。 –

+0

順便說一句,我在上面假設替換從未引入需要進一步處理的模式。在這裏就是這種情況,但如果不是這樣,那麼這將是一個完全不同的問題,需要進一步澄清(例如,整個操作是否意味着冪等性?如果不是,應該如何定製模式/替換對呢?它仍然是可以使用地圖來保存它們嗎?)。就在旁邊...... :-) –

+0

@Michał感謝您的評論。我沒有意識到reduce的性能影響,所以我今天學到了一些有價值的東西(並且仍然有很多Clojure需要學習!)。 – Scott