Functional Things
Pass a function as an argument to another function:
(def painkiller {:artist "Judas Priest" :name "Painkiller"
:genre "Metal" :year 1990})
(def vengeance {:artist "Judas Priest" :name "Screaming for Vengeance"
:genre "Metal" :year 1980})
(defn old? [album]
(< (:year album) 1990))
(defn metal? [album]
(= (:genre album) "Metal"))
(defn both? [album a? b?]
(and (a? album) (b? album)))
(both? painkiller old? metal?) ; false
(both? vengeance old? metal?) ; trueReturn a function from a function:
(defn both? [a? b?]
(fn [album] (and (a? album) (b? album))))
((both? old? metal?) painkiller) ; false
((both? old? metal?) vengeance) ; trueIn fact, defn is a shortcut of def and fn.
Apply a function to a number of values:
(apply + [0 1 2 3 4])Apply a function partially:
(defn chatty-increment-by [x y]
(println "increment" y "by" x)
(+ x y))
(def increment (partial chatty-increment-by 1))
(increment 13)
;; increment 13 by 1
;; 14Create a complement function, which negates a predicate:
(defn old? (< (:year album) 1990))
(def new? (complement old?))
(old? thriller) ; true
(new? thriller) ; falseCombine multiple predicate functions:
(defn old? [album]
(< (:year album) 1990))
(def new? (complement old?))
(new? painkiller) ; true
(new? vengeance) ; falseCombine multiple predicate functions (using and logic):
(def new-priest-metal?
(every-pred (complement old?)
(fn [x] (= (:artist x) "Judas Priest"))
metal?))
(new-priest-metal? painkiller) ; true
(new-priest-metal? vengeance) ; falseUse a function literal or lambda:
(#(Math/sqrt (+ (Math/pow %1 2) (Math/pow %2 2))) 3 4) ; 5.0Instead of named parameters, function literals use numbered parameters %1,
%2, etc. If only a single parameter is used, it can be abbreviated as %:
(#(* % 2) 13) ; 26Exercises
Combining and Complementing Predicate Functions
Given the following country data:
(def fr {:name "France" :population 68373433
:hdi 0.91 :gdp 4.359e12})
(def it {:name "Italy" :population 58968501
:hdi 0.906 :gdp 2.376e12})
(def ng {:name "Nigeria" :population 230842743
:hdi 0.548 :gdp 0.252738e12})
(def bd {:name "Bangladesh" :population 174655977
:hdi 0.670 :gdp 1.801e12})Write the following predicate functions:
wealthy?returnstrueif the GDP per Capita is25000or higher, andfalseotherwise.populous?returnstrueif the population is100000000or larger, andfalseotherwise.developed?returntrueif the Human Development Index (HDI) is0.75or higher, andfalseotherwise.
Combine these predicates to a single predicate first-world? that returns true
if the predicates wealthy? and developed? do and populous? does not
apply.
Hint: Use every-pred to combine predicates and complement to negate a
predicate.
Test: (first-world? fr) and (first-world? it) shall return true;
(first-world? ng) and (first-world? bd) shall return false.
Partially Applied Predicate Functions
The predicates in the last exercises used arbitrary thresholds. Re-write the predicates so that the threshold used for comparison can be passed as their first argument. Re-create the single-parameter predicates from the last exercise by applying the two-parameter predicates partially with the threshold values from before.
Hint: Use partial for partial function application. Re-define the predicates
wealthy?, populous?, developed?, and first-world? using def bindings.
Test: Same as before.
Parametrized Predicate Functions
Re-write the predicate functions once more, but this time the parametrized predicate functions return a predicate function:
wealthy-predaccepts a parametergdp-capitaand returns a predicate corresponding towealthy?.populous-predaccepts a parameterpopulationand returns a predicate corresponding topopulous?.developed-predaccepts a parameterhdiand returns a predicate correspondng todeveloped?.
Hint: The outer function accepts a threshold parameter, and the inner, returned
function accepts a country parameter. The first-world? predicate provides the
actual arguments to create the predicates.
Test: Same as before.
Number Transformations
Write a function transform that accepts a vector of single argument functions
that transform a given initial number. Use apply to run the functions. Write a
tail-recursive function that applies the functions in the vector one by one.
Hint: Pass function literals with the vector.
Test: (transform 2 [#(* % 10) #(+ % 2) #(/ % 2) #(- % 1)]) shall return 10,
and (transform 7 []) shall return 7.
Map Update Function
Given the following inventory data:
(def pliers {:price 7.55 :stock 23 :value 173.65 :revenue 0.0})
(def hammers {:price 3.95 :stock 10 :value 39.50 :revenue 0.0})
(def nails {:price 0.05 :stock 1974 :value 98.70 :revenue 0.0})Write a function (sell quantity item) that decreases :stock by the given
quantity, discounts :value by the product of :price and quantity, and
increases the :revenue by the same product.
Hint: Use nested update function calls to update the relevant fields.
Test: (sell 1 pliers) shall return {:price 7.55 :stock 22 :value 166.1 :revenue 7.55}, and (sell 7 hammers) shall return {:price 3.95 :stock 3 :value 11.85 :revenue 27.65}.