Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1204 lines (990 sloc) 23.6 KB
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>(Clojure School)</title>
<meta name="author" content="(Likely)"/>
<link rel="stylesheet" href="./reveal.js/css/reveal.min.css"/>
<link rel="stylesheet" href="./reveal.js/css/theme/solarized.css" id="theme"/>
<link rel="stylesheet" href="css/zenburn.css"/>
<link rel="stylesheet" href="./reveal.js/css/print/pdf.css" type="text/css" media="print"/>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>Clojure School</h1>
<h2>Likely</h2>
<h2><a href="mailto:"></a></h2>
<h2></h2></section>
<section>
<section id="sec-1" >
<h2>Welcome</h2>
</section>
</section>
<section>
<section id="sec-2" >
<h2>Introductions</h2>
<ul class="org-ul">
<li>Henry Garner (CTO, Likely)
</li>
<li>James Henderson (Senior Developer, Likely)
</li>
</ul>
<p>
Professionally using Clojure for 18 months
</p>
</section>
</section>
<section>
<section id="sec-3" >
<h2>Who are you?</h2>
<p>
&#x2026; and what are you hoping to get out of these sessions?
</p>
</section>
</section>
<section>
<section id="sec-4" >
<h2>What we'll cover today</h2>
<ul class="org-ul">
<li>Getting up and running
</li>
<li>Language primitives
</li>
<li>Working with sequences
</li>
<li>Working with higher order functions
</li>
<li>Working with real data
</li>
<li>Moving beyond the REPL
</li>
</ul>
</section>
</section>
<section>
<section id="sec-5" >
<h2>Where will we get to in 4 weeks?</h2>
<ul class="org-ul">
<li>The ability to create your own non-trivial Clojure projects
</li>
</ul>
</section>
</section>
<section>
<section id="sec-6" >
<h2>Housekeeping</h2>
<ul class="org-ul">
<li>Experience of LISP?
</li>
<li>Other functional languages?
</li>
<li>What text editor?
</li>
<li>Who has Java?
</li>
<li>Who has leiningen?
</li>
</ul>
</section>
</section>
<section>
<section id="sec-7" >
<h2>Getting Set Up</h2>
<p>
First, you’ll need a Java Virtual Machine, or JVM, and its associated development tools, called the JDK. This is the software which runs a Clojure program. If you’re on Windows, install Oracle JDK 1.7. If you’re on OS X or Linux, you may already have a JDK installed. In a terminal, try:
</p>
<pre><code data-trim class="clojure">
which javac
</code></pre>
<p>
If you see something like
</p>
<pre><code data-trim class="clojure">
/usr/bin/javac
</code></pre>
<p>
Then you're good to go
</p>
<p>
Windows users: <a href="http://leiningen.org/#install">http://leiningen.org/#install</a>
</p>
</section>
</section>
<section>
<section id="sec-8" >
<h2>Leiningen</h2>
<div class="org-src-container">
<pre class="src src-bash">mkdir -p ~/bin
cd ~/bin
curl -O https://raw.github.com/technomancy/leiningen/stable/bin/lein
chmod a+x lein
</pre>
</div>
<div class="org-src-container">
<pre class="src src-bash">cd
lein new scratch
</pre>
</div>
<div class="org-src-container">
<pre class="src src-bash">export PATH="$PATH":~/bin
</pre>
</div>
</section>
</section>
<section>
<section id="sec-9" >
<h2>REPL</h2>
<div class="org-src-container">
<pre class="src src-bash">lein repl
</pre>
</div>
<p>
This is an interactive Clojure environment called a REPL, for “Read, Evaluate, Print Loop”. It’s going to read a program we enter, run that program, and print the results. REPLs give you quick feedback, so they’re a great way to explore a program interactively, run tests, and prototype new ideas.
</p>
</section>
</section>
<section>
<section id="sec-10" >
<h2>REPL</h2>
<p>
With most languages, you write the system from the outside.
</p>
<p>
With LISPs, you bring the system up and develop it from the inside.
</p>
</section>
</section>
<section>
<section id="sec-11" >
<h2>Jumping inside</h2>
<pre><code data-trim class="clojure">
user=> nil
nil
</code></pre>
<p>
nil is the most basic value in Clojure.
</p>
</section>
</section>
<section>
<section id="sec-12" >
<h2>Boolean values</h2>
<pre><code data-trim class="clojure">
user=> true
true
user=> false
false
</code></pre>
</section>
</section>
<section>
<section id="sec-13" >
<h2>Basic Types</h2>
<pre><code data-trim class="clojure">
0
-42
1.2e-5
1/3
"Hi there!"
\space
\E
:keywords
#"\d+"
</code></pre>
</section>
</section>
<section>
<section id="sec-14" >
<h2>Collection Types</h2>
<p>
Maps
</p>
<pre><code data-trim class="clojure">
{:a 1 :b 2}
</code></pre>
<p>
Sets
</p>
<pre><code data-trim class="clojure">
#{1 2 3}
</code></pre>
<p>
Vectors
</p>
<pre><code data-trim class="clojure">
[1 2 3]
</code></pre>
<p>
&#x2026; that's it!
</p>
<p>
"It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures." —Alan Perlis
</p>
</section>
</section>
<section>
<section id="sec-15" >
<h2>Def</h2>
<pre><code data-trim class="clojure">
user=> (def x 3)
#'user/x
</code></pre>
<p>
We've defined a var in the 'user' namespace and can refer to it:
</p>
<pre><code data-trim class="clojure">
user=> x
3
</code></pre>
</section>
</section>
<section>
<section id="sec-16" >
<h2>Lists</h2>
<pre><code data-trim class="clojure">
user=> (1 2 3)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn
user/eval146 (NO_SOURCE_FILE:1)
</code></pre>
</section>
</section>
<section>
<section id="sec-17" >
<h2>Wha happen?</h2>
<p>
The REPL sees a list and treats it as a function invocation.
</p>
<p>
The first element in the list is always the function to be invoked, with any remaining elements passed as arguments.
</p>
</section>
</section>
<section>
<section id="sec-18" >
<h2>Function Invocation</h2>
<pre><code data-trim class="clojure">
user=> (inc 0)
1
user=> (inc x)
4
</code></pre>
</section>
</section>
<section>
<section id="sec-19" >
<h2>Nesting</h2>
<pre>
Increment
increment
the number
</pre>
<pre><code data-trim class="clojure">
user=> (inc (inc 0))
2
</code></pre>
</section>
</section>
<section>
<section id="sec-20" >
<h2>Evaluation</h2>
<p>
Every list starts with a verb. Parts of a list are evaluated from left to right. Innermost lists are evaluated before outer lists.
</p>
<pre><code data-trim class="clojure">
(+ 1 (- 5 2) (+ 3 4))
(+ 1 3 (+ 3 4))
(+ 1 3 7)
11
</code></pre>
</section>
</section>
<section>
<section id="sec-21" >
<h2>Control structures:</h2>
<pre><code data-trim class="clojure">
user=> (if (> 3 2) "Higher" "Lower")
"Higher"
user=> (when (< 3 2) "Lower")
nil
user=> (when (> 3 2)
(println "3 is greater than 2")
"Higher")
3 is greater than 2
"Higher"
</code></pre>
<p>
See also: `if-not` and `when-not`
</p>
</section>
</section>
<section>
<section id="sec-22" >
<h2>More conditionals</h2>
<pre><code data-trim class="clojure">
user=> (case (inc 3)
3 "Uh oh"
4 "Yep!"
"Not so sure...")
"Yep!"
user=> (cond
(= 4 (inc 2)) "(inc 2) is 4"
(= 4 (/ 8 2)) "Cond picks the first correct case"
(zero? (- (* 4 2) 8) "This is true, but we won't get here"
:otherwise "None of the above."
"Cond picks the first correct case"
</code></pre>
<p>
See also: condp
</p>
</section>
</section>
<section>
<section id="sec-23" >
<h2>Having fn yet?</h2>
<pre><code data-trim class="clojure">
user=> (fn [x] (+ x 1))
#<user$eval149$fn__150 user$eval149$fn__150@397d812b>
</code></pre>
<p>
We've created a function!
</p>
<pre><code data-trim class="clojure">
user=> (fn [x]
(if (even? x)
(inc x)
(dec x)))
#<user$eval149$fn__150 user$eval149$fn__150@397d812c>
</code></pre>
</section>
</section>
<section>
<section id="sec-24" >
<h2>Usage</h2>
<pre><code data-trim class="clojure">
user=> ((fn [x] (+ x 1)) 10)
11
</code></pre>
<p>
You probably won't see this in production code&#x2026;
</p>
</section>
</section>
<section>
<section id="sec-25" >
<h2>Defn</h2>
<pre><code data-trim class="clojure">
user=> (def half
(fn [number]
(/ number 2)))
#'user/half
user=> (half 6)
3
</code></pre>
<p>
Creating a function and binding it to a var is so common that it has its own form: defn, short for def fn.
</p>
<pre><code data-trim class="clojure">
user=> (defn half [number]
(/ number 2))
#'user/half
</code></pre>
</section>
</section>
<section>
<section id="sec-26" >
<h2>Function Arity</h2>
<p>
Functions don’t have to take an argument. We’ve seen functions which take zero arguments, like (+).
</p>
<pre><code data-trim class="clojure">
user=> (defn half [] 1/2)
#'user/half
user=> (half)
1/2
</code></pre>
<p>
But if we try to use our earlier form with one argument, Clojure complains that the arity–the number of arguments to the function–is incorrect.
</p>
<pre><code data-trim class="clojure">
user=> (half 10)
ArityException Wrong number of args (1) passed to:
user$half clojure.lang.AFn.throwArity (AFn.java:437)
</code></pre>
</section>
</section>
<section>
<section id="sec-27" >
<h2>Multiple Arities</h2>
<p>
To handle multiple arities, functions have an alternate form. Instead of an argument vector and a body, one provides a series of lists, each of which starts with an argument vector, followed by the body.
</p>
<pre><code data-trim class="clojure">
user=> (defn half
([] 1/2)
([x] (/ x 2)))
#'user/half
user=> (half)
1/2
user=> (half 10)
5
</code></pre>
</section>
</section>
<section>
<section id="sec-28" >
<h2>Variable Arities</h2>
<p>
Some functions can take any number of arguments. For that, Clojure provides &amp;, which slurps up all remaining arguments as a list:
</p>
<pre><code data-trim class="clojure">
user=> (defn vargs
[x y & more-args]
{:x x
:y y
:more more-args})
#'user/vargs
user=> (vargs 1)
ArityException Wrong number of args (1) passed to: user$vargs
clojure.lang.AFn.throwArity (AFn.java:437)
user=> (vargs 1 2)
{:x 1, :y 2, :more nil}
user=> (vargs 1 2 3 4 5)
{:x 1, :y 2, :more (3 4 5)}
</code></pre>
</section>
</section>
<section>
<section id="sec-29" >
<h2>Bindings</h2>
<p>
We know that symbols are names for things, and that when evaluated, Clojure replaces those symbols with their corresponding values.
</p>
<pre><code data-trim class="clojure">
user=> +
#<core$_PLUS_ clojure.core$_PLUS_@12992c>
</code></pre>
<p>
We can create names which are locally scoped.
</p>
<pre><code data-trim class="clojure">
user=> (let [cats 5]
(str "I have " cats " cats."))
"I have 5 cats."
</code></pre>
</section>
</section>
<section>
<section id="sec-30" >
<h2>Bindings are local</h2>
<p>
Let bindings apply only within the let expression itself. They also override any existing definitions for symbols at that point in the program. For instance, we can redefine addition to mean subtraction, for the duration of a let:
</p>
<pre><code data-trim class="clojure">
user=> (let [+ -]
(+ 2 3))
-1
</code></pre>
<p>
But that definition doesn’t apply outside the let:
</p>
<pre><code data-trim class="clojure">
user=> (+ 2 3)
5
</code></pre>
</section>
</section>
<section>
<section id="sec-31" >
<h2>Bindings can be composed</h2>
<p>
We can also provide multiple bindings. Since Clojure doesn’t care about spacing, alignment, or newlines, I’ll write this on multiple lines for clarity.
</p>
<pre><code data-trim class="clojure">
user=> (let [person "joseph"
num-cats 186]
(str person " has " num-cats " cats!"))
"joseph has 186 cats!"
</code></pre>
<p>
When multiple bindings are given, they are evaluated in order. Later bindings can use previous bindings.
</p>
<pre><code data-trim class="clojure">
user=> (let [cats 3
legs (* 4 cats)]
(str legs " legs all together"))
"12 legs all together"
</code></pre>
</section>
</section>
<section>
<section id="sec-32" >
<h2>Keywords as functions</h2>
<pre><code data-trim class="clojure">
user=> (def my-map {:a 1 :b 2})
#'user/my-map
user=> (:a my-map)
1
</code></pre>
</section>
</section>
<section>
<section id="sec-33" >
<h2>Destructuring</h2>
<pre><code data-trim class="clojure">
user=> (def my-map {:a 1 :b 2 :c [3 4 5]})
#'user/my-map
user=> (let [a (:a my-map)
b (:b my-map)]
(+ a b))
3
user=> (let [{a :a b :b} my-map]
(+ a b))
3
user=> (let [{:keys [a b]} my-map]
(+ a b))
3
</code></pre>
</section>
</section>
<section>
<section id="sec-34" >
<h2>Nested Destructuring</h2>
<pre><code data-trim class="clojure">
user=> (let [{:keys [c]} my-map
[c1 c2 c3] c]
(+ c1 c2 c3))
12
user=> (let [{[c1 c2 c3] :c} my-map]
(+ c1 c2 c3))
12
</code></pre>
</section>
</section>
<section>
<section id="sec-35" >
<h2>A brief tour of clojure.core</h2>
<p>
Working with maps:
</p>
<pre><code data-trim class="clojure">
user=> (def my-map {:a 1 :b 2})
#'user/my-map
user=> (assoc my-map :c 3)
{:a 1, :c 3, :b 2}
user=> (dissoc my-map :a)
{:b 2}
user=> my-map
{:a 1, :b 2}
</code></pre>
</section>
</section>
<section>
<section id="sec-36" >
<h2>A brief tour of clojure.core</h2>
<p>
Working with maps:
</p>
<pre><code data-trim class="clojure">
user=> (def my-map {:a 1
:b 2
:c {:d 4
:e 5}})
#'user/my-map
user=> (keys my-map)
(:a :c :b)
user=> (vals my-map)
(1 {:d 4, :e 5} 2)
user=> (assoc-in my-map [:c :f] 6)
{:a 1, :c {:f 6, :d 4, :e 5}, :b 2}
</code></pre>
</section>
</section>
<section>
<section id="sec-37" >
<h2>Vector functions</h2>
<pre><code data-trim class="clojure">
user=> (def my-coll [2 3 1 5 6 4 0])
#'user/my-coll
user=> (first my-coll)
2
user=> (second my-coll)
3
user=> (nth my-coll 4)
6
user=> (conj my-coll 7)
[2 3 1 5 6 4 0 7]
user=> my-coll
[2 3 1 5 6 4 0]
</code></pre>
</section>
</section>
<section>
<section id="sec-38" >
<h2>Vector functions</h2>
<pre><code data-trim class="clojure">
user=> (def my-coll [2 3 1 5 6 4 0])
#'user/my-coll
user=> (sort my-coll)
(0 1 2 3 4 5 6)
user=> (interpose -1 my-coll)
(2 -1 3 -1 1 -1 5 -1 6 -1 4 -1 0)
user=> (zipmap [:a :b :c :d :e :f] my-coll)
{:f 4, :e 6, :d 5, :c 1, :b 3, :a 2}
user=> (frequencies "Hello world!")
{\space 1, \! 1, \d 1, \e 1, \H 1, \l 3, \o 2, \r 1, \w 1}
</code></pre>
<p>
See also: concat, interleave, cons, last, butlast
</p>
</section>
</section>
<section>
<section id="sec-39" >
<h2>Sheer laziness</h2>
<p>
While Clojure is technically eager by default, most of the functions on collections operate lazily:
</p>
<pre><code data-trim class="clojure">
user=> (def my-coll [0 1 2 3 4 5 6])
#'user/my-coll
user=> (take 3 my-coll)
(0 1 2)
user=> (drop 2 my-coll)
(2 3 4 5 6)
user=> (partition 3 my-coll)
((0 1 2) (3 4 5))
user=> (partition-all 3 my-coll)
((0 1 2) (3 4 5) (6))
user=> (split-at 3 my-coll)
[(0 1 2) (3 4 5 6)]
user=> (range)
;; good luck with this one! (cancel with Ctrl-c)
user=> (range 5)
(0 1 2 3 4)
user=> (take 5 (range))
(0 1 2 3 4)
</code></pre>
</section>
</section>
<section>
<section id="sec-40" >
<h2>Higher order functions</h2>
<p>
Functions that accept or return functions
</p>
<pre><code data-trim class="clojure">
user=> (def names [{:forename "Henry" :surname "Garner"}
{:forename "James" :surname "Henderson"}])
#'user/names
user=> (defn full-name [{:keys [forename surname]}]
(str forename " " surname))
#'user/full-name
user=> (full-name (first names))
"Henry Garner"
user=> (map full-name names)
["Henry Garner" "James Henderson"]
</code></pre>
</section>
</section>
<section>
<section id="sec-41" >
<h2>Anonymous Functions</h2>
<p>
Used where you have a case for a single-use function that doesn't warrant a name.
</p>
<pre><code data-trim class="clojure">
user=> (def names [{:forename "Henry" :surname "Garner"}
{:forename "James" :surname "Henderson"}])
#'user/names
user=> (defn full-name [forename surname]
(str forename " " surname))
#'user/full-name
user=> (map (fn [x] (full-name (:forename x) (:surname x))) names)
;; Equivalent to
user=> (map #(full-name (:forename %) (:surname %)) names)
</code></pre>
</section>
</section>
<section>
<section id="sec-42" >
<h2>Anonymous function arities</h2>
<p>
You can refer to multiple args by %1, %2, &#x2026;
</p>
<pre><code data-trim class="clojure">
(fn [x y] (+ x y))
;; Equivalent to
#(+ %1 %2)
</code></pre>
</section>
</section>
<section>
<section id="sec-43" >
<h2>Other higher-order functions</h2>
<p>
Higher order functions can make use of functions.
</p>
<pre><code data-trim class="clojure">
user=> (update-in {:name "Henry" :age 30} [:age] inc)
{:name "Henry" :age 31}
</code></pre>
</section>
</section>
<section>
<section id="sec-44" >
<h2>Sequence-Sequence higher order functions</h2>
<pre><code data-trim class="clojure">
user=> (map inc [1 2 3 4])
(2 3 4 5)
user=> (filter even? [1 2 3 4 5 6])
(2 4 6)
user=> (sort-by count ["bb" "aaa" "c"]
("c" "bb" "aaa")
user=> (sort-by first > [[1 2] [2 2] [3 3]])
([3 3] [2 2] [1 2])
</code></pre>
<p>
See also: mapcat, remove, partition-by
</p>
</section>
</section>
<section>
<section id="sec-45" >
<h2>Sequence in &gt; Something else out</h2>
<pre><code data-trim class="clojure">
user=> (reduce + [1 2 3])
6
user=> (group-by even? [1 2 3 4])
{false [1 3], true [2 4]}
</code></pre>
</section>
</section>
<section>
<section id="sec-46" >
<h2>Namespaces</h2>
<p>
In the REPL we get a 'user' namespace. In larger projects we like to split our code out into more namespaces.
</p>
<p>
We can refer to symbols in other namespaces.
</p>
<pre><code data-trim class="clojure">
(ns some.namespace
(:require [other.namespace :as blah]))
</code></pre>
</section>
</section>
<section>
<section id="sec-47" >
<h2>Leiningen's project.clj</h2>
<pre><code data-trim class="clojure">
(defproject weather "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[clj-http "0.7.7"]])
</code></pre>
</section>
</section>
<section>
<section id="sec-48" >
<h2>Your code goes here</h2>
<p>
src/weather/core.clj
</p>
<p>
Open up that file and remove the template function.
</p>
</section>
</section>
<section>
<section id="sec-49" >
<h2>Add dependencies</h2>
<pre><code data-trim class="clojure">
(ns weather.core
(:require [clj-http.client :as http]))
</code></pre>
</section>
</section>
<section>
<section id="sec-50" >
<h2>Let's use some real data</h2>
<p>
<a href="http://openweathermap.org/API">http://openweathermap.org/API</a>
</p>
<p>
Free, JSON api that provides current weather data and forecasts.
</p>
</section>
</section>
<section>
<section id="sec-51" >
<h2>Sample questions</h2>
<ul class="org-ul">
<li>How many cities called London are there? (hint: find?q=London)
</li>
<li>What are the lat/long positions of all the Londons?
</li>
<li>What has been the average temperature of London, UK for the last 5 days? (hint: forecast?q=London)
</li>
<li>What has been the average temperature of London, UK for the last 10 days?
</li>
<li>On how many of the last 10 days has it been cloudy?
</li>
<li>On how many of the last 10 days has it not been cloudy?
</li>
</ul>
</section>
</section>
<section>
<section id="sec-52" >
<h2>One I prepared earlier</h2>
<p>
<a href="https://github.com/likely/weather">https://github.com/likely/weather</a>
</p>
</section>
</section>
<section>
<section id="sec-53" >
<h2>Clojure the Parasite</h2>
<p>
Clojure is, by its very nature, a hosted language.
</p>
<p>
Stable:
</p>
<ul class="org-ul">
<li>Clojure (obviously!) - targets the JVM
</li>
<li>ClojureScript - compiles to JavaScript
</li>
</ul>
<p>
On the way:
</p>
<ul class="org-ul">
<li>Clojure.NET
</li>
<li>clojurescript-lua
</li>
<li>clojurec
</li>
<li>clojure-py
</li>
<li>clojure-scheme
</li>
</ul>
</section>
</section>
<section>
<section id="sec-54" >
<h2>Accessing the host environment - Clojure &amp;rarr; Java</h2>
<pre><code data-trim class="clojure">
(let [date (new org.joda.DateTime 2013 11 12)]
(.getDayOfWeek date))
;; or
(let [date (org.joda.DateTime. 2013 11 12)]
(. date (getDayOfWeek))
(System/currentTimeMillis)
</code></pre>
</section>
</section>
<section>
<section id="sec-55" >
<h2>With a spoonful of sugar</h2>
<pre><code data-trim class="clojure">
;; for date.withHourOfDay(12).withMinuteOfHour(53);
;; rather than
(.withMinuteOfHour (.withHourOfDay date 12) 53)
;; we can write
(.. date (withHourOfDay 12) (withMinuteOfHour 53))
</code></pre>
</section>
</section>
<section>
<section id="sec-56" >
<h2>Recycling with 'doto'</h2>
<pre><code data-trim class="clojure">
(let [my-obj (.. (doto (MyObjectBuilder.)
(.setValue 8)
(.setString "Hello!")
(.setOtherThing (+ 145.2 13.25)))
(build))]
my-obj)
</code></pre>
<pre><code data-trim class="java">
;; equivalent to:
MyObjectBuilder builder = new MyObjectBuilder();
builder.setValue(8);
builder.setString("Hello!");
builder.setOtherThing(145.2 + 13.25);
MyObject myObj = builder.build();
</code></pre>
</section>
</section>
<section>
<section id="sec-57" >
<h2>Importing the goods</h2>
<pre><code data-trim class="clojure">
(ns your-ns
(:require [your-clj.namespace :refer [your-fn]])
(:import [java.util UUID Date Random Currency]
[org.joda.time DateTime Period Interval]))
</code></pre>
</section>
</section>
<section>
<section id="sec-58" >
<h2>Implementing Java interfaces</h2>
<pre><code data-trim class="clojure">
(.addActionListener button
(reify ActionListener
(actionPerformed [this e]
(prn "Got action:" e))))
</code></pre>
</section>
</section>
</div>
</div>
<script src="./reveal.js/lib/js/head.min.js"></script>
<script src="./reveal.js/js/reveal.min.js"></script>
<script>
// Full list of configuration options available here:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: false,
center: true,
rollingLinks: false,
keyboard: true,
overview: true,
// slide width
// slide height
// slide margin
// slide minimum scaling factor
// slide maximum scaling factor
theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
transition: Reveal.getQueryHash().transition || 'fade', // default/cube/page/concave/zoom/linear/fade/none
transitionSpeed: 'default',
// Optional libraries used to extend on reveal.js
dependencies: [
{ src: './reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } }
,{ src: './reveal.js/plugin/markdown/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }
,{ src: './reveal.js/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }
,{ src: './reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
,{ src: './reveal.js/plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }
,{ src: './reveal.js/plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
// { src: './reveal.js/plugin/search/search.js', async: true, condition: function() { return !!document.body.classList; } }
// { src: './reveal.js/plugin/remotes/remotes.js', async: true, condition: function() { return !!document.body.classList; } }
]
});
</script>
</body>
</html>