Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clojure, Plain and Simple

Ben Mabey
August 16, 2013

Clojure, Plain and Simple

Ben Mabey
Clojure, Plain and Simple
by Ben Mabey on Aug 16, 2013 Edit

1 views

This introduction to Clojure was given to the Utah Java Users Group Aug. 15. It's main focus was on Clojure's time model and how the design of Clojure separates (decomplects) many concepts which are all implemented on top of Objects in Java, and other OO languages. This is the abstract for the original talk:

Tony Hoare famously said "There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." Clojure is a functional Lisp that targets, among other platforms, the JVM and strives to enable the former approach to building software.

In its pursuit of simplicity Clojure encourages the use of pure functions, sequence abstractions which allow for lazy and parallel processing of data, persistent (immutable) data structures, and a novel way of dealing with state as a succession of values. While these concepts may sound intimidating for those unfamiliar with functional programming, they are actually less complicated than many programming constructs that programmers use everyday.

This talk will cover these concepts and the motivation behind them. You will learn the basics of Clojure programming and will be given a taste of what developing an application in Clojure is like.

Ben Mabey

August 16, 2013
Tweet

More Decks by Ben Mabey

Other Decks in Technology

Transcript

  1. "There are two ways of constructing a software design: One

    way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." Tony Hoare, 1980 Turing Award Lecture
  2. (defmacro when "Evaluates test. If logical true, evaluates body in

    an implicit do." [test & body] (list 'if test (cons 'do body)))
  3. (defmacro when "Evaluates test. If logical true, evaluates body in

    an implicit do." [test & body] (list 'if test (cons 'do body))) (macroexpand '(when (= code :RED) (launch-missiles!) (sound-the-alarm!)))
  4. (defmacro when "Evaluates test. If logical true, evaluates body in

    an implicit do." [test & body] (list 'if test (cons 'do body))) (macroexpand '(when (= code :RED) (launch-missiles!) (sound-the-alarm!))) => (if (= code :RED) (do (launch-missiles!) (sound-the-alarm!)))
  5. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts

    Logic Programming a’la Prolog - core.logic Lightweight threads + channels a’la Go - core.async
  6. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts

    Logic Programming a’la Prolog - core.logic Lightweight threads + channels a’la Go - core.async Optional/Gradual Type system - core.typed
  7. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts

    Logic Programming a’la Prolog - core.logic Lightweight threads + channels a’la Go - core.async Optional/Gradual Type system - core.typed Actor model a’la Erlang - pulsar
  8. Paradigms as Libraries Design by Contract a’la Eiffel - core.contracts

    Logic Programming a’la Prolog - core.logic Lightweight threads + channels a’la Go - core.async Optional/Gradual Type system - core.typed Actor model a’la Erlang - pulsar And more...
  9. “If you give someone Fortran, he has Fortran. If you

    give someone Lisp, he has any language he pleases.” Guy Steele
  10. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode) (macroexpand '(..

    person getAddress getState getCode)) (. (. (. person getAddress) getState) getCode)
  11. Chained Access person.getAddress().getState().getCode(); (.. person getAddress getState getCode) (macroexpand '(..

    person getAddress getState getCode)) (. (. (. person getAddress) getState) getCode) Clojure has parens so its Java doesn’t need to
  12. Implementing Interfaces new Runnable() { public void run() { System.out.println("Hello

    World"); } }; (reify Runnable (run [] (println "Hello")))
  13. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; }
  14. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } Reference Object
  15. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } Reference Object Mutable! Impure Function with side effects
  16. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } import org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); }
  17. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } import org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); } Value Object
  18. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } import org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); } Value Object Pure Function
  19. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } import org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { //MutableDateTime temp = new MutableDateTime(date); //temp.addYears(1); return date.plusYears(1); } Pure Function Nice Immutable API
  20. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } import org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { //MutableDateTime temp = new MutableDateTime(date); //temp.addYears(1); return date.plusYears(1); }
  21. public static int square(int x) { return x * x;

    } import java.util.Date; public static Date oneYearFrom(Date date) { date.setYear(date.getYear()+1); return date; } import org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { //MutableDateTime temp = new MutableDateTime(date); //temp.addYears(1); return date.plusYears(1); }
  22. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI
  23. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc.
  24. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc...
  25. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Default for domain objects
  26. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Default for domain objects
  27. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! Default for domain objects
  28. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! + They are values! Default for domain objects
  29. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! + They are values! - Copy on write (very good impls though) Default for domain objects
  30. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! + They are values! - Copy on write (very good impls though) - Can’t help with nesting Default for domain objects
  31. References Values primitives java.lang wrappers (Boolean, Byte, Character, Double, Float,

    Integer, Long, Short, String) other wrappers... e.g. UUID, URL, URI Date BigInteger,BigDecimal, etc. Libraries joda-time, joda-money google-guava Collections ArrayList,ArrayDeque,TreeSet, HashSet, TreeMap, HashMap, LinkedList,PriorityQueue,etc... Immutable Collections! + They are values! - Copy on write (very good impls though) - Can’t help with nesting Default for domain objects
  32. References Values primitives Java’s wrappers & libs Collections List, Vector,

    HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
  33. Explicit Ref Types atom ref agent References Values primitives Java’s

    wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords
  34. Explicit Ref Types atom ref agent References Values primitives Java’s

    wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords How Clojure addresses the non-functional aspect of programs, i.e. state.
  35. Explicit Ref Types atom ref agent References Values primitives Java’s

    wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords deftype
  36. Explicit Ref Types atom ref agent References Values primitives Java’s

    wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords Java libs for interop deftype
  37. Explicit Ref Types atom ref Persistent Collections References Values primitives

    Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values
  38. Explicit Ref Types atom ref Persistent Collections References Values primitives

    Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values + Structural Sharing + Memory efficient + Fast
  39. Explicit Ref Types atom ref Persistent Collections References Values primitives

    Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values + Structural Sharing + Memory efficient + Fast + Everything nests
  40. Explicit Ref Types atom ref Persistent Collections References Values primitives

    Java’s wrappers & libs Collections List, Vector, HashMap, TreeMap, ArrayMap, StructMap, Queue, HashSet, TreeSet, LazySeq, defrecords + They are values + Structural Sharing + Memory efficient + Fast + Everything nests The rest of Clojure and its ecosystem is built on these!
  41. Structural Sharing String brother = "brother"; String the = brother.substring(3,

    6); http://www.slreynolds.net/talks/clojure/collections/index.html
  42. http://www.innoq.com/blog/st/2010/04/clojure_performance_guarantees.html hash- map sorted- map hash- set sorted- set vector

    queue list lazy seq conj log32(n) log(n) log32(n) log(n) 1 1 1 1 assoc log32(n) log(n) log32(n) dissoc log32(n) log(n) disj log32(n) log(n) nth log32(n) n n n get log32(n) log(n) log32(n) log(n) log32(n) pop 1 1 1 1 peek 1 1 1 1 count 1 1 1 1 1 1 1 n TL;DR, they are fast
  43. Transients MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); import

    org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); }
  44. Transients MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); import

    org.joda.time.DateTime; public static DateTime oneYearFrom(DateTime date) { MutableDateTime temp = new MutableDateTime(date); temp.addYears(1); return temp.toDateTime(); } (transient data) (persistent! transient-data)
  45. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested

    2 :double]) Nesting First map key Index into vector Nested map key
  46. (update-in data [:nested 2 :double] upper-case) (get-in data [:nested 2

    :double]) > "nested" (def data {:nested [0 1 {:double "nested"}]}) Nesting
  47. (update-in data [:nested 2 :double] upper-case) (get-in data [:nested 2

    :double]) > "nested" (def data {:nested [0 1 {:double "nested"}]}) Nesting Same path
  48. (update-in data [:nested 2 :double] upper-case) (get-in data [:nested 2

    :double]) > "nested" (def data {:nested [0 1 {:double "nested"}]}) Nesting Fn applied to nested value
  49. (get-in data [:nested 2 :double]) > "nested" (def data {:nested

    [0 1 {:double "nested"}]}) Nesting (update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]} Entire “updated” data is returned as a value
  50. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested

    2 :double]) > "nested" Nesting (update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]} (assoc-in data [:nested 2 :another] "val")
  51. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested

    2 :double]) > "nested" Nesting (update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]} (assoc-in data [:nested 2 :another] "val") New key
  52. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested

    2 :double]) > "nested" Nesting (update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]} (assoc-in data [:nested 2 :another] "val") New value New key
  53. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested

    2 :double]) > "nested" Nesting (update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]} (assoc-in data [:nested 2 :another] "val") > {:nested [0 1 {:double "nested" :another "val"}]}
  54. (def data {:nested [0 1 {:double "nested"}]}) (get-in data [:nested

    2 :double]) > "nested" Nesting (update-in data [:nested 2 :double] upper-case) > {:nested [0 1 {:double "NESTED"}]} (assoc-in data [:nested 2 :another] "val") > {:nested [0 1 {:double "nested" :another "val"}]} Imagine doing this in Java.
  55. Easy ease < aise < adjacens lie near i.e. familiar,

    convenient, near to our skill set or current understanding
  56. Easy ease < aise < adjacens lie near i.e. familiar,

    convenient, near to our skill set or current understanding always relative! opposite of hard
  57. Simple sim - plex one fold/braid no interleaving! one concept,

    one dimension one role, but maybe multiple operations opposite of complex
  58. public class GameBoard { ! ! private List<ColoredSpace> spaces =

    new ArrayList<ColoredSpace>(); ! private CardDeck cardDeck = new CardDeck(); ! private List<Player> players = new ArrayList<Player>(); ! // Points to player whose turn it is next ! private int playerPointer = 0; ! // Players position on the board ! private Integer[] playerPositions; .... }
  59. public class GameBoard { ! ! private List<ColoredSpace> spaces =

    new ArrayList<ColoredSpace>(); ! private CardDeck cardDeck = new CardDeck(); ! private List<Player> players = new ArrayList<Player>(); ! // Points to player whose turn it is next ! private int playerPointer = 0; ! // Players position on the board ! private Integer[] playerPositions; .... } public class Player { ! private String name; ! public Player(String name) { ! ! this.name = name; ! } ! public String getName() { ! ! return name; ! } }
  60. {:players [{:location 32 :name "ben"} {:location 14 :name "maren"}] :board

    [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...] :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...) :played (:red :blue ...)}}
  61. {:players [{:location 32 :name "ben"} {:location 14 :name "maren"}] :board

    [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...] :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...) :played (:red :blue ...)}} Commas Optional
  62. distinct filter remove for keep keep-indexed cons concat lazy-cat mapcat

    cycle interleave interpose rest next fnext nnext drop drop-while nthnext for take take-nth take-while butlast drop-last for flatten reverse sort sort-by shuffle split-at split- with partition partition-all partition-by map pmap mapcat for replace reductions map-indexed seque first ffirst nfirst second nth when- first last rand-nth zipmap into reduce set vec into-array to-array-2d frequencies group-by apply not-empty some reduce seq? every? not- every? not-any? empty? some filter doseq dorun doall realized? assoc get get-in assoc-in update-in peek pop subvec conj cons into
  63. It is better to have 100 functions operate on one

    data structure than 10 functions on 10 data structures. Alan Perlis
  64. ( )

  65. Create a Game Board • 129 colored spaces • 6

    colors in repeating sequence: 1. Purple 2.Yellow 3.Blue 4.Orange 5.Green 6.Red
  66. Create a Game Board ! private static final int NUMBER_SPACES

    = 129; ! public static String[] COLOR_SEQUENCE = { ! ! "Purple", ! ! "Yellow", ! ! "Blue", ! ! "Orange", ! ! "Green", ! ! "Red" ! }; !
  67. Create a Game Board ! private static final int NUMBER_SPACES

    = 129; ! public static String[] COLOR_SEQUENCE = { ! ! "Purple", ! ! "Yellow", ! ! "Blue", ! ! "Orange", ! ! "Green", ! ! "Red" ! }; ! ! public GameBoard() { ! ! // Create Spaces ! ! for (int i = 0; i < NUMBER_SPACES; i++) { ! ! ! String color = COLOR_SEQUENCE[i % COLOR_SEQUENCE.length]; ! ! ! spaces.add(new ColoredSpace(color)); ! ! } ! ! ... } ! !
  68. (def colors [:purple :yellow :blue :orange :green :red]) user> (find-doc

    "cycle") ------------------------- clojure.core/check-cyclic-dependency ([path]) Detects .... ------------------------- clojure.core/cycle ([coll]) Returns a lazy (infinite!) sequence of repetitions of the items in coll. ProTip, use find-doc to search for fns
  69. (def colors [:purple :yellow :blue :orange :green :red]) user> (find-doc

    "cycle") ------------------------- clojure.core/check-cyclic-dependency ([path]) Detects .... ------------------------- clojure.core/cycle ([coll]) Returns a lazy (infinite!) sequence of repetitions of the items in coll.
  70. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...)
  71. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue)
  72. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) Laziness Increases Modularity
  73. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) Laziness Increases Modularity for (int i = 0; i < NUMBER_SPACES; i++) { ! ! ! String color = COLOR_SEQUENCE[i % COLOR_SEQUENCE.length]; ! ! ! spaces.add(new ColoredSpace(color)); ! ! }
  74. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue)
  75. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) Inside-Out Code
  76. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) Inside-Out Code (->> (cycle colors) (take 3)) => (:purple :yellow :blue) Threading Operators
  77. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) (->> (cycle colors) (take 3)) => (:purple :yellow :blue)
  78. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) (->> (cycle colors) (take 3)) => (:purple :yellow :blue) (defn make-space [color] {:color color})
  79. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) (->> (cycle colors) (take 3)) => (:purple :yellow :blue) (defn make-space [color] {:color color}) (->> colors (map make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue})
  80. (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors)

    => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) (->> (cycle colors) (take 3)) => (:purple :yellow :blue) (defn make-space [color] {:color color}) (->> colors (map make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue}) Small fns can be inlined
  81. (map make-space) (defn make-card [color] {:color color}) (->> colors (map

    make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue}) (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) (->> (cycle colors) (take 3)) => (:purple :yellow :blue) Small fns can be inlined (->> colors (map #(array-map :color %)) cycle (take 3))
  82. (map make-space) (defn make-card [color] {:color color}) (->> colors (map

    make-space) cycle (take 3)) => ({:color :purple} {:color :yellow} {:color :blue}) (def colors [:purple :yellow :blue :orange :green :red]) (cycle colors) => (:purple :yellow :blue :orange :green :red :purple :yellow :blue :orange ...) (take 3 (cycle colors)) => (:purple :yellow :blue) (->> (cycle colors) (take 3)) => (:purple :yellow :blue) Small fns can be inlined (->> colors (map #(array-map :color %)) cycle (take 3)) % is the anonymous first param
  83. (def game-board (->> [:purple :yellow :blue :orange :green :red] (map

    #(array-map :color %)) cycle (take 129))) ! public GameBoard() { ! ! // Create Spaces ! ! for (int i = 0; i < NUMBER_SPACES; i++) { ! ! ! String color = COLOR_SEQUENCE[i % COLOR_SEQUENCE.length]; ! ! ! spaces.add(new ColoredSpace(color)); ! ! } ! ! ... } ! ! Create a Game Board
  84. Play the game public class GameBoard { ! public void

    play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! }
  85. ! // Player pulls a card from the top of

    deck and moves player ! private boolean nextTurn() { ! ! // Player selects card from top of deck ! ! Card currCard = cardDeck.takeTopCard(); ! ! // If the game has ended, return now ! ! if (movePlayerOnBoard(currCard)) return false; ! ! // Next players turn ! ! playerPointer = (playerPointer + 1) % players.size(); ! ! // Game has not ended yet ! ! return true; ! } Play the game public class GameBoard { ! public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! } ! // Player pulls a card from the top of deck and moves player ! private boolean nextTurn() { ! ! // Player selects card from top of deck ! ! Card currCard = cardDeck.takeTopCard(); ! ! // If the game has ended, return now ! ! if (movePlayerOnBoard(currCard)) return false; ! ! // Next players turn ! ! playerPointer = (playerPointer + 1) % players.size(); ! ! // Game has not ended yet ! ! return true; ! }
  86. ! // Player pulls a card from the top of

    deck and moves player ! private boolean nextTurn() { ! ! // Player selects card from top of deck ! ! Card currCard = cardDeck.takeTopCard(); ! ! // If the game has ended, return now ! ! if (movePlayerOnBoard(currCard)) return false; ! ! // Next players turn ! ! playerPointer = (playerPointer + 1) % players.size(); ! ! // Game has not ended yet ! ! return true; ! } Play the game public class GameBoard { ! public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! } ! // Player pulls a card from the top of deck and moves player ! private boolean nextTurn() { ! ! // Player selects card from top of deck ! ! Card currCard = cardDeck.takeTopCard(); ! ! // If the game has ended, return now ! ! if (movePlayerOnBoard(currCard)) return false; ! ! // Next players turn ! ! playerPointer = (playerPointer + 1) % players.size(); ! ! // Game has not ended yet ! ! return true; ! } Reference Object Land
  87. ! // Player pulls a card from the top of

    deck and moves player ! private boolean nextTurn() { ! ! // Player selects card from top of deck ! ! Card currCard = cardDeck.takeTopCard(); ! ! // If the game has ended, return now ! ! if (movePlayerOnBoard(currCard)) return false; ! ! // Next players turn ! ! playerPointer = (playerPointer + 1) % players.size(); ! ! // Game has not ended yet ! ! return true; ! } Play the game public class GameBoard { ! public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! } ! // Player pulls a card from the top of deck and moves player ! private boolean nextTurn() { ! ! // Player selects card from top of deck ! ! Card currCard = cardDeck.takeTopCard(); ! ! // If the game has ended, return now ! ! if (movePlayerOnBoard(currCard)) return false; ! ! // Next players turn ! ! playerPointer = (playerPointer + 1) % players.size(); ! ! // Game has not ended yet ! ! return true; ! } Reference Object Land
  88. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game]
  89. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] Docstring attached as metadata
  90. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game
  91. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game Bind the keys we need with desctructuring
  92. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index)
  93. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks)
  94. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) Returns a value, pair of [card updated-decks]
  95. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) We destructure and bind the pair as we like
  96. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)]
  97. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)] (-> game (assoc :decks new-decks :player-index (-> player-index inc (mod (count players))))
  98. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)] (-> game (assoc :decks new-decks :player-index (-> player-index inc (mod (count players)))) (assoc-in [:players player-index :location] players-next-location))))
  99. (defn next-move "Takes the game forward one move by drawing

    a card for the current player and moving them accordingly." [game] (let [{:keys [decks board player-index players]} game player (get players player-index) [card new-decks] (draw-card decks) players-next-location (next-space card board player)] (-> game (assoc :decks new-decks :player-index (-> player-index inc (mod (count players)))) (assoc-in [:players player-index :location] players-next-location))))
  100. (defn play [game] (filter game-over?) first)) Play the game (->>

    (iterate next-move game) Text v1 next-move v2
  101. (defn play [game] (filter game-over?) first)) Play the game (->>

    (iterate next-move game) Text v1 next-move v2 v3 next-move
  102. (defn play [game] (filter game-over?) first)) Play the game (->>

    (iterate next-move game) Text v1 next-move v2 v3 next-move next-move ...
  103. (defn play [game] (filter game-over?) first)) Play the game (->>

    (iterate next-move game) Text v1 next-move v2 v3 next-move next-move ... (->> 1 (iterate inc) (take 5)) > (1 2 3 4 5)
  104. Play the game (defn play [game] (->> (iterate next-move game)

    (filter game-over?) first)) (filter game-over?) first)) Predicate function
  105. Play the game (defn play [game] (->> (iterate next-move game)

    (filter game-over?) first)) (filter game-over?) first)) Return the ended game value
  106. Play the game (defn play [game] (->> (iterate next-move game)

    (filter game-over?) first)) public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! }
  107. Play the game (defn play [game] (->> (iterate next-move game)

    (filter game-over?) first)) (->> (create-game ["Ben" "Maren"]) play winning-player :name (println "The winning player is:")) public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! }
  108. Play the game (defn play [game] (->> (iterate next-move game)

    (filter game-over?) first)) (->> (create-game ["Ben" "Maren"]) play winning-player :name (println "The winning player is:")) public void play() { ! ! while (nextTurn()); ! ! System.out.println(“The winning player is:” + players.get(playerPointer).getName()); ! } Our first side effect!
  109. {:players [{:location 32, :name "ben"} {:location 14, :name "maren"}] :board

    [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...], :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...), :played (:red, :blue, ...)}, :began-at #inst "2013-08-08T07:29:30.134-00:00"}
  110. {"players" : [ {"location" : 32,"name" : "ben"}, {"location" :

    14,"name" : "maren"} ], "board" : [{"color" : "purple"}, ... {"color" : "orange","shortcut-to" : 62}, ... {"color" : "yellow","picture" : "candy-heart"} ... ], "decks" : { "unplayed" : [ "red", ["orange", "orange"], "peppermint-stick", ...], "played" : [ "red", "blue" ...] }, "began-at" : "2013-08-08T07:29:30Z"}
  111. {:players [{:location 32, :name "ben"} {:location 14, :name "maren"}] :board

    [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...], :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...), :played (:red, :blue, ...)}, :began-at #inst "2013-08-08T07:29:30.134-00:00"}
  112. {:players [{:location 32, :name "ben"} {:location 14, :name "maren"}] :board

    [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...], :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...), :played (:red, :blue, ...)}, :began-at #inst "2013-08-08T07:29:30.134-00:00"} Custom Tagged Literal
  113. {:id 54321 :players [{:location 32 :name "ben"} {:location 14 :name

    "maren"}] :board [{:color :purple} ... {:color :orange, :shortcut-to 62} .... {:color :yellow, :picture :candy-heart} ...] :decks {:unplayed (:red [:orange :orange] :peppermint-stick ...) :played (:red :blue ...)} :began-at #inst "2013-08-08T07:29:30.134-00:00"}
  114. Game Reference (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move)

    Atomic Succession Fn to apply the ref’s current value to
  115. (swap! game-ref add-player "Carter") Game Reference (def game-ref (atom (create-game

    ...))) (swap! game-ref take-next-move) Additional args to fn
  116. (swap! game-ref add-player "Carter") Game Reference (deref game-ref) => current-game-value

    (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move)
  117. (swap! game-ref add-player "Carter") Game Reference (deref game-ref) => current-game-value

    (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move) Observers can deref to get current state
  118. (swap! game-ref add-player "Carter") Game Reference (deref game-ref) => current-game-value

    (def game-ref (atom (create-game ...))) (swap! game-ref take-next-move) @game-ref => current-game-value Observers can deref to get current state
  119. Clojure’s Time Model State is the current value of an

    identity. An identity is series of values over time. v1 v2 v3
  120. Clojure’s Time Model State is the current value of an

    identity. An identity is series of values over time. A reference to an identity allows updates and reads to it. v1 v2 v3
  121. Clojure’s Time Model State is the current value of an

    identity. An identity is series of values over time. A reference to an identity allows updates and reads to it. Values never change, the past never changes. v1 v2 v3
  122. (defn next-location [card board player] (let [spaces-after-player (->> board (drop

    (:location player))) next-color-id (find-index #(= (:color card) (:color %)))] (or next-color-id (:winning-location board))))
  123. (defn next-location [card board player] (let [spaces-after-player (->> board (drop

    (:location player))) next-color-id (find-index #(= (:color card) (:color %)))] (or next-color-id (:winning-location board))))
  124. (defrecord ColorCard [color] Card (next-location [_ board player] ....) (defrecord

    PictureCard [picture] Card (next-location [_ board player] (find-index #(= picture (:picture %)) board))) .... (defprotocol Card (next-location [card board player] "Determines the next location of the player"))
  125. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s]

    (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj]))
  126. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s]

    (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj])) (in-ns 'user) (require '[abstraction-a :as a]) (a/foo "Bar") => "foo-A-BAR" (a/foo nil) => "foo-A!"
  127. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s]

    (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj])) (in-ns 'user) (require '[abstraction-a :as a]) (a/foo "Bar") => "foo-A-BAR" (a/foo nil) => "foo-A!" (ns abstraction-b) (defprotocol AbstractionB (foo [obj]))
  128. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s]

    (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj])) (in-ns 'user) (require '[abstraction-a :as a]) (a/foo "Bar") => "foo-A-BAR" (a/foo nil) => "foo-A!" (extend-protocol AbstractionB nil (foo [s] (str "foo-B!")) String (foo [s] (str "foo-B-" (.toLowerCase s)))) (ns abstraction-b) (defprotocol AbstractionB (foo [obj]))
  129. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s]

    (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj])) (in-ns 'user) (require '[abstraction-a :as a]) (a/foo "Bar") => "foo-A-BAR" (a/foo nil) => "foo-A!" (extend-protocol AbstractionB nil (foo [s] (str "foo-B!")) String (foo [s] (str "foo-B-" (.toLowerCase s)))) (ns abstraction-b) (defprotocol AbstractionB (foo [obj])) (in-ns 'user) (require '[abstraction-b :as b]) (b/foo "Bar") => "foo-B-bar" (b/foo nil) => "foo-B!"
  130. (extend-protocol AbstractionA nil (foo [s] (str "foo-A!")) String (foo [s]

    (str "foo-A-" (.toUpperCase s)))) (ns abstraction-a) (defprotocol AbstractionA (foo [obj])) (in-ns 'user) (require '[abstraction-a :as a]) (a/foo "Bar") => "foo-A-BAR" (a/foo nil) => "foo-A!" (extend-protocol AbstractionB nil (foo [s] (str "foo-B!")) String (foo [s] (str "foo-B-" (.toLowerCase s)))) (ns abstraction-b) (defprotocol AbstractionB (foo [obj])) (in-ns 'user) (require '[abstraction-b :as b]) (b/foo "Bar") => "foo-B-bar" (b/foo nil) => "foo-B!" Polymorphic functions live in namespaces, not complected on Class
  131. (require '[clojure.core.reducers :as r]) (->> (range 100000) (r/map inc) (r/reduce

    +)) (->> (range 100000) (map inc) (reduce +)) Process sequences in parallel with ForkJoin
  132. (require '[clojure.core.reducers :as r]) (->> (range 100000) (r/map inc) (r/reduce

    +)) (->> (range 100000) (map inc) (reduce +)) Process sequences in parallel with ForkJoin The same “what”, different “how”
  133. (extend-protocol CollFold nil (coll-fold [coll n combinef reducef] (combinef)) Object

    (coll-fold [coll n combinef reducef] ;;can't fold, single reduce (reduce reducef (combinef) coll)) clojure.lang.IPersistentVector (coll-fold [v n combinef reducef] (foldvec v n combinef reducef)) clojure.lang.PersistentHashMap (coll-fold [m n combinef reducef] (.fold m n combinef reducef fjinvoke fjtask fjfork fjjoin)))
  134. Clojure’s Time Model A reference to an identity allows updates

    and reads to it. Values never change, the past never changes. v1 v2 v3 State is the current value of an identity. An identity is series of values over time.
  135. Clojure’s Time Model A reference to an identity allows updates

    and reads to it. Values never change, the past never changes. v1 v2 v3 State is the current value of an identity. An identity is series of values over time. Observers can remember the past
  136. (defn shadow-ref "Returns a ref that contains the time -

    1 value of the given ref. In other words, shawdow-ref contains the value of ref before the las update to it (e.g. swap!). " [ref] (let [shadow (atom nil)] (add-watch ref :shawdower (fn [_key _ref old-state _new-state] (reset! shadow old-state))) shadow)) (def old-game-ref (shadow-ref game-ref))
  137. (defn undo-and-skip-card [game-ref old-game-ref] (let [alternate-reality (-> @old-game-ref skip-card take-next-move)]

    (reset! game-ref alternate-reality))) (defn shadow-ref "Returns a ref that contains the time - 1 value of the given ref. In other words, shawdow-ref contains the value of ref before the las update to it (e.g. swap!). " [ref] (let [shadow (atom nil)] (add-watch ref :shawdower (fn [_key _ref old-state _new-state] (reset! shadow old-state))) shadow)) (def old-game-ref (shadow-ref game-ref))
  138. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is

    slower than idiomatic Java in micro benchmarks.
  139. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is

    slower than idiomatic Java in micro benchmarks. Not as much structure provided (e.g. no familiar class structure), easier to make a mess.
  140. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is

    slower than idiomatic Java in micro benchmarks. Not as much structure provided (e.g. no familiar class structure), easier to make a mess. Tool support. Not many great IDE plugins conveniently available.
  141. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is

    slower than idiomatic Java in micro benchmarks. Not as much structure provided (e.g. no familiar class structure), easier to make a mess. Tool support. Not many great IDE plugins conveniently available. Harder to hire for?
  142. Tradeoffs Different way of thinking takes time. Idiomatic Clojure is

    slower than idiomatic Java in micro benchmarks. Not as much structure provided (e.g. no familiar class structure), easier to make a mess. Tool support. Not many great IDE plugins conveniently available. Harder to hire for?
  143. ( ) Free Read Watch Do Free clojure.org clojure.org/cheatsheet clojure-doc.org

    Tutorials clojuredocs.org Examples youtube.c/user/ClojureTV infoq.com/Clojure/presentations .com Free clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples youtube.c/user/ClojureTV infoq.com/Clojure/presentations Free clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples youtube.c/user/ClojureTV infoq.com/Clojure/presentations $ clojure.org clojure.org/cheatsheet clojure-doc.org Tutorials clojuredocs.org Examples $ Clojure/Conj clojure-conj.org Training clojure.com $ Clojure/Conj clojure-conj.org Training clojure.com $ Clojure/Conj clojure-conj.org Training clojure.com
  144. C# Async async void Go() { _button.IsEnabled = false; string[]

    urls = "clojure.org www.albahari.com/nutshell/ golang.org".Split(); int totalLength = 0; foreach (string url in urls) { var uri = new Uri ("http://" + url); byte[] data = await new WebClient().DownloadDataTaskAsync (uri); _results.Text += "Length of " + url + " is " + data.Length + totalLength += data.Length; } _results.Text += "Total length: " + totalLength; }
  145. CSP in Go // Run the Web, Image, and Video

    searches concurrently, // and wait for all results. // No locks. No condition variables. No callbacks. func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- Web(query) } () go func() { c <- Image(query) } () go func() { c <- Video(query) } () for i := 0; i < 3; i++ { result := <-c results = append(results, result) } return } // http://talks.golang.org/2012/concurrency.slide#46
  146. Go in Clojure (use 'clojure.core.async) (defn google [query] (let [c

    (chan)] (go (>! c (<! (web query)))) (go (>! c (<! (image query)))) (go (>! c (<! (video query)))) (go (loop [i 0 ret []] (if (= i 3) ret (recur (inc i) (conj ret (alt! [c t] ([v] v)))))))))
  147. “APL is like a beautiful diamond - flawless, beautifully symmetrical.

    But you can't add anything to it. If you try to glue on another diamond, you don't get a bigger diamond. Lisp is like a ball of mud. Add more and it's still a ball of mud - it still looks like Lisp.” Joel Moses, 1970s
  148. Erlang Actors in Clojure ;; http://puniverse.github.io/pulsar/ (use 'co.paralleluniverse.pulsar.core) (let [actor

    (spawn #(receive :abc "yes!" [:why? answer] answer :else "oy"))] (! actor [:why? "because!"]) (join actor)) ; => "because!"
  149. Color # in Deck Red 6 Orange 4 Yellow 6

    Green 4 Blue 6 Purple 4 Create a Card Deck
  150. Create a Card Deck public class CardDeck { ! private

    Stack<Card> cards; ! private static final Map<String, Integer> CARD_GROUPS; ! static { ! ! CARD_GROUPS = new HashMap<String, Integer>(); ! ! CARD_GROUPS.put("Red", 4); ! ! CARD_GROUPS.put("Orange", 4); ! ! CARD_GROUPS.put("Yellow", 6); ! ! CARD_GROUPS.put("Green", 4); ! ! CARD_GROUPS.put("Blue", 6); ! ! CARD_GROUPS.put("Purple", 4); ! }
  151. Create a Card Deck ! private void addCardsToDeck() { !

    ! cards = new Stack<Card>(); ! ! // Add cards to deck based on color and number ! ! for (Map.Entry<S,I> cardGroupEntry : CARD_GROUPS.entrySet()) { ! ! ! String color = cardGroupEntry.getKey(); ! ! ! int numCardsInGroup = cardGroupEntry.getValue(); ! ! ! for (int i = 0; i < numCardsInGroup; i++) { ! ! ! ! cards.push(new Card(color)); ! ! ! } ! ! } ! }
  152. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4})
  153. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red)
  154. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue))
  155. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue)) (map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue))
  156. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue)) (map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue)) (mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue)
  157. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue)) (map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue)) (mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue) (defn create-deck [face-freqs]
  158. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue)) (map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue)) (mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue) (defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))
  159. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue)) (map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue)) (mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue) (defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs)) (def deck
  160. Create a Card Deck (def card-counts {:red 6 :orange 4

    :yellow 6 :green 4 :blue 6 :purple 4}) (repeat 3 :red) => (:red :red :red) (map (fn [pair] (repeat (last pair) (first pair))) {:red 2 :blue 2})=> ((:red :red) (:blue :blue)) (map (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => ((:red :red) (:blue :blue)) (mapcat (fn [[face freq]] (repeat freq face)) {:red 2 :blue 2}) => (:red :red :blue :blue) (defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs)) (def deck (create-deck card-counts))
  161. Create a Card Deck ! private void addCardsToDeck() { !

    ! cards = new Stack<Card>(); ! ! // Add cards to deck based on color and number ! ! for (Map.Entry<S,I> cardGroupEntry : CARD_GROUPS.entrySet()) { ! ! ! String color = cardGroupEntry.getKey(); ! ! ! int numCardsInGroup = cardGroupEntry.getValue(); ! ! ! for (int i = 0; i < numCardsInGroup; i++) { ! ! ! ! cards.push(new Card(color)); ! ! ! } ! ! } ! }
  162. Create a Card Deck ! private void addCardsToDeck() { !

    ! cards = new Stack<Card>(); ! ! // Add cards to deck based on color and number ! ! for (Map.Entry<S,I> cardGroupEntry : CARD_GROUPS.entrySet()) { ! ! ! String color = cardGroupEntry.getKey(); ! ! ! int numCardsInGroup = cardGroupEntry.getValue(); ! ! ! for (int i = 0; i < numCardsInGroup; i++) { ! ! ! ! cards.push(new Card(color)); ! ! ! } ! ! } ! } (defn create-deck [face-freqs] (mapcat (fn [[face freq]] (repeat freq face)) face-freqs))