First off, good job on your formatting! I often see people who are new to Lisp trying to write and format their code as if it were Java, so props to you for not doing that.
You've misspelled "appendage" as "apendage" in your code, so my first change would be to correct that misspelling.
Since you are using a function from clojure.string
, your code would be more readable if you were to :require
that namespace and alias it:
(ns appendages.core
(:require (clojure [string :as s])))
Now you can replace, for instance, clojure.string/replace
with just s/replace
. You could also use a longer alias such as str
if you find s
to be too cryptic.
As you've noted, next-appendage
is quite ugly. This is partially because you wrote it as a literal anonymous function instead of taking advantage of defn
:
(defn next-appendage
[part done]
(hash-map :name (s/replace (:name part) #"[0-9]" (str (+ 1 done (read-string (re-find #"[0-9]" (:name part))))))))
A few comments on usage here:
- Instead of calling
hash-map
, you should just write a literal map with your key-value pairs.
- You should never use
read-string
unless you have an extremely good reason, because it can evaluate arbitrary code. Since you're simply using it to parse an integer, you should use something like Long/parseLong
instead.
- All your
next-appendage
function really does is get the value in part
at :name
, transform that value in some way, and return a new map with the :name
associated with that new value. This is exactly the use case for the update
function.
- Since the code in
next-appendage
is so deeply nested, its readability would benefit greatly from use of the ->>
macro.
With these changes, next-appendage
would look something like this:
(defn next-appendage
[part done]
(update part :name
#(->> (re-find #"[0-9]" %)
Long/parseLong
(+ 1 done)
str
(s/replace % #"[0-9]"))))
The add-appendages
function can be greatly simplified. You're basically just looping through several values of done
(which brings to mind the range
function) and, for each of those values, calling next-appendage
on part
(like map
), and finally assembling the result into a sequence with part
at the beginning (cons
):
(defn add-appendages
[times part]
(cons part (map (partial next-appendage part) (range times))))
You could make this even simpler by having replacing done
with just the number that you're adding to the digit in the :name
of part
, which removes the need for the + 1
in next-appendage
and the cons
in add-appendages
.