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.

Was it helpful?

Solution

(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))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top