In this part of the exercise, we will write our first functional-language program that will solve the same problem as circle_area.java.
Unlike traditional languages, LISP and its descendents are functional languages: languages in which all computation is accomplished by calling functions and passing them arguments (which can be other functions). The particular variety of LISP we will be studying is Clojure, a modern (2007) LISP dialect that modernizes the LISP language, and adds object-oriented and concurrent/parallel features to it. Clojure runs on the Java Virtual Machine, and (as we shall see) it supports interoperability with the Java language. Clojure has been used in real-world systems at a variety of companies, ranging from Apple to Walmart. You may find it convenient to bookmark this Clojure Cheat Sheet so that you can access it conveniently.
There are two ways to use Clojure:
The clj REPL interpreter provides a quick and convenient means of experimenting with Clojure functions. To try it out, open a terminal window and enter:
cljAfter showing the version number, you should see a prompt like this:
user=>You can enter any valid Clojure expression and Clojure will (i) read the expression, (ii) evaluate the expression, (iii) print the result returned by the expression, and (iv) loop back to display the prompt again. Let's try it; enter this:
user=> (println "Welcome to Clojure!")This expression invokes the println() function, and passes it the string Welcome to Clojure!" as an argument. The interpreter reads it, evaluates it, which causes "Welcome to Clojure" to appear followed by a newline, and then prints the value returned by the println() function, which is nil.
So far so good! Try this:
user=> (print "Welcome to Clojure!")What is different from before? Does print() return the same value as println(), or a different value? If you aren't sure, talk to your neighbor and see if you can reach a consensus.
Note that where a function-call in a traditional language like Java has the form:
functionName( arguments)a function-call in Clojure follows this pattern:
(functionName arguments)You just position the open-parenthesis before the name of the function instead of after it.
In Clojure, operators are functions. Try entering this:
user=> (+ 1 2)When you enter that expression, Clojure reads it, evaluates it (which calls the + function with 2 and 3 as arguments), and prints the result that the function returns: 3.
Next, try entering the equivalent expression the "normal" way:
user=> (1 + 2)You should see an error message. (Get used to it; this will happen a lot.)
The problem is that this expression does not follow the pattern: (1 + 2) is trying to call a function named 1 and pass it + and 2 as arguments. Since there is no function named 1, Clojure displays that lovely error message.
Next, try entering this:
user=> (+ 1 2 3 4)Perhaps surprisingly, this works correctly, producing the result 1+2+3+4 = 10! For convenience, many of the arithmetic operatators in Clojure allow you to enter an arbitrary number of arguments.
Note also that with clj, you can use the arrow keys:
user=> (* (+ 1 2) (- 3 4))Note how, as you enter each close-parenthesis, the cursor "bounces" to the matching open-parenthesis. This can help you keep from entering expressions with mismatched parentheses.
One last thing: enter this:
user=> (* (+ 1 2) (- 3 4)Note how clj nothing happens. The problem is that you have not finished the expression. Type the missing close-parenthesis, press Enter again, and you should see the same result as before. The expressions you enter can thus span multiple lines; a multi-line expression like this:
user=> (* (+ 1 2) (- 3 4) )will work just fine.
To exit clj, type Ctrl-d (hold down the Ctrl key, press the d key once, and then release the Ctrl key). This should return you to the command-line prompt in your terminal.
The clj REPL system is convenient for quickly trying out Clojure functions to see how they behave, but it isn't a very convenient way to enter full programs. For that, we will use a text editor and the clojure compiler.
Clojure source programs are simple text files, but Clojure is a bit particular about where you store those files, because it likes to keep the files related to a given Clojure product separate from the files for other projects.
In a terminal window, navigate to your 214/labs/01 folder. Inside your 01 folder, create a clojure folder; then create a src folder inside your 01/clojure folder. The Clojure source file you will create for this lab exercise must reside in this src folder. You will need to do this same procedure for each lab exercise and homework project.
Clojure source programs consist of text files whose names end in the .clj suffix, so in your terminal/console window, make certain you are "in" your 214/labs/01/clojure/src folder. (If you are not sure, enter the pwd command, which stands for path-to-working-directory.)
You can use any text editor to enter a Clojure source program, but make certain it does parentheses-matching. To illustrate using vim, we can create and edit a source file named circle_area.clj by entering:
vim circle_area.cljThis will launch vim within your terminal window and create an empty text file named circle_area.clj in your current working directory (i.e., 214/labs/01/clojure/src).
And now, on to our program!
Copy the following program into your circle_area.clj file. As usual, be sure to personalize the opening documentation.
;;;; circle_area.clj calculates the area of a circle.
;;;;
;;;; Input: The radius of a circle.
;;;; Output: The area of that circle.
;;;;
;;;; Usage: clojure -m circle_area
;;;;
;;;; Begun by: Prof. Adams, for CS 214 at Calvin College.
;;;; Completed by:
;;;; Date:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ns circle_area) ; namespace function names the program
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Function circleArea() computes the area of a circle.
;;; Receive: itsRadius, a number.
;;; Precondition: itsRadius >= 0.0.
;;; Return: the area of the corresponding circle.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn circleArea [itsRadius]
(* Math/PI (* itsRadius itsRadius) ) ; return PI*r^2
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Function -main is where execution begins
;;; Input: the radius of a circle.
;;; Output: the area of that circle.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn -main []
(println "\nTo compute the area of a circle,")
(print " enter its radius: ") (flush)
(let
[ radius (read) ]
(assert (>= radius 0) "-main: radius must be positive")
(printf "\nThe area is %f\n\n" (circleArea radius))
(print "\nThe area is ")
(print (circleArea radius))
(print "\n\n")
(printf "\nThe area is %.15f\n\n" (circleArea radius))
)
)
This program consists of two functions,
one named circleArea() and one named -main(),
where execution will begin.
Since Clojure is probably the most unusual of our four languages,
let's spend some time studying the code carefully,
as we can learn several things from it:
functionName(arguments)a Clojure function call has the form:
(functionName arguments)Thus, the notation (ns circle_area) invokes the ns (namespace) function and passes it the name circle_area as an argument.
Likewise, the notation (defn circleArea ... ) invokes the defn (define function) function, and passes it a sequence of arguments including (i) the name circleArea, (ii) the square brackets containing the function's arguments, and (iii) the function-expressions that make up the function's body.
Similarly, inside the circleArea() function, the line:
(* Math/PI (* itsRadius itsRadius) )computes the area π r2 by invoking the multiplication function, passing it Math/PI (from Java's Math library), and the product of itsRadius times itsRadius as arguments.
Likewise, inside the -main() function, the line:
(printf "\nThe area is %f\n\n" (circleArea radius))invokes the printf function, passing it two arguments: a format-string and the value returned by the circleArea function.
Take a few moments to locate and study each of these points, as understanding them will help you on this week's project.
When you are done studying the code, save the file and continue.
When working with a text editor, there are different ways to build and run your program. One way to run your (saved) program that does not require your to quit the text editor is to open a second terminal/console window, navigate to your 214/labs/01/clojure/ folder (the folder "above" your src folder), and there enter:
clojure -m circle_area
This invokes the clojure compiler which will compile everything in the local src folder. The switch -m circle_area tells the compiler to run the -main function that is defined within the circle_area namespace.
Using this method, test what you have written using the same test values as before:
1 2 2.5 4.99999
Make certain that your results are equivalent to those produced by the other languages before continuing.
How do the print, println, and printf functions differ?
When displaying a real number, how can you control the number of decimal digits displayed by printf?
Finally, note that the Clojure Style Guide recommends several different "levels" of comments, each of which should appear immediately before the thing being commented on:
Clojure also provides a comment() function that can be used for block-comments. To use it, you can just put
(commentbefore a chunk of code you want to comment out, and then put a close parenthesis at the end of that chunk. We won't use this in these exercises, but it is available to you if you find you need to use it. If you do, one thing to keep in mind is that the comment() function returns nil, so if you use it to comment out the body of a function:
(defn aFunction [ itsArg ]
(comment
function body
)
)
and then call that function, its return-value will be nil
because that is the value comment() returns.
Clojure also supports docstrings -- documentation comments that are enclosed within double-quotes -- which must appear immediately after the first line of a function. We have not used any of these in this exercise and will not use them in this course, but you will often see them in examples you find on the Internet, as they are used to auto-generate HTML documentation for function libraries being publicly distributed.
Return to the lab 1 page to complete the other parts of this exercise.
Calvin > CS > 214 > Labs > 01 > Clojure