clojurecember

My attempt to learn at least a little bit of Clojure each day in December
git clone https://git.sr.ht/~jbauer/clojurecember
Log | Files | Refs | README | LICENSE

commit 93f4841e77989a466653c025ccb253c8c728509a
parent 69ac4b06f3c13b82217887274250c917c6c4a41e
Author: Jake Bauer <jbauer@paritybit.ca>
Date:   Fri,  2 Dec 2022 23:17:16 -0500

Second day

Diffstat:
MREADME.md | 4++++
Aday2.clj | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 231 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md @@ -7,3 +7,7 @@ about one standard library data structure, each day of December. Installed Clojure and followed the [Clojure Syntax Learning Guide](https://www.clojure.org/guides/learn/syntax). + +## DEC02 + +Continued following the Clojure Guide with [Functions](https://www.clojure.org/guides/learn/functions). diff --git a/day2.clj b/day2.clj @@ -0,0 +1,227 @@ +; CREATING FUNCTIONS + +; defn defines a named function +; name params body +; ----- ------ ------------------- +(defn greet [name] (str "Hello, " name) ) + +; Invoke a function with the name of the function like so: +(greet "humans.") + +; MULTI-ARITY FUNCTIONS + +; Different arities must all be defined in the same defn, a second defn +; overwrites the first. +; Each arity is a list ([param*] body*). One arity can invoke another. +(defn messenger + ([] (messenger "Hello world!")) + ([msg] (println msg)) + +(messenger) +; Hello world! + +(messenger "Hello humans!") +; Hello humans! + +; VARIADIC FUNCTIONS + +; Variable number of parameters, the beginning of the variable parameters is +; marked with &. The variable number of parameters are collected into a list. +(defn hello [greeting & who] + (println greeting who)) + +(hello "Hello" "world" "class") +; Hello (world class) + +; ANONYMOUS FUNCTIONS + +; Created with fn +; Because it's anonymous, it cannot be referred to later and is typically +; created at the point it's passed to another function. +(fn [message] (println message) ) + +; It might be useful to think of defn as a contraction of def and fn where fn +; defines a function and def assigns it to a name. + +; Shorter form for the fn anonymous function syntax #() +; Omits parameter list and names parameters based on their position +; % used for a single character +; %1, %2, %3, etc are used for multiple parameters +; %& used for remaining variadic parameters +#(+ 6 %) ; (fn [x] (+ 6 x)) +#(+ %1 %2) ; (fn [x y] (+ x y)) +#(println %1 %2 %&) ; (fn [x y & zs] (println x y zs)) + +; Special GOTCHA +; Anonymous function that takes an element and wraps it in a vector +; #([%]) is wrong, it makes the equivalent (fn [x] ([x])) which will wrap in a +; vector and try to invoke the vector with no arguments. Use this instead: +#(vector %) +; or this +(fn [x] [x]) +; or just this +vector + +; APPLYING FUNCTIONS +; apply invokes a function with 0 or more fixed arguments, and draws the rest of +; the needed arguments from a final sequence. The final argument _must_ be a +; sequence. +(apply f '(1 2 3 4)) ;; same as (f 1 2 3 4) +(apply f 1 '(2 3 4)) ;; same as (f 1 2 3 4) +(apply f 1 2 '(3 4)) ;; same as (f 1 2 3 4) +(apply f 1 2 3 '(4)) ;; same as (f 1 2 3 4) + +; apply is useful when arguments are given to you as a sequence but you must +; invoke the function with the values in the sequence. For example, use it to +; avoid writing this: +(defn plot [shape coords] ;; coords is [x y] + (plotxy shape (first coords) (second coords))) +; and instead write this: +(defn plot [shape coords] + (apply plotxy shape coords)) + +; LOCALS AND CLOSURES + +; let binds symbols to values in "lexical scope" +; bindings name is defined here +; ------------ ---------------------- +(let [name value] (code that uses name)) + +; Each let can define 0 or more bindings and can have 0 or more expressions in +; the body +( let [x 1 + y 2] + (+x y)) + +; fn special form creates a "closure" (it "closes over" the surrounding lexical +; scope and captures their values beyond that scope) + +(defn messenger-builder [greeting] + (fn [who] (println greeting who))) ; closes over greeting + +; greeting provided here, then goes out of scope +(def hello-er (messenger-builder "Hello")) + +; greeting value still available because hello-er is a closure +(hello-er "world!") +; Hello world! + +; JAVA INTEROP + +; Below is a summary of calling conventions for calling into Java from Clojure: +; Task Java Clojure +; Instantiation new Widget("foo") (Widget. "foo") +; Instance method rnd.nextInt() (.nextInt rnd) +; Instance field object.field (.-field object) +; Static method Math.sqrt(25) (Math/sqrt 25) +; Static field Math.PI Math/PI + +; Java methods are not Clojure functions +; Can't store them or pass them as arguments +; Can wrap them in functions when necessary + +; make a function to invoke .length on arg +(fn [obj] (.length obj)) +; same thing +#(.length %) + +; TEST YOUR KNOWLEDGE + +; 1. Define a function greet that takes no arguments and prints "Hello" + +(defn greet [] + (println "Hello")) + +; 2. Redefine greet using def, first with the fn special form and then with the +; #() reader macro. + +(def greet (fn [] (println "Hello"))) + +(def greet #(println "Hello")) + +; 3. Define a function greeting which: +; Given no arguments, returns "Hello, World!" +; Given one argument x, returns "Hello, x!" +; Given two arguments x and y, returns "x, y!" + +(defn greeting + ([] (println "Hello, World!")) + ([x] (println "Hello" x)) + ([x, y] (println x, y))) + +; 4. Define a function do-nothing which takes a single argument x and returns +; it, unchanged. + +(defn do-nothing [x] 'x) + +; In Clojure, this is the identity function. By itself, identity is not very +; useful, but it is sometimes necessary when working with higher-order +; functions. + +(source identity) + +; 5. Define a function always-thing which takes any number of arguments, ignores +; all of them, and returns the number 100. + +(defn always-thing [& args] 100) + +; 6. Define a function make-thingy which takes a single argument x. It should +; return another function, which takes any number of arguments and always +; returns x. + +(defn make-thingy [x] '(fn [&] x)) + +; In Clojure this is the constantly function + +(source constantly) + +; 7. Define a function triplicate which takes another function and calls it +; three times, without any arguments. + +(defn triplicate [f] (f) (f) (f)) + +; 8. Define a function opposite which takes a single argument f. It should +; return another function which takes any number of arguments, applies f on +; them, and then calls not on the result. The not function in Clojure does +; logical negation. + +(defn opposite [f] + (fn [& args] (not (f args)))) + +; In Clojure, this is the complement function +(defn complement + "Takes a fn f and returns a fn that takes the same arguments as f, + has the same effects, if any, and returns the opposite truth value." + [f] + (fn + ([] (not (f))) + ([x] (not (f x))) + ([x y] (not (f x y))) + ([x y & zs] (not (apply f x y zs))))) + +; 9. Define a function triplicate2 which takes another function and any number +; of arguments, then calls that function three times on those arguments. Re-use +; the function you defined in the earlier triplicate exercise. + +(defn triplicate2 [f & args] + (triplicate (f args))) + +; 10. Using the java.lang.Math class (Math/pow, Math/cos, Math/sin, Math/PI), +; demonstrate the following mathematical facts: + + +; 11. Define a function that takes an HTTP URL as a string, fetches that URL +; from the web, and returns the content as a string. + +; 12. Define a function one-less-arg that takes two arguments: +; f, a function +; x, a value +; and returns another function which calls f on x plus any additional arguments. + +; In Clojure, the partial function is a more general version of this. + +; 13. Define a function two-fns which takes two functions as arguments, f and g. +; It returns another function which takes one argument, calls g on it, then +; calls f on the result, and returns that. +; That is, your function returns the composition of f and g. +