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]
(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).
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))))
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.
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.
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.
Yeah, thats a good point. No point in taking the extra step of making years a sequence when its kind of being treated like one implicitly.
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)))
Its not what I’m looking for now, but it will be once I learn how to import libraries 🙂
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)))
You could write this 1-liner:
(defn calculate-salary [years salary]
(reduce + salary (take years (iterate #(* % 2) salary))))
Nothing wrong with other methods.
[…] part 1 of my “Learning Clojure” series, I created a simple program to calculate salary based […]