\documentclass{beamer} \title{Clojure Core} \subtitle{Primatives, collections, functions, and macros} \author{Lily Carpenter} \institute{https://gitlab.com/azrazalea/} \mode {\usetheme{Dresden}} \date{} \usepackage[latin1]{inputenc} \usepackage{times} \usepackage[T1]{fontenc} \usepackage[english]{babel} \usepackage{hyperref} \usepackage{minted} \usepackage{upquote} \AtBeginDocument{% \def\PYZsq{\textquotesingle}% } \begin{document} \begin{frame} \titlepage \end{frame} \begin{frame} \frametitle{Summary} \tableofcontents \end{frame} \section{Primitives} \begin{frame} \frametitle{Primitives} Uses host language primitives \begin{itemize} \item{Integers} \item{Floating points} \item{Ratios} \item{Strings} \item{Regexes} \item{Keywords} \item{Symbols} \end{itemize} \end{frame} \begin{frame} \frametitle{Integers} \begin{itemize} \item{Standard java Long} \item{Automatically upgrade to arbitrary precision BigInteger with certain functions} \item{BigInteger does not exist for ClojureScript} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Integers} \begin{minted}{clojure} (+ 1 1) ;=> 2 (/ 6 3) ;=> 2 (> 3 2 1) ;=> false (< 3 2 1) ;=> true (class (+ 1 1)) ;=> java.lang.Long \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Integers} \begin{minted}{clojure} ;; To the more experienced, ;; why does this not need +'? (class (+ 200000000000000000000 100000000000000000)) ;=> clojure.lang.BigInt \end{minted} \end{frame} \begin{frame} \frametitle{Floating points} \begin{itemize} \item{Standard java Double} \item{Automatically upgrade to BigDecimal with certain functions} \item{BigDecimal does not exist for ClojureScript} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Floating points} \begin{minted}{clojure} (+ 0.1 0.1) ; leading zero required ;=> 0.2 (* 0.1 0.1) ;=> 0.010000000000000002 (class (* 0.1 0.1)) ;=> java.lang.Double ;; Note the *', needed to promote doubles (class (*' (- 2M 2e-52M) 2e1023M)) ;=> java.math.BigDecimal \end{minted} \end{frame} \begin{frame} \frametitle{Ratios} \begin{itemize} \item{Represents fractions without using floating point} \item{Standard numeric operations work} \item{Automatically reduces} \item{Not in clojurescript} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Ratios} \begin{minted}{clojure} (/ 2 3) ;=> 2/3 (/ 3 6) ;=> 1/2 (+ 1/2 1/2) ;=> 1N (class (+ 1/2 1/2)) ;=> clojure.lang.BigInt (+ 3/4 0.25) ;=> 1.0 (class (+ 3/4 0.25)) ;=> java.lang.Double (+ 3/4 1) ;=> 7/4 \end{minted} \end{frame} \begin{frame} \frametitle{Strings} \begin{itemize} \item{Java strings} \item{Common to use Java methods to manipulate} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Strings} \begin{minted}{clojure} (.toUpperCase "blah") ;=> "BLAH" (.split "BLAH BLAH BLAH" "") ;=> ("BLAH" "BLAH" "BLAH") (clojure.string/upper-case "blah") ;=> "BLAH" (clojure.string/split "BLAH BLAH BLAH" "") ;=> ("BLAH" "BLAH" "BLAH") \end{minted} \end{frame} \begin{frame} \frametitle{Regexes} \begin{itemize} \item{Use either re-pattern or literal syntax} \item{Creates java.util.regex.Pattern} \item{Use re-seq, re-find, re-matches} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Regex} \begin{minted}{clojure} (re-seq #"[0-9]+" "abs123def345ghi567") ;=> ("123" "345" "567") (re-find #"([-+]?[0-9]+)/([0-9]+)" "22/7") ;=> ["22/7" "22" "7"] \end{minted} \end{frame} \begin{frame} \frametitle{Keywords} \begin{itemize} \item{Identifiers that just evaluate to themselves} \item{Can be used to lookup keys in maps} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Keywords} \begin{minted}{clojure} :foo ;=> :foo (keyword 'foo) ;=> :foo (:foo {:foo 1}) ;=> 1 \end{minted} \end{frame} \begin{frame} \frametitle{Symbols} \begin{itemize} \item{"Variables": Not really, but partially serve the same purpose} \item{They are identifiers pointing to something else} \item{They don't vary (hence not really variables)} \item{The actual storage location is called a var, symbol is just a name that points to it} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Symbols} \begin{minted}{clojure} (symbol 'foo) ;=> foo (symbol "foo") ;=> foo (symbol "clojure.core" "foo") ;=> clojure.core/foo (defn blah [] (println "blah")) ;=> #'user/blah (let [x 5] (+ x 2)) ;=> 7 \end{minted} \end{frame} \section{Sequences} \begin{frame} \frametitle{Sequences} \begin{itemize} \item{Generalized interface implemented by most collections.} \item{Many functions on sequences are lazy} \item{first} \item{rest} \item{cons} \end{itemize} \end{frame} \section{Collections} \begin{frame} All persistent(shared structure). Also implement the ISeq interface. \frametitle{Collections} \begin{itemize} \item{Lists} \item{Vectors} \item{Maps} \item{Sets} \item{Records} \end{itemize} \end{frame} \begin{frame} \frametitle{Lists} \begin{itemize} \item{Basic linked list in function} \item{Directly implements Sequence interface, except for empty list} \item{Conj puts the item at the front of the list} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Lists} \begin{minted}{clojure} '(1 2 3 4) ; Literal list ;=> (1 2 3 4) (list 1 2 3 4) ;=> (1 2 3 4) (+ 1 2) ; List as code structure ;=> 3 (first '(1 2 3 4)) ; Using list as data structure ;=> 1 (rest '(1 2 3 4)) ;=> (2 3 4) (conj '(1 2 3 4) 5) ;=> (5 1 2 3 4) \end{minted} \end{frame} \begin{frame} \frametitle{Vectors} \begin{itemize} \item{Very similar to Arrays in other languages} \item{Conj puts the item at the end of the vector} \item{Supports rseq function for quick reversal} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Vectors} \begin{minted}{clojure} [1 2 3 4 5] ; Literal vector ;=> [1 2 3 4 5] (vector 1 2 3 4 5) ;=> [1 2 3 4 5] (conj [1 2 3 4] 5) ;=> [1 2 3 4 5] (rseq [1 2 3 4 5]) ;=> (5 4 3 2 1) \end{minted} \end{frame} \begin{frame} \frametitle{Maps} \begin{itemize} \item{Standard key and value pairs} \item{Either hashed or sorted} \item{Conj expects another Map and merges them} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Maps} \begin{minted}{clojure} {:key1 1, :key1 2} ; Literal HashMap ;=> {:key1 2} (hash-map :key1 1, :key1 2) ;=> {:key1 2} (assoc {} :key1 1) ;=> {:key1 1} (dissoc {:key1 1} :key1) ;=> {} (conj {:key1 1} {:key2 2}) ;=> {:key1 1 :key2 2} \end{minted} \end{frame} \begin{frame} \frametitle{Sets} \begin{itemize} \item{Unique set of values} \item{Either hashed or sorted} \item{Conj adds new elements to set} \item{Various set operations including union, difference, and intersection.} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Sets} \begin{minted}{clojure} #{1 2 3} ; Literal HashSet ;=> #{1 2 3} (hash-set 1 2 3) ;=> #{1 2 3} (conj #{1 2 3} 4) ;=> #{1 2 3 4} (disj #{1 2 3} 3) ;=> #{1 2} \end{minted} \end{frame} \begin{frame} \frametitle{Basic collection functions} \begin{itemize} \item{Reduce: Standard functional reduce(fold left), iterate over collection and return a single accumulated result.} \item{Map: Standard functional map, iterate over collection and return a Sequence containing one value per a collection item.} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Basic collection functions} \begin{minted}{clojure} (reduce + [1 2 3 4 5]) ;=> 15 (reduce conj #{} [:a :b :c]) ;=> #{:a :b :c} (map inc [1 2 3]) ;=> (2 3 4) (map + [1 2 3] [4 5 6] [7 8 9]) ;=> (12 15 18) (defn wrong-arity-func [a b] (+ a b)) (map wrong-arity-func [1 2 3] [4 5 6] [7 8 9]) ;=> ArityException: Wrong number of args (3) ;=> passed to wrong-arity-func \end{minted} \end{frame} \section{Control Flow} \begin{frame} \frametitle{Looping/recursion} \begin{itemize} \item{Loop: Provides a lexical closure to recur to.} \item{Recur: Used for loop control flow as well as for general recursive functions.} \item{Recur will throw an exception if it is not in tail position} \item{Quite often your use case can be solved using map/reduce or similar instead.} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Looping/recursion} \begin{minted}{clojure} (loop [x 10] (when (> x 1) (println x) (recur (- x 2)))) ;=> 10 ;=> 2 (defn count-to-10 [x] (when (<= x 10) (println x) (recur (inc x)))) (count-to-10 5) ;=> 5 ;=> 10 \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Looping/recursion} \begin{minted}{clojure} (defn count-to-10 [x] (when (<= x 10) (recur (inc x)) (println x))) ;=> CompilerException java.lang ;=> .UnsupportedOperationException: Can only ;=> recur from tail position \end{minted} \end{frame} \begin{frame} \frametitle{Conditionals} \begin{itemize} \item{If: Basic if statement, only support true and false branches for one statement. } \item{Cond: More complex if, takes many different conditionals.} \item{Case: Standard switch case statement} \end{itemize} \end{frame} \begin{frame}[fragile] \frametitle{Conditionals} \begin{minted}{clojure} (if (even? 6) (println true) (println false)) ;=> true (if (even? 5) (println true) (println false)) ;=> false (cond (< 5 0) true (> 5 0) false :else nil) ;=> false \end{minted} \end{frame} \begin{frame}[fragile] \frametitle{Conditionals} \begin{minted}{clojure} (cond (< 0 5) true (> 0 5) false :else nil) ;=> true (let [mystr "no match"] (case mystr "" 0 "hello" (count mystr) "default")) ;=> "default" \end{minted} \end{frame} \section{Macros} \begin{frame} \frametitle{Macros} Very very very brief(and technically incorrect) explanation is: \\~\\ A 'function' that does not evaluate its arguments by default and returns a list that will then be evaluated as a code form. \\~\\ Read \url{http://www.braveclojure.com/writing-macros/} \end{frame} \begin{frame}[fragile] \frametitle{Macros} \begin{minted}{clojure} (defmacro pointless [n] n) (pointless (+ 1 2)) ;=> 3 (pointless 3) ;=> 3 (pointless [1 2 3 4]) ;=> [1 2 3 4] \end{minted} \end{frame} \begin{frame} \begin{center} \Huge Questions? \end{center} \end{frame} \end{document}