问题
I would like to write a function replace-several
that receives a string and a set of replacements and apply all the replacements (where the replacements see the result of the previous replacements).
I thought about the following interface:
(replace-several "abc" #"a" "c"
#"b" "l"
#"c" "j"); should return "jlj"
Two questions:
- Is it the most idiomatic interface in clojure?
- How to implement this function?
Remark: To make a single replacement, there is replace
available in clojure.string.
回答1:
Implementing @kotarak's advice using replace
, reduce
and partition
:
(defn replace-several [content & replacements]
(let [replacement-list (partition 2 replacements)]
(reduce #(apply string/replace %1 %2) content replacement-list)))
; => (replace-several "abc" #"a" "c" #"b" "l" #"c" "j")
"jlj"
回答2:
So you have replace
, reduce
and partition
. From these building blocks you can build your replace-several
.
回答3:
Here is another shot but that has different output results, this one uses the regex engine features so it potentially may be faster, also the interface is different, since it maps keys to replacement strings. I provide this in case it may be useful to someone with a similar question.
(defn replace-map
"given an input string and a hash-map, returns a new string with all
keys in map found in input replaced with the value of the key"
[s m]
(clojure.string/replace s
(re-pattern (apply str (interpose "|" (map #(java.util.regex.Pattern/quote %) (keys m)))))
m))
So usage would be like so:
(replace-map "abc" {"a" "c" "b" "l" "c" "j"})
=> "clj"
回答4:
I'm late to this party, but for what it's worth, I think the most idiomatic way to do this would be to use threading and multiple replacements:
(require '[clojure.string :refer [replace])
(-> "abc"
(replace #"a" "c")
(replace #"b" "l")
(replace #"c" "j"))
;=> "jlj"
The meaning of this is quite clear, though it is nice to avoid typing the "replace" multiple times.
回答5:
You can use reduce with replace:
(defn replace-several
[str & replacements]
(reduce (fn [s [a b]]
(clojure.string/replace s a b))
str
(partition 2 replacements)))
(replace-several "abc"
#"a" "c"
#"b" "l"
#"c" "j")
回答6:
first guess..
(defn replace-several [string & mappings]
(loop [grouped-mappings (partition 2 mappings) string string]
(if (empty? grouped-mappings)
string
(let [[re rep] (first grouped-mappings)]
(recur (rest grouped-mappings) (clojure.string/replace string re rep))))))
; => (replace-several "abc" #"a" "c" #"b" "l" #"c" "j")
"jlj"
回答7:
Personally I wouldn't create a separate function for this, as it is just a composition of existing Clojure functions:
(reduce-kv clojure.string/replace "Hello" {#"[A-Z]" "J", "o" "y"}
;=> "Jelly"
Of course if you want varargs and an interface then:
- Yes it seems reasonably idiomatic
- I'd implement it thus:
(defn replace-many [string & {:as rplcmnts}]
(reduce-kv clojure.string/replace string rplcmnts))
来源:https://stackoverflow.com/questions/9568050/in-clojure-how-to-write-a-function-that-applies-several-string-replacements