POSTS

Guess the Number, Part 3 - conditionals

In this part, we’ll have the computer react accordingly to the player input.

Conditional statements: if

In programming, we use conditional statements to decide how the program will proceed when given a certain condition. Clojure supports conditional branching via a number of forms: if, while, cond, condp, and so on. The most basic of the forms is if.

(if (= 5 (+ 2 3))
  "5 is equals to 2 plus 3"
  "5 is not equal to 2 plus 3")

Try the above snippet in the REPL to see how if works. Note the usage of = as an equality test function, also known as a predicate function. Unless there was a typo, the REPL should return "5 is equals to 2 plus 3", because the test form (= 5 (+ 2 3)) evaluated to true. If the test form had evaluated to false, the REPL would have returned "5 is not equal to 2 plus 3". Roughly equivalent code in languages such as JavaScript, Java and C would be:

String result = "";
if (5 == 2 + 3) {
  result = "5 is equals to 2 plus 3";
} else {
  result = "5 is not equal to 2 plus 3";
}

By contrast, the Clojure version strips away most of the syntax, leaving behind a “minimalistic” if statement. The tradeoff here is that we have to remember what each part of the if statement is for by their position within the form, rather than relying on syntax hints. Most of the standard Clojure forms follow this trend, allowing us to express intent as succintly as possible. In fact, if itself is rarely used in favor of higher level constructs.

Conditional statements: condp

To express multiple branching conditions in Java-like languages, we would use if-else or switch-case statements. The equivalent forms in Clojure would be cond, and a variant called condp. For now, let’s look at condp:

(condp = (+ 2 3)
  7 "7 is equals to 2 plus 3"
  5 "5 is equals to 2 plus 3"
  "I don't know what equals 2 plus 3!")

After entering it into the REPL, the above code should hopefully return "5 is equals to 2 plus 3".

Let’s take apart the condp function. From the condp documentation:

(condp pred expr & clauses)

pred: the first parameter is a binary predicate function, which simply means any function that takes in at least two parameters and returns a truthy or falsey value.

expr: the second parameter is the expression that you would like to test. expr will be given as a parameter to pred.

& clauses: the third parameter, clauses, takes in any number of clauses. Each clause is a pair of forms of the format: test-expression result-expression. For each pair of clauses, test-expression and expr will be passed to the pred function until pred returns a truthy value, after which result-expression will be returned. The last form in clauses can be a single result-expression, which will be taken as the default value if none of the clauses match.

The & in front of clauses indicates that this is a catchall parameter. This allows condp to accept any number of additional parameters, which will be placed in a list-like structure named clauses. A function can only have one such variadic parameter.

Handling player input with condp

Let’s see how condp can help with our game. After the computer makes a guess, we’ll allow the player to enter three choices: whether the computer’s guess should be bigger, smaller, or whether it was just right. Open core.clj and modify -main to look like the following:

(defn -main []
  (println "Think of a number between 0 to 99 and I'll try to guess it!")
  (let [minimum 0
        maximum 99
        guess (int (/ (- maximum minimum) 2))]
    (println "My guess is:" guess)
    (println "1. Bigger")
    (println "2. Smaller")
    (println "3. That's correct!")
    (print "> ")
    (flush)
    (condp = (Integer/parseInt (read-line))
      1 (println "My minimum guess should now be" guess)
      2 (println "My maximum guess should now be" guess)
      3 (println "All right!!!")
      (println "I didn't understand your choice"))))

Give it a try with lein run -m guessnumber.core:

Think of a number between 0 to 99 and I'll try to guess it!
My guess is: 49
1. Bigger
2. Smaller
3. That's correct!
> 1
My minimum guess should now be 49

On line 12, we’ve made use of condp to process the player’s input. Integer/parseInt is a call to Java’s Integer.parseInt method. We won’t go into Java interop in this tutorial, but you can read more about it here. The rest of condp is about printing out what the computer should be doing based on the player’s choice. Finally, if the user enters a choice that’s not handled, we’ll print “I didn’t understand your choice”.

Note that if the player enters a non-number as input (such as any alphabet or symbol) the program will throw an exception since Integer/parseInt only accepts numbers.

Next time, we’ll cover looping which enables us to actually finish a game!