Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Clojure Programming Cookbook

You're reading from   Clojure Programming Cookbook Handle every problem you come across in the world of Clojure programming with this expert collection of recipes

Arrow left icon
Product type Paperback
Published in Oct 2016
Publisher Packt
ISBN-13 9781785885037
Length 618 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Nicolas Modrzyk Nicolas Modrzyk
Author Profile Icon Nicolas Modrzyk
Nicolas Modrzyk
Makoto Hashimoto Makoto Hashimoto
Author Profile Icon Makoto Hashimoto
Makoto Hashimoto
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Live Programming with Clojure FREE CHAPTER 2. Interacting with Collections 3. Clojure Next 4. File Access and the Network 5. Working with Other Languages 6. Concurrency and Parallelism 7. Advanced Tips 8. Web Applications 9. Testing 10. Deployment and DevOps

Using and defining functions

In this recipe, we will review Clojure's function definitions:

  • Defining simple functions
  • Defining variadic functions
  • Defining multiple arity functions
  • Defining functions that specify arguments using a keyword
  • Defining functions with a pre-condition and a post-condition

Getting ready

You only need REPL, as described in the first recipe in this chapter, and no additional libraries. Start REPL so that you can test the sample code immediately in this recipe.

How to do it...

Here, we will learn how to define functions using Clojure. Let's start with a simple function which returns Hello world:

Defining simple functions

Let's start with a minimum function definition. Here is a minimal syntax of defn:

(defn funtion-name [arg1 arg2 ...] 
  expr-1  
  expr-2 
  .. 
  expr-n 
  ) 

defn is a special form. The first argument is a function name and is followed by a vector of one or more arguments, then one or more expressions. The last expression is returned to the caller.

Here, we define a very simple function. The hello function returns a Hello world string:

(defn hello [s] 
  (str "Hello world " s " !")) 
;;=> #'living-clojure.core/hello 
(hello "Nico") 
;;=> "Hello world Nico !" 
(hello "Makoto") 
;;=> "Hello world Makoto !" 

The next sample defines a simple adder function:

(defn simple-adder [x y] 
  (+ x y) 
  ) 
;;=> #'living-clojure.core/simple-adder 
(simple-adder  2 3) 
;;=> 5 

Defining variadic functions

A variadic function allows a variable number of arguments. The next example defines another adder. It may have an arbitrary number of arguments:

(defn advanced-adder [x & rest] 
  (apply + (conj rest x)) 
  ) 
;;=> #'living-clojure.core/advanced-adder 
(advanced-adder 1 2 3 4 5) 
;;=> 15 

Defining multiple arity functions

Here, we will introduce the multiple arity function. The following function defines a single argument function and a couple of argument functions with the same defn:

(defn multi-arity-hello 
  ([] (hello "you")) 
  ([name] (str "Hello World " name " !"))) 
;;=> #'living-clojure.core/multi-arty-hello 
(multi-arity-hello) 
;;=> Hello World you ! 
(multi-arity-hello "Nico") 
  ;;=> Hello World Nico ! 

Defining functions that specify arguments using a keyword

Sometimes, specifying a keyword is useful, since it is not necessary to remember the order of arguments.

The next example shows how to define such a function. The options are :product-name, :price, and :description. The :or expression supplies default values if any values in keys are omitted:

(defn make-product-1 
  [serial & 
   {:keys [product-name price description] 
    :or {product-name "" price nil description "no description !"} 
    }  
   ] 
   {:serial-no serial :product-name product-name 
    :price price :description description} 
  ) 
;;=> #'living-clojure.core/make-product-1 
 
(defn make-product-2 
  [serial & 
   {:keys [product-name price description] 
    :or {:product-name "" :description "no description !"} 
    }    
   ] 
   {:serial-no serial :product-name product-name 
    :price price :description description} 
  ) 
;;=> #'living-clojure.core/make-product-2 
 
(make-product-1 "0000-0011") 
;;=> {:serial-no "0000-0011", :product-name "", :price nil, :description "no description !"} 
(make-product-2 "0000-0011") 
;;=> {:serial-no "0000-0011", :product-name nil, :price nil, :description nil} 

