I'm currently learning Clojure, and am working through a few exercises. The exercise that I'm working on right now is accept an arbitrary string and determine if the opening and closing parenthesis, curly brackets, and square brackets correctly nest and line up to each other.
For example, "hello { ( world ) } []"
would be an example of a balanced string whereas "foo { [ bar ) [}"
would be an example of an unbalanced string.
While I have previous experience in other programming languages like Python and Java, I'm very new to Clojure and functional languages in general, so am uncertain if my code is fully idiomatic or if there's a more cleaner or efficient way to complete the problem.
Here's what I have so far. The approach I've taken is to first filter out any characters that are not opening or closing braces. I then loop through the characters, adding the opening brackets to a stack and checking closing brackets against the stack to see if it matches.
Any feedback would be greatly appreciated.
; Given a N different open and close braces in a string ""( { [ } ] )"".
; Write a program to check to see if the braces in the string are balanced.
(ns braces
(:require [clojure.string :as str]))
(def braces #{\( \) \[ \] \{ \}})
(def pairs { \) \(,
\] \[,
\} \{ })
(defn remove-non-braces
"Takes a string and removes any characters that is not a brace."
[raw-string]
(filter #(braces %) raw-string))
(defn is-balanced?
"Returns true if all the braces in the string are balanced; returns false otherwise."
([raw-string] (is-balanced? (remove-non-braces raw-string) []))
([string stack]
(let [[first-char & other] string]
(cond
; if we're done, but the stack is non-empty, we still have opening braces left.
(empty? string) (empty? stack)
; if we encounter a closing brace, check it against the top of the stack.
(pairs first-char) (if (= (pairs first-char) (peek stack))
(is-balanced? other (pop stack))
false)
; if we encounter an opening brace, add it to the stack.
:else (is-balanced? other (conj stack first-char))))))
(defn test-strings
"Tests the given strings and prints the results."
[& strings]
(let [format-str (fn [s] (str (is-balanced? s) " : " s))]
(println (str/join "\n" (map format-str strings)))))
(defn main
"The main entry point of the program."
[]
(println "SHOULD PASS")
(test-strings
"Hello { ( world ) } [] end"
""
"{[] () {[{{ }}]} ([{}])}")
(println "")
(println "SHOULD FAIL")
(test-strings
"Hello { world"
"{{"
"])"
"}}{{"
"{[))"))
(main)