(def trim-patterns
[[#"([^\\(\[{@~`'])(\(|\[|\{)" "$1 $2"]
[#"(\)|\]|})(\[|\(|\{)" "$1 $2"]
[#"[ \t]*(\)|\]|\})" "$1"]
[#"[ \t]{2,}" " "]
[#"(\)|\]|\})[ \t]*(?=(\)|\]|\}))" "$1"]
[#"(\(|\[|\{)[ \t]*(?=(\(|\[|\{))" "$1"]
[#"^[ \t]*" ""]
[#"(\\\{|\\\(|\\\[) " "$1 "]
[#"(\{|\(|\[) " "$1"]
[#" ('|`) (\(|\[|\{)" " $1$2"]])
(defn trim
[s]
(reduce
(fn [s [match replacement]]
(clojure.string/replace s match replacement))
s trim-patterns))
Rewriting this trimming function in a functional way in Clojure
Question
I have this function(an excerpt from this program) that I want to rewrite in a more appealing/functional/idiomatic way.
It's supposed to remove any unnecessary whitespace from s-expressions.
(defn trim
" Trim whitespace from code:
(trim (trim \" d ' ( print ( + 1 1 ) )\")
==> d '(print (+ 1 1))
The replaced string is passed repeatedly to every call.
Functional version
"
[string]
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
(clojure.string/replace
string #"([^\\(\[{@~`'])(\(|\[|\{)" "$1 $2")
#"(\)|\]|})(\[|\(|\{)" "$1 $2")
#"[ \t]*(\)|\]|\})" "$1")
#"[ \t]{2,}" " ")
#"(\)|\]|\})[ \t]*(?=(\)|\]|\}))" "$1")
#"(\(|\[|\{)[ \t]*(?=(\(|\[|\{))" "$1")
#"^[ \t]*" "")
#"(\\\{|\\\(|\\\[) " "$1 ")
#"(\{|\(|\[) " "$1")
#" ('|`) (\(|\[|\{)" " $1$2"))
I'm not very pleased with the way it looks like right now because it's hard to figure out how it works at first glance(the layout could be the issue) and will pose a problem if I'll ever want to add more regexes/replacements.
Here's another version that's more friendly(and imperative) and works but looks mediocre:
(defn trim1
" Not so functional version"
[raw-string]
(def string (atom raw-string))
(reset! string (clojure.string/replace @string #"([^\\(\[{@~`'])(\(|\[|\{)" "$1 $2"))
(reset! string (clojure.string/replace @string #"(\)|\]|})(\[|\(|\{)" "$1 $2"))
(reset! string (clojure.string/replace @string #"[ \t]*(\)|\]|\})" "$1"))
(reset! string (clojure.string/replace @string #"[ \t]{2,}" " "))
(reset! string (clojure.string/replace @string #"(\)|\]|\})[ \t]*(?=(\)|\]|\}))" "$1"))
(reset! string (clojure.string/replace @string #"(\(|\[|\{)[ \t]*(?=(\(|\[|\{))" "$1"))
(reset! string (clojure.string/replace @string #"^[ \t]*" ""))
(reset! string (clojure.string/replace @string #"(\\\{|\\\(|\\\[) " "$1 "))
(reset! string (clojure.string/replace @string #"(\{|\(|\[) " "$1"))
(reset! string (clojure.string/replace @string #" ('|`) (\(|\[|\{)" " $1$2")))
I'm hoping for a solution that looks something like:
- Store the regexp and the replacements in an associative list:
['(#"^[ \t]*" "")]
- And then do something like
(reduce trim-fn replacement-list string)
The reduce
part would be really cool if it's implementable. But, if it's not possible, I'll welcome any other solution that is better than those two.
Solution
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow