Learning Clojure Part 5: Robot Factory
Tags: clojure, tutorial
Published: 2021-01-11 12:40:13

In the previous 4 parts, we learned a lot, it's time to do something little big bigger and learn something new in the process. 
Let's imagine we have a little robot assembly factory. We are ordering all the parts from China. We figured out that parts that come in pairs (eyes, hands, legs, etc..) are easier and cheaper to order with only the left variant and we make the matching right one by our self. 

The code to make those right parts is our first goal here. 

We receive parts like this:
```clojure
(def robot-parts [
 {:name "head" :price 50}
 {:name "left-eye" :price 15}
 {:name "neck" :price 10}
 {:name "left-hand" :price 20}
 {:name "torso" :price 50}
 {:name "left-leg" :price 20}
])
```
It's a vector of maps, each map has the name of the part and it's target price. Later we will sum the prices to figure out our costs. 

## Complete the robot

The first thing we need to be able to do is to create a right part from the left one.
Let's create this simple function:
```clojure
(defn make-right-part
 [left-part]
 {
 :name (clojure.string/replace (:name left-part) #"^left-" "right-")
 :price (:price left-part)
 }
)
```

### regex

Notice we used a regular expression here. Clojure regular expressions work pretty much similar to regexes in other languages. The notation for a regex is
```clojure
#"regular-expression"
```
We can play with it a bit using the `re-find` function.
```clojure
(re-find #"^left-" "left-eye")
;=> "left-"

(re-find #"^left-" "head")
;=> nil
```

Using our expression, our function will output `"right-eye"` for a `"left-eye"` input and other strings, that are not starting with left will return untouched. 
```clojure
(make-right-part {:name "left-hand" :price 1})
;=> {:name "right-hand", :price 1}

(make-right-part {:name "right-hand" :price 1})
;=> {:name "right-hand", :price 1}
```

OK. Now we can create the right part from the left one. We can create a function that will complete the robot. 

```clojure
(defn complete-robot
 [parts]
 (loop [remaining parts final []]
 (if (empty? remaining)
 final
 (let [[part & rest] remaining]
 (recur rest (into final (set [part (make-right-part part)]))) 
 )
 )
 
 )
)
```
Uggh. There is a lot of new stuff here. Let's go over it. 

### loop

As the main body of our complete function, we are using the `loop` function. Loop behaves differently than loops in other languages. Loop is another way how to write recursion. 

Lets do a simple loop example:
```clojure
(loop [i 0]
 (println (str "i=" i))
 (if (> i 2)
 (println "END")
 (recur (inc i))
 )
)
;i=0
;i=1
;i=2
;i=3
;END
```
Let's break it down. The first line begins the loop and introduces an `i` variable with the initial value. We can introduce more variables here. 
The second line is the body of a loop. That is the action that will be done each iteration. The third line is a condition that will break the loop when we should end it. In the else part of the condition, we are calling the `recur` function, that is telling the loop to do the next round (continue the recursion), we are providing the new attribute values here. 

### let

Another new piece into the puzzle is `let`. It establishes the new scope and binds values to names. We can use the same tricks for arguments as we can do in functions. 
```clojure
(let [i 42] i)
;=> 42

(def characters ["Darth Vader" "Yoda" "Obi-Wan"])
(let [[main & rest] characters]
 (str "Main character is " main " rest are " rest)
)
;=> "Main character is Darth Vader rest are (\"Yoda\" \"Obi-Wan\")"
```

### reduce

Right now we have all the parts explained and our function for completing robots works. 
```clojure
(complete-robot robot-parts)
;[{:name "head", :price 50}
;{:name "right-eye", :price 15}
;{:name "left-eye", :price 15}
;{:name "neck", :price 10}
;{:name "left-hand", :price 20}
;{:name "right-hand", :price 20}
;{:name "torso", :price 50}
;{:name "right-leg", :price 20}
;{:name "left-leg", :price 20}]
```

The code we wrote (process each element in a sequence and build a result) is a pattern called `reduce` and Clojure has a built-in function for it. 

Simple reduce example would be 
```clojure
(reduce + [1 2 3 4 5 6 7 8 9 10])
;=> 55
```

The `reduce` works like this:
- Apply the provided function to the first element(s)
- Apply the function to the result and next element
- Repeat until we have elements

It can also take an optional initial value.

```clojure
(reduce + 10 [1 2 3 4 5 6 7 8 9 10])
;=> 65
```

We can use `reduce` to reduce the code of our complete robot function.

```clojure
(defn complete-robot
 [parts]
 (reduce
 (fn [final part] (into final (set [part (make-right-part part)])))
 []
 parts
 )
)
```
## Count our costs

Now let's make a function that will take a list of parts. Makes right matching parts. 
And sums up all the costs.

```clojure
(defn bill-robot
 [parts]
 (reduce 
 (fn [sum part] (+ sum (:price part)))
 0
 (complete-robot parts)
 )
)

(bill-robot robot-parts)
;=> 220
```

Yes. It's another example of the `reduce` pattern. 

This brings us to the part where we know all the elementals we need to work with Clojure. Next time we will dive into the core Clojure functions. 

---

You can follow me on [Twitter](https://twitter.com/pavel_polivka) to get more content like this.