Categories
Clojure Other Programming

Learning Clojure: Part 1

Clojure is a functional programming language based on Lisp and written to run on top of the JVM. I’ve tried learning it in the past, but have failed mostly due to biting off more than I could chew. But not this time! I’m taking my time, reading lots of code, and doing 1st year computer science assignments with it. I figure this worked well when I first learned how to program, so it will probably work well now.

The Return to Trivial Programs

After spending the past 6 years neck deep in non-trivial professional programming, I’m returning to trivial toy programs to learn Clojure. My first task is to write a program that takes user input from the terminal and calculates their salary at a year which they input. More specifically:

  • Starting salary is $1000
  • Salary doubles every year
  • Validate input to make sure it is a number.
  • Write history to file called: salary_history.txt
  • In format…. [years_working]:$[salary]

All in all its pretty straight forward. I currently could write this program in a handful of different languages (Python, PHP, Java, Javascript [Node], Ruby), but am struggling with one bit of the Clojure implementation.

(ns salary.core
  (:gen-class))
 
(defn get-integer
    "Returns a string in integer form, else false."
    [input]
    (try
        (#(Integer/parseInt %) input)
        (catch Exception e false)))
 
;; Incomplete.  Will eventually write to a file.
(defn output
    "Takes the console input and error message and outputs them to file and console."
    [console-input message]
    (println (str console-input ": " message)))
 
;;
;; ????? WTF DO I DO HERE
;;
(defn calculate-salary
    [years]
    ())
 
(defn -main
  [& args]
  (println "How many years do you want to work?")
  (let [user-input (read-line)]
    (let [years (get-integer user-input)]
        (if years
            (calculate-salary (- years 1) 1000)
            (output user-input "This is NOT an integer.")))))

The Python implementation of calculate salary would look something like this:

def calculate_salary(years):
    salary = 1000
    for i in range(years-1):
        salary = salary * 2
    return salary

But in Clojure things are bit more complicated. In Clojure values are immutable. I can’t just loop over the years and keep doubling the salary while storing it in the same variable. I need to use recursion. Or reduce. Or map. Hell, I don’t know. I need to use something functional, lest I want the Clojure experts to laugh at me. I need something that will call a function that doubles whatever value comes into it, then returns. Then I need to call said function up to N times (where N is the number of years that the person enters).

Any ideas?

EDIT
With the help of Ryan (below), I came up with:

(defn calculate-salary
    [years salary]
    (if (= years 0)
        salary
        (calculate-salary (- years 1) (* salary 2))))

By Jack Slingerland

Founder of Kernl.us. Working and living in Raleigh, NC. I manage teams of software engineers and work in Python, Django, TypeScript, Node.js, React+Redux, Angular, and PHP. I enjoy hanging out with my wife and kids, lifting weights, and PC gaming in my free time.

9 replies on “Learning Clojure: Part 1”

One way would be to use the accumulator pattern. Below is general idea in Python. This could be done with reduce since it follows a similar idea, but I like this for its simplicity.

def calculate_salary(year, salary):
    if year == 0:
        return salary
    return calculate_salary(year-1, salary*2)

Thats kind of where I was going in my head too. I’m worried that it isn’t the “Clojure” way to do it though. I mean, it is functional and recursive, but I feel like I should be able to use reduce or something.

(defn calculate-salary
    [years salary]
    (if (= year 0)
        (salary)
        (calculate-salary (- year 1) (* salary 2))))

Also, I should probably use “recur” at the end just in case years gets large.

I like the tail-call.

I wouldn’t worry too much about using reduce. To me, it seems like extra work. You’ll have to make a list to run the reduce on (something like range), and at that point, it effectively becomes the same thing.

This probably isn’t what you were looking for but this could much more easily be done in a single line of code (once you’ve included math.numeric-tower library using :as math):


(defn calculate-salary [years] (* 1000 (math/expt 2 years)))

You could use Java’s Math.pow as well if you don’t care about the number coming back as a double.


(defn calculate-salary [years] (* 1000 (Math/pow 2 years)))

Comments are closed.