Learning Clojure - Easy Challenge #1

Lately I spent a week working through Clojure for the Brave and True, an excellent introductory book for the programming language Clojure, written by Daniel Higginbotham. At the end of it, I felt like I understood the basic syntax of the language, and had followed along with the examples in the book fairly well, but I definitely knew that I needed a bit more practice.

To help with this, I've started a process of working through some of the programming challenges posed over at the DailyProgrammer subreddit, using Clojure as my language of choice. What I'm going to start doing is to record my answers here, so that I can better understand what I've written and so that I can refer back to them easily if I ever need to.

The challenges are ranked according to their difficulty (Easy, Intermediate, and Hard) and Easy Challenge #1 asks me to write a program that asks for a users name, age, and Reddit username, and prints them out to screen. Here is what I came up with:

(defn get-answer-for-question
  "Expects a question in the form of a string, and gets the user answer"
  (println question)
  (clojure.string/trim (read-line)))

(defn get-answers-for-questions
  (map get-answer-for-question questions))

(defn easy-challenge-1
  (let [answers (get-answers-for-questions ["What is your name?"
                                            "What is your age?"
                                            "What is your username?"])]
    (println (str "Your name is " (first answers)
                  ", you are " (second answers)
                  " years old, and your username is " (last answers)))))

The function get-answer-for-question accepts a question in the form of text, prints the question, and waits for a response from the user before returning the trimmed user response as the output of the function.

The function get-answers-for-questions is possibly function-overkill, as all it does is map over the provided sequence of questions to build a sequence of answers from the get-answer-for-question function. I put this in there because calling the descriptive function name is (to me - at the moment) the clearer way of expressing what I'm trying to achieve. In time, once I'm more used to the functional programming mindset, I imagine that I'll be happier writing just the map statement and saving the extra lines of code. We shall see.

Lastly, the easy-challenge-1 function creates a scope, whereby answers is mapped to the output from the map in get-answers-for-questions (described above) for the given questions. It then prints those answers out, interleaved with some filler text to make it more readable.

How do I think I did?

I'm pretty happy with my solution to this one. For a first attempt it's gone pretty well. The key place I've identified as needing extra work is in the way I access the answers in the str function. At the moment I'm relying on the answers being in a set order, and if I wanted to add more questions and answers in to the mix then that method of retrieval would become a bit of a headache. 

I actually asked for feedback on this over on the Clojure subreddit, and amongst lots of helpful feedback, someone suggested the following, neater version:

    "Your name is %s, you are %s years old, and your username is %s"
    (ask "What is your name?")
    (ask "What is your age?")
    (ask "What is your username?")))

Where ask is the old get-answer-for-question function. This is a lot neater, and I didn't know about the format command before, so I'll probably use this technique going forward.

Anyway, that concludes my answer for Easy Challenge #1. On to the next one.