开发者

Iterating through a map with doseq

I'm new to Clojure and I'm doing some basic stuff from labrepl, now I want to write a function that will replace certain letters with other letters, for example: elosska → elößkä.

I wrote this:

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

but it doesn't work as I expect. Could you help me, please?


Here is my take,


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


There are 2 problems here:

  1. doseq doesn't preserve head of list that created by its evaluation, so you won't get any results
  2. str/replace works on separate copies of text, producing 4 different results - you can check this by replacing doseq with for and you'll get list with 4 entries.

You code could be rewritten following way:

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

In this case, intermediate results are collected, so all replacements are applied to same string, producing correct string:

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

P.S. it's also not recommended to use def in the function - it's better to use it for top-level forms


Alex has of course already correctly answered the question with respect to the original issue using doseq... but I found the question interesting and wanted to see what a more "functional" solution would look like. And by that I mean without using a loop.

I came up with this:

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

Which yields the same result:

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

The regex (re-pattern... line simply evaluates to #"ss|a|o|u", which would have been cleaner, and simpler to read, if entered as an explicit string, but I thought it best to have only one definition of the german letters.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