Clojure
- File size
- 19.1KB
- Lines of code
- 381
Clojure
A Lisp dialect with a stronger emphasis on functional programming than Common Lisp.
Comments
; ---------- COMMENT -----------
; single-line comments begin with a semicolon
; there is no built-in implementation for multi-line comments
Printing
; ---------- PRINT ----------
; println => prints strings to stdout and appends a newline to the output
; prn => prints a more readable representation of data structures to the stdout and appends a newline to the output
(println "Hello world!") ; prints "Hello world!" to the console with a newline
(prn {:cereal-chicken 2.50 :thai-fish 3.00 :egg 1.25}) ; prints the array map in a human-readable format to the console with a newline
Quickstart
; ----------- QUICKSTART -----------
; clojure is comprised of forms (list of whitespace delimited things surrounded by () parantheses, similar to expressions)
; clojure assumes the first thing is a function or macro call, and the remaining things are arguments
; clojure types are immutable, so remember to reassign returned values (functional programming!)
; ns => first call in a clojure file to set the namespace
(ns parklane-cai-fan) ; sets everything in the file to be within the parklane-cai-fan namespace
Variables
; --------- VARIABLE ----------
; def => creates a global variable and assigns a corresponding value
; let => creates a local variable lexically scoped to the current scope of () brackets and assigns a corresponding value, and has an implicit return of the last expression
(def an-int 1) ; global-binding of a variable (global variable)
(def a-float 2.2) ; global-binding of a variable (global variable)
(def a-string "cereal chicken") ; global-binding of a variable (global variable)
(def a-bool true) ; global-binding of a variable (global variable)
(let [a 1 b 2]
(> a b)) ; this creates local bindings, then evaluates to false from the last expression
Types
; ---------- TYPE -----------
; integer
; float => floating point numbers
; string => double quotation marks
; boolean => true, false
; nil => special value to represent the absence of data
; keyword => strings with efficiency bonuses, declared with : colon in front of keyword
1 ; integer
1.23 ; float
"watermelon" ; string
false ; boolean
nil ; special value nil
:a ; keyword
; USEFUL TYPE FUNCTIONS
; quote => prevents literal data from being evaluated, has a shorthand '
; ' => prevents literal data from being evaluated, the shorthand for quote
; eval => evaluates a literal list
(quote (+ 1 2)) ; this literal list of data won't be evaluated and will evaluate to value of (+ 1 2)
'(+ 1 2) ; this literal list of data won't be evaluated, evaluates to value of (+ 1 2)
(eval '(+ 3 4)) ; this forces an evaluation on a quoted list, thus evaluating this to the value of 7
Operators
; ---------- OPERATOR ----------
; ARITHMETIC OPERATORS
; + => addition
; - => subtraction
; * => multiplication
; / => division
; mod => modulo
; LOGICAL OPERATORS
; and
; or
; not
; COMPARISON OPERATORS
; = => partial equality check for value
; not= => partial inequality check for value
; == => complete equality check for type and value
; not== => complete inequality check for type and value
; < > <= >= for other comparison checks
(+ 1 1) ; evaluates to 2
(- 2 1) ; evaluates to 1
(* 1 2) ; evaluates to 2
(/ 2 1) ; evaluates to 2
(mod 5 2) ; evaluates to 1
(not true) ; evaluates to false
(false and true) ; evaluates to false
(false or true) ; evaluates to true
(= 1 1) ; evaluates to true
(= 2 1) ; evaluates to false
(= 1 "1") ; evaluates to true
(== 1 "1") ; evaluates to false
Control structures
; ---------- CONTROL STRUCTURE ----------
; CONDITIONAL CHECKS
; if => operates as you'd expect, called as a function like everything else in clojure
; general syntax is (if {CONDITIONAL CHECK} {THEN EXPRESSION} {ELSE EXPRESSION})
; cond => evaluates each specified predicate in order and evaluates to the value of the first true condition, equivalent to an if elseif else chain in other languages
; :else => added as the final else case in a cond chain
; case => equivalent to match-case chain in other languages
; :else => added as the final default case in a case chain
(def x 10)
(if (> x 5)
"Greater than 5"
"Less than or equal to 5") ; this evaluates to "Greater than 5"
(def y 10)
(cond
(> y 15) "Greater than 15"
(> y 10) "Greater than 10"
:else "10 or less") ; this evaluates to "10 or less"
(def day-of-week 3)
(case day-of-week
1 "Sunday"
2 "Monday"
3 "Tuesday"
:else "Unknown day") ; this evaluates to "Tuesday"
; LOOPS
; for most iterative solutions, clojure's functional programming constructs are encouraged instead of loops (map, reduce, filter)
; loop => creates and defines the lexical scope of a loop, used alongside recur
; recur => indicates to initiate another iteration of the current loop with recursion
; doseq => creates a loop that iterates over a specified structure, achieved without explicit recursion
(defn countdown-recur [n]
(loop [i n]
(if (<= i 0)
"Blast off!"
(do
(println i)
(recur (dec i)))))) ; function definition for a function that contains a recursive loop which counts down from n to 0, then prints blast off, here recur is used to decrement i by 1 and initiate another iteration of the loop
(defn countdown-iter [n]
(doseq [i (range n 0 -1)]
(println i))
(println "Blast off!")) ; function defintion for a function that achieves the same thing as the above function, but does it using doseq to create a loop that does not rely on explicit recursion but instead iterates over a vector created using the range function
Data structures
; ---------- DATA STRUCTURE -----------
; collection => groups of data (lists, vectors)
; sequence => abstract descriptions of lists of data (lists)
; LIST
; linked-list data structure
; declared with () brackets, must be quoted as a literal with ' to prevent clojure from thinking its a function
; list => creates a list literal, an alternative to declaring a list literal with '
; range => creates a list of range 0 to a specified length, no specification results in an infinite series
; take => retrieve a slice of a list based on length from the front
; cons => insert an item to the start of a list
; conj => inserts an item to the start of a list (the most efficient method of insertion for a list)
; concat => concatenates collections (both lists and vectors) together and returns them as a list
; map => apply a specified function on every item within the list
; filter => apply a specified function on every item within the list and keep those that meet the specified function predicate
; reduce => aggregate multiple items within a list to a single result by applying the specified function to each item and accumulating the result, can take an initial argument value also
(list 1 2 3) ; creates a list literal of value (1 2 3), the equivalent of '(1 2 3)
(range) ; creates an infinite series of value (0 1 2 3 4 ...)
(range 4) ; creates a list of value (0 1 2 3) with length 4
(take 4 (range)) ; retrieves a slice of length 4 from an infinite list, evaluating to (0 1 2 3)
(cons 4 '(1 2 3)) ; this evaluates to (4 1 2 3)
(conj '(1 2 3) 4) ; this evaluates to (4 1 2 3) also by inserting the element at the start of the list
(concat '(1 2) '(3 4)) ; this evaluates to (1 2 3 4)
(concat [1 2] '(3 4)) ; this also evaluates to (1 2 3 4)
(map inc '(1 2 3)) ; this evaluates to (2 3 4)
(filter even? '(1 2 3)) ; this evaluates to (2)
(reduce + '(1 2 3 4)) ; this evaluates to 10
(reduce conj [] '(3 2 1)) ; this evaluates to [3 2 1]
; VECTOR
; array-backed vector
; declared with [] square brackets
; cons => insert an item to the start of a vector
; conj => appends an item to the end of a vector (the most efficient method of insertion for a vector)
; concat => concatenates collections (both lists and vectors) together and returns them as a list
; map => apply a specified function on every item within the vector
; filter => apply a specified function on every item within the vector and keep those that meet the specified function predicate
; reduce => aggregate multiple items within a vector to a single result by applying the specified function to each item and accumulating the result, can take an initial argument value also
[1 2 3] ; creates a vector literal of value [1 2 3]
(cons 4 [1 2 3]) ; this evaluates to (4 1 2 3)
(conj [1 2 3] 4) ; this evaluates to [1 2 3 4] by appending the element to the end of the vector
(concat [1 2] [3 4]) ; this evaluates to (1 2 3 4)
(concat [1 2] '(3 4)) ; this also evaluates to (1 2 3 4)
(map inc [1 2 3]) ; this evaluates to (2 3 4)
(filter even? [1 2 3]) ; this evaluates to (2)
(reduce + [1 2 3 4]) ; this evaluates to 10
(reduce conj [] [3 2 1]) ; this evaluates to [3 2 1]
; ARRAY MAP
; declared with {} curly braces
; assoc => adds new key-value pairs to an array map and returns the new array map
; dissoc => removes key-value pairs from an array map and returns the new array map
; retains key-value pair order, slower lookups than hash maps
; keys can be any hashable type, but keywords are used as keys by convention
; array maps are automatically converted to hash maps when their size gets big enough
; retrieve a value from its key by calling the array map as a function, retrieving an absent key returns nil
{:a 1 :b 2 :c 3} ; an array map literal
(def watermelon-array-map {"a" 4 "cd" 10 "ef" 100}) ; creates an array map and assigns it to a variable
(watermelon-array-map "cd") ; evaluates to the integer value 10
(def new-watermelon-array-map (assoc watermelon-array-map "watermelon-pie" 2000)) ; adds a new key-value pair to watermelon-array-map, then assigns the new array map value to the variable new-watermelon-array-map since clojure types are immutable
(def edited-watermelon-array-map (dissoc new-watermelon-array-map "a" "cd")) ; removes the key-value pairs of key "a" and "cd" from the new-watermelon-array-map and returns the new array map, then assigns that value to the variable edited-watermelon-array-map since clojure types are immutable
; HASH MAP
; hash-map => creates a hash map literal
; assoc => adds new key-value pairs to a hash map and returns the new hash map
; dissoc => removes key-value pairs from a hash map and returns the new hash map
; does not retain key-value pair order, faster lookups than array maps
; keys can be any hashable type, but keywords are used as keys by convention
; retrieve a value from its key by calling the hash map as a function, retrieving an absent key returns nil
(hash-map :a 1 :b 2 :c 4) ; a hash map literal
(def apple-hash-map (hash-map :z 100.00 :in 6 :kl 2)) ; creates a hash map and assigns it to a variable
(apple-hash-map :z) ; evaluates to the float value of 100.00
(def new-apple-hash-map (assoc apple-hash-map :apple-pie 10)) ; adds a new key-value pair to apple-hash-map, then assigns the new hash map value to the variable new-apple-hash-map since clojure types are immutable
(def edited-apple-hash-map (dissoc new-apple-hash-map :z :kl)) ; removes the key-value pairs of key :z and :kl from new-apple-hash-map and returns the new hash map, then assigns that value to the variable edited-apple-hash-map since clojure types are immutable
; SET
; declared with #{}
; set => creates a set, alongside [] square brackets
; conj => appends a specified member to the set
; disj => removes a member from a set by value
; test for set members by calling the set as a function
; similar to sets in other languages, with a range of functions for operation (see clojure.sets namespace)
#{1 2 3} ; creates a set literal
(set [1 2 3 4]) ; also creates a set literal which evaluates to the value #{1 2 3 4}
(conj #{1 2 3} 4) ; this evaluates to #{1 2 3 4}
(disj #{1 2 3} 1) ; this evaluates to #{2 3}
(#{1 2 3} 1) ; this evaluates to 1, which evaluates to boolean true
(#{1 2 3} 4) ; this evaluates to nil, which evaluates to boolean false
Functions
; ---------- FUNCTION -----------
; functions have implicit return on their last statement
; functions can take multiple kinds of arguments and evaluate differently, just specify them with separate [] square brackets and () brackets
; fn => creates a new anonymous function, where [] square brackets list function arguments
; anonymous functions are called with (()) double brackets
; def => assigns an anonymous function to a variable to create a named function
; defn => creates a named function, where [] square brackets list function arguments
; & => recieves extra arguments and packs them in a list, can be mixed with normal arguments
; ANONYMOUS FUNCTIONS
(fn [] "Hello World") ; evaluates to fn
((fn [] "Shit ass")) ; calls the anonymous function, evaluating to the string "Shit ass"
(def hello-world-ass (fn [] "Hello World Ass")) ; creates the anonymous function which returns the string "Hello World Ass" then assigns it to the variable hello-world-ass to make it a named function
(hello-world-ass) ; this calls the named function, evaluating to the string "Hello World Ass"
; NAMED FUNCTIONS
(defn watermelon [] "watermelon") ; creates a named function watermelon which evaluates to the string value "watermelon"
(watermelon) ; calls the specified function, evaluating to the string value "watermelon"
(defn hello [name]
(str "Hello " name)) ; another function definition
(hello "Steve") ; calls the specified function, evaluating to string value "Hello Steve"
; MULTI-VARIADIC FUNCTIONS
(defn hello-flexible
([] "Hello World")
([name] (str "Hello " name))) ; a multi-variadic function definition that can take different arguments and evaluates differently based on arguments provided
(hello-flexible "Jake") ; this will evaluate to "Hello Jake"
(hello-flexible) ; this will evaluate "Hello World"
; VARIABLE NUMBER OF FUNCTION ARGUMENTS
(defn count-args [& achoo]
(str "You passed " (count achoo) " arguments: " achoo)) ; function definition for a function that accepts multiple arguments under the argument name achoo
(count-args 1 2 3) ; this evaluates to the string value of "You passed 3 args: (1 2 3)"
(defn hello-count [name & orange]
(str "Hello " name ", you passed " (count orange) " extra arguments")) ; function definition for a function that mixes a variable number of arguments with other named arguments
(hello-count "Finn" 1 2 3) ; this evaluates to the string value of "Hello Finn, you passed 3 extra args"
Modules
; ----------- MODULE -----------
; use => brings all functions from the specified module into the current global scope, does not requires namespace qualification
; require => imports a module and its functions but requires namespace qualification when calling module functions
; ns => namespaces can be used to require a module as well, doing so means we don't need to quote the module
(use 'clojure.set) ; brings all set functions within the global scope
(intersection #{1 2 3} #{2 3 4}) ; we can now call set functions, this evaluates to #{2 3}
(require 'clojure.string) ; imports the clojure string module within the global scope but namespace qualfiication required when calling string functions
(clojure.string/blank? "") ; we can now call string functions but must reference the clojure.string namespace, this evaluates to true
(ns test
(:require
[clojure.string :as str]
[clojure.set :as set])) ; this has the same effect as above and gives the modules shorter names, note there is no need to quote ' the module here