(eval "(+ 1 1)")This quick trip to the REPL reminded me of the last time I needed to eval a string, and the previous experiences I'd had with read-string and load-string. It turns out load-string does the trick, and read-string + eval can also get the job done.
"(+ 1 1)"
user=> (load-string "(+ 1 1)")Looking at the documentation for read-string and load-string can help point you at which you might need for your given situation.
2
user=> (eval (read-string "(+ 1 1)"))
2
read-string: Reads one object from the string [argument]Okay, it looks like you'd probably want load-string if you had multiple forms. Back to the REPL.
load-string: Sequentially read[s] and evaluate[s] the set of forms contained in the string [argument]
user=> (load-string "(println 1) (println 2)")As you can see, load-string evaluated the entire string and read-string only returned the first form to eval. The last examples show printing values (and I removed the nil return values), but it's more likely that you'll be concerned with returning something from read-string or load-string. The following example shows what's returned by both functions.
1
2
user=> (eval (read-string "(println 1) (println 2)"))
1
user=> (load-string "1 2")Given our previous results, I'd say load-string and read-string are returning the values you'd expect. It seems like it's easy to default to load-string, but that's not necessarily the case if evaluation time is important to you. Here's a quick snippet to get the point across.
2
user=> (eval (read-string "1 2"))
1
user=> (time (dotimes [_ 100] (eval (read-string "1 2"))))One of the applications I previously worked on would write a large number of events to a log each day; the format used to log an event was a Clojure map. If the application was restarted the log would be read and each event would be replayed to get the internal state back to current. We originally started by using load-string; however, start-up time was becoming a problem, and a switch to read-string completely removed the issue.
"Elapsed time: 12.277 msecs"
nil
user=> (time (dotimes [_ 100] (load-string "1 2")))
"Elapsed time: 33.024 msecs"
nil
Like so many examples in programming, there's a reason that two versions exist. It's worth taking the time to see which solution best fits your problem.
It's unfortunate if read-string doesn't fuss if there is more than one expression in the string (or more non-whitespace text at all after the first expression). That's just an opportunity for program errors to creep in.
ReplyDelete