51
Understanding Core Clojure Functions Dr. Jonathan Graham http://jonathangraham.github.io twitter: @graham_jp 8th Light

Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Understanding Core Clojure Functions

Dr. Jonathan Graham

http://jonathangraham.github.io twitter: @graham_jp

8th Light

Page 2: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 3: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 4: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 5: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Are you fully in control when you code?

http://jonathangraham.github.io twitter: @graham_jp

Page 6: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 7: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that

result and the 2nd item, etc.

If coll contains no items, returns val and f is not called.

http://jonathangraham.github.io twitter: @graham_jp

Page 8: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Let’s start with a test…

http://jonathangraham.github.io twitter: @graham_jp

Page 9: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

A second test could have a single element in the collection.

http://jonathangraham.github.io twitter: @graham_jp

Page 10: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

With more than one item in the collection…

We can add tests with different functions, collections and initial values.

http://jonathangraham.github.io twitter: @graham_jp

Page 11: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

If val is not supplied, returns the result of applying f to the first 2 items in coll, then applying f to that

result and the 3rd item, etc.

http://jonathangraham.github.io twitter: @graham_jp

Page 12: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

No val…

We can add tests with multiple items in coll.

http://jonathangraham.github.io twitter: @graham_jp

Page 13: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

If coll contains no items, f must accept no arguments as well, and reduce returns the result of

calling f with no arguments.

http://jonathangraham.github.io twitter: @graham_jp

Page 14: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

No val and empty coll…

http://jonathangraham.github.io twitter: @graham_jp

Page 15: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

How many unit tests are enough to give us confidence that our function behaves in the same

was as reduce?

http://jonathangraham.github.io twitter: @graham_jp

Page 16: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Property-based tests

Make statements about the expected behaviour of the code that should hold true for the entire domain of possible

inputs. These statements are then verified for many different (pseudo)randomly generated inputs.

http://jonathangraham.github.io twitter: @graham_jp

Clojure test.check

Page 17: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 18: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 19: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Functions requiring two arguments can be passed to reduce with only a single argument

(defn f [x y] (+ x y))

(reduce f [1])

http://jonathangraham.github.io twitter: @graham_jp

Page 20: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Care needed with functions that cannot be evaluated with no arguments

(reduce - ‘())

http://jonathangraham.github.io twitter: @graham_jp

Page 21: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 22: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Taking a TDD approach again…

http://jonathangraham.github.io twitter: @graham_jp

Page 23: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Make tests with more than one element pass

http://jonathangraham.github.io twitter: @graham_jp

Page 24: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

With a passing test suite, refactor

http://jonathangraham.github.io twitter: @graham_jp

Page 25: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Again, property-based tests can confirm that our function behaves the same as the core function

http://jonathangraham.github.io twitter: @graham_jp

Page 26: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 27: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Filter returns a lazy sequence

http://jonathangraham.github.io twitter: @graham_jp

Page 28: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Build function up recursively?

http://jonathangraham.github.io twitter: @graham_jp

Page 29: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

We can add more tests, and these pass

http://jonathangraham.github.io twitter: @graham_jp

But, our function is not lazy!

We just convert the result to a lazy sequence

Page 30: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

cons is lazy; conj is not

http://jonathangraham.github.io twitter: @graham_jp

conj depends on the collection type, so is realised immediately

cons adds an item to the start of a collection, and can be evaluated lazily

Page 31: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Refactor lazily

http://jonathangraham.github.io twitter: @graham_jp

Page 32: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 33: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

How do we get a vector containing just the even numbers given an input of [0 1 2 3 4 5]?

http://jonathangraham.github.io twitter: @graham_jp

Page 34: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 35: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

For a single collection, write our map function analogously to our filter function

http://jonathangraham.github.io twitter: @graham_jp

Page 36: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

We can extend this approach for two collections

http://jonathangraham.github.io twitter: @graham_jp

Page 37: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

What about more than two collections? Build up recursively?

http://jonathangraham.github.io twitter: @graham_jp

Page 38: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Does this work for non-commutative functions?

http://jonathangraham.github.io twitter: @graham_jp

Page 39: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

How can build lazily?

http://jonathangraham.github.io twitter: @graham_jp

1) Take all of the input collections, and put them into a single sequence.

2) Reorder this sequence, so the first collection is all of the first elements, the second collection is all of the second elements, etc.

3) Map the result of applying the function to each reordered collection in turn

Page 40: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Our map function

http://jonathangraham.github.io twitter: @graham_jp

Page 41: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Our map function

http://jonathangraham.github.io twitter: @graham_jp

Page 42: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 43: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

map is lazy, and the time we are measuring is the time to make a new lazy-seq, not the time to actually execute it

http://jonathangraham.github.io twitter: @graham_jp

Page 44: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 45: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 46: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Fails

http://jonathangraham.github.io twitter: @graham_jp

Page 47: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

We need to generate all of the futures before we start to deref

Remember that conj is not lazy…

http://jonathangraham.github.io twitter: @graham_jp

Page 48: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Extend to multiple collections as we did with my-map

http://jonathangraham.github.io twitter: @graham_jp

Page 49: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

The behavior of all the functions can be confirmed to be the same as the core functions using

property-based tests

http://jonathangraham.github.io twitter: @graham_jp

Page 50: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

http://jonathangraham.github.io twitter: @graham_jp

Page 51: Understanding Core Clojure Functions...If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc

Understanding Core Clojure Functions

Dr. Jonathan Graham

http://jonathangraham.github.io twitter: @graham_jp

8th Light