commit 93f4841e77989a466653c025ccb253c8c728509a
parent 69ac4b06f3c13b82217887274250c917c6c4a41e
Author: Jake Bauer <jbauer@paritybit.ca>
Date: Fri, 2 Dec 2022 23:17:16 -0500
Second day
Diffstat:
M | README.md | | | 4 | ++++ |
A | day2.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.
+