Reputation: 33
I've successfully written a function that converts a space delimited string of integers to a vector of integers in Clojure but since I'm (very) new to functional languages I'm worried I'm still thinking too procedurally.
The function uses split
to tokenize the string then iterates through the returned vector individually converting the tokens to integers before appending them to a new vector. I'm using read-string
because the input is self provided and I'm not really concerned about safety.
(defn parser [myStr]
;;counter
(def i 0)
;;tokenizes string and returns vector of tokens
(def buffer (clojure.string/split myStr #"\s"))
;;reads vector of strings as integers then appends them to a new vector x
(def x (vector-of :int))
(while ( < i (count buffer))
(def x (conj x (read-string (nth buffer i))))
(def i (inc i)))
(println x))
My code works but I'm concerned that by changing states and iterating through the buffer vector that I'm cheating a bit and sticking to my procedural roots.
Is there a more elegant or functional way to solve this problem?
Upvotes: 3
Views: 624
Reputation: 45745
There's a few very notable things here:
Never use def
inside of defn
unless you have a very good reason to. The use case here isn't justified. Just use let
instead:
(defn parser [myStr]
(let [i 0
buffer (clojure.string/split myStr #"\s")
x (vector-of :int)]
...)
To see what the difference is, run your function, then check what i
holds. def
creates globals that persist after the function exits, which leaks the state of the function and pollutes the namespace.
You're using read-string
to parse. Don't do that. Just use Java's Long/parseLong
. read-string
has eval
behavior, which is never good to abuse.
You can also use clojure.edn/read-string
which can read Clojure structures and literals, but doesn't execute code.
You're using while
to carry out side effects when really you could use loop
or a number of other functional methods. @xs0 is basically right. I'd write your function as:
(defn parser [myStr]
; The v in mapv means it returns a vector
; Just map returns a lazy seq
(mapv #(Long/parseLong %) (clojure.string/split myStr #"\s"))
Unfortunately Long/parseLong
needs to be wrapped in a function since Java interop methods can't be used like normal Clojure functions.
Long/parseLong
is only safe to use if you can guarantee that each token split
returns is parsable. Of course, if there's no such guarantee, you'll need to do some error handling, or clean the input before trying to parse.
Upvotes: 6