Defining functions with pre-condition and post-condition

Clojure can define functions with pre-condition and post-condition. In the following defn, :pre checks whether an argument is positive. :post checks whether the result is smaller than 10:

(require '[clojure.math.numeric-tower :as math]) 
;;=> nil 
(math/sqrt -10) 
;;=> NaN 
(defn pre-and-post-sqrt [x] 
  {:pre  [(pos? x)] 
   :post [(< % 10)]} 
   (math/sqrt x)) 
;;=> #'living-clojure.core/pre-and-post-sqrt 
(pre-and-post-sqrt 10) 
;;=> 3.1622776601683795 
(pre-and-post-sqrt -10) 
;;=> AssertionError Assert failed: (pos? x)  user/pre-and-post-sqrt (form-init2377591389478394456.clj:1) 
(pre-and-post-sqrt 120) 
AssertionError Assert failed: (< % 10)  user/pre-and-post-sqrt (form-init2377591389478394456.clj:1) 

Moreover, in this recipe, we will show a more complicated function. The make-triangle function prints a triangle with a character. If this function is called without a :char argument, it prints a triangle made of asterisks. If it is called with a :char argument, it prints a triangle comprising characters specified by :char:

(defn make-triangle 
  [no & {:keys [char] :or {char "*"}}] 
  (loop [x 1] 
    (when (<= x no)  
      (dotimes  
          [n (- no x)] (print " ")) 
      (dotimes  
          [n  
           (if (= x 1)  
             1     
             (dec (* x 2)))] 
        (print char)) 
      (print "\n") 
      (recur (inc x)) 
      ) 
    ) 
  ) 
(make-triangle 5)     
;;=>     * 
;;=>    *** 
;;=>   ***** 
;;=>  ******* 
;;=> ********* 
;;=> nil 
(make-triangle 6 :char "x"))     
;;=>      x 
;;=>     xxx 
;;=>    xxxxx 
;;=>   xxxxxxx 
;;=>  xxxxxxxxx 
;;=> xxxxxxxxxxx 

How it works...

We have already reviewed how to define functions and how to use them. You should understand how they work after reviewing the previous section.

To define functions using defn is the same as vars bind to functions by fn as follows:

(defn pow-py-defn [x] (* x x)) 
;;=> #'living-clojure/pow-py-defn 
(def pow-by-def (fn [x] (* x x))) 
;;=> #'living-clojure/pow-by-def 
(pow-py-defn 10) 
;;=> 100 
(pow-by-def 10) 
;;=> 100 

There's more...

clojure.repl has some useful functions to use with other functions. To get a symbol in the specific namespace, use clojure.repl/dir:

(require 'clojure.string) 
;;=> nil 
(clojure.repl/dir clojure.string) 
;;=> blank? 
;;=> capitalize 
;;=> escape 
;;=> join 
;;=> lower-case 
;;=> re-quote-replacement 
;;=> replace 
;;=> replace-first 
;;=> reverse 
;;=> split 
;;=> split-lines 
;;=> trim 
;;=> trim-newline 
;;=> triml 
;;=> trimr 
;;=> upper-case 
;;=> nil 

To get the documentation of a function, use clojure.repl/doc:

(clojure.repl/doc clojure.string/trim) 
------------------------- 
;;=> clojure.string/trim 
;;=> ([s]) 
;;=>   Removes whitespace from both ends of string. 
;;=> nil 

To get symbols that have a specific string, use clojure.repl/apropos:

(clojure.repl/apropos "defn") 
;;=> (clojure.core/defn 
;;=>  clojure.core/defn- 
;;=>  deps.compliment.v0v2v4.compliment.sources.local-bindings/defn-like-forms 
;;=>  deps.compliment.v0v2v4.deps.defprecated.v0v1v2.defprecated.core/defn) 
You have been reading a chapter from
Clojure Programming Cookbook
Published in: Oct 2016
Publisher: Packt
ISBN-13: 9781785885037
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime