Skip to content

Commit

Permalink
Merge pull request ClojureBridge#89 from clojurebridge-sf/master
Browse files Browse the repository at this point in the history
Changes made for the San Francisco workshop - for team review
  • Loading branch information
seancorfield committed May 9, 2014
2 parents cccf13c + 642b724 commit e805245
Show file tree
Hide file tree
Showing 19 changed files with 1,345 additions and 1,256 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Outline
* [Introduction to Programming with Clojure](outline/intro.md)
* [Data Structures](outline/data_structures.md)
* [Functions](outline/functions.md)
* [More Simple Values](outline/simple_values2.md)
* [More Functions](outline/functions2.md)
* [More Data Structures](outline/data_structures2.md)
* [Flow Control](outline/flow_control.md)
* [Making Your First Program](outline/first-program.md)
* [Making Your Own Web Application](outline/app.md)
Expand Down
185 changes: 13 additions & 172 deletions outline/data_structures.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ Data Structures
===============

* Collections
* Vectors
* Lists
* Maps
* Sequences
* Vectors

## Collections

Expand All @@ -26,30 +23,30 @@ Vectors are written using square brackets with any number of pieces of data insi

```clj
[1 2 3 4 5]
["a" 1 2 "b"]
[56.9 60.2 61.8 63.1 54.3 66.4 66.5 68.1 70.2 69.2 63.1 57.1]
[]
```

What can you do with vectors? Vectors are easy to add more items to, delete items from, or pull arbitrary items out of. Here are some functions that operate on vectors.

```clj
(vector? [:a :b :c])
(vector? [5 10 15])
;=> true

(vector :a :b :c)
;=> [:a :b :c]
(vector 5 10 15)
;=> [5 10 15]

(conj [:a :b] :c)
;=> [:a :b :c]
(conj [5 10] 15)
;=> [5 10 15]

(count [:a :b :c])
(count [5 10 15])
;=> 3

(nth [:a :b :c] 1)
;=> :b
(nth [5 10 15] 1)
;=> 10

(first [:a :b :c])
;=> :a
(first [5 10 15])
;=> 5
```

Let's look at these functions together. First, you see a function called `vector?`. You can probably guess what that does: it tells us whether the argument is a vector. Notice that it has a question mark at the end of it. We often call functions like these _predicate functions_, and they answer true-or-false questions about the data we give them.
Expand All @@ -63,160 +60,4 @@ Now, take a look at the last three functions. `count` does what you'd expect: it

### EXERCISE: Make a vector

Make a vector of all the places you've ever lived. Then use the `nth` function to get the current place you live from the vector.


## Lists

Lists look a lot like vectors, but they work differently. It's easiest to imagine a list as a chain of items. Each item has a value stored in it, but it also has a link to the next item in the chain.

![Linked list](../img/linked-list.png)

We write lists by calling the function `list` with the items we want in the list afterwards. Here are some examples:

```clj
(list :a :b)
(list "a" 1 2 3 "b")
(list)
```

Note that the last example produces an empty list.

What can you do with lists? You'll usually use a vector to store a collection of data (I want to say "list" here, but that's super-confusing). Sometimes, though, you want a collection that you can quickly add and remove the head of. Think of a list like this as a stack of something--coins, plates, books, whatever. It's easy to add to the top or take stuff off the top. Can you see why it'd be hard to remove things from the bottom of a list?

Here are some functions that operate on lists:

```clj
(list? (list 1 2 3))
; => true

(list 1 2 3)
; => (1 2 3)

(conj (list 1 2) 3)
; => (3 1 2)

(count (list 1 2 3))
; => 3

(first (list 1 2 3))
; => 1

(rest (list 1 2 3))
; => (2 3)
```

A lot of these are the same as you saw with vectors.

You can use `conj` with lists, too. What's different from when we use it with vectors? New items get added to the head of the list. `conj` works with all the data structure we're going to cover, but it doesn't promise where it will add the new item: that's specific to the type of data structure.

Lastly, there's a function here we didn't see before: `rest`. `rest` removes the head from the list and returns the rest, usually called the "tail."


## Maps

Maps hold a set of keys and values associated with them. You can think of it like a dictionary: you look up things using a word (a key) and see the definition (its value). If you've programmed in another language, you might have seen something like maps--maybe called dictionaries, hashes, or associative arrays.

![Map](../img/map.png)

We write maps by enclosing alternating keys and values in curly braces, like so:

```clj
{:first "Sally", :last "Brown"}
{:a 1 :b "two"}
{}
```

Maps are useful because they can hold data in a way we normally think about it. Take our made up example, Sally Brown. A map can hold her first name and last name, her address, her favorite food, or anything else. It's a simple way to collect that data and make it easy to look up. That last example? It is an empty map. It is a map that is ready to hold some things, but doesn't have anything in it yet.

Let's look at some functions we can use with maps:

```clj
(map? {:first "Sally" :last "Brown"})
;=> true

(get {:first "Sally" :last "Brown"} :first)
;=> "Sally"

(get {:first "Sally"} :last :MISS)
=> :MISS

(assoc {:first "Sally"} :last "Brown")
;=> {:first "Sally", :last "Brown"}

(dissoc {:first "Sally" :last "Brown"} :last)
;=> {:first "Sally"}

(merge {:first "Sally"} {:last "Brown"})
;=> {:first "Sally", :last "Brown"}

(count {:first "Sally" :last "Brown"})
;=> 2

(keys {:first "Sally" :last "Brown"})
;=> (:first :last)

(vals {:first "Sally" :last "Brown"})
;=> ("Sally" "Brown")

(into {} [[1 2] [3 4]])
;=> {1 2, 3 4}
```

We don't have nearly as many functions here in common as vectors and lists did.

`get` works a lot like `nth` did with vectors, but takes a key instead of a number. It uses the supplied key to look up a value in the map. What do you think is happening with the second example of `get`? We can supply a value for `get` to return if it can't find the key we asked for. In this case, we supplied the key `:MISS`.

`assoc` and `dissoc` are paired functions: they associate and disassociate items from a map. See how we add the last name "Brown" to the map with `assoc`, and then we remove it with `dissoc`. `merge` merges two maps together to make a new map.

We have `count`, like we have with every collection. Why do you think the answer is two? `count` is returning the number of associations.

Then we have `keys` and `vals`, which are pretty simple: they return the keys and values in the map. The order is not guaranteed, so we could have gotten `(:first :last)` or `(:last :first)`.

The last one - into - works with any kind of collection. It takes everything from one collection and puts it into another one, so it's useful for converting from one type of collection to another.

Let's look at one more thing about maps before we move on. You can always use `get` to get values out of maps, but you'll more often see something like the following:

```clj
(:first {:first "Sally" :last "Brown"})
=> "Sally"
(:last {:first "Sally"})
=> nil
(:last {:first "Sally"} :MISS)
=> :MISS
```

You can use a keyword like you would use a function in order to look up values in a map. Feel free to use `get` in your own code: it can be more clear and less confusing as you're learning Clojure.


### EXERCISE: Modeling Yourself

Make a map representing yourself. Make sure it contains your first name and last name. Then, add your hometown to the map using `assoc` or `merge`.


## Sequences

The reason you see the same functions used on different types of collections is because all of these collections are different kinds of _sequences_. (Technically, they all provide access to their elements as a sequence. While this is an important distinction for advanced Clojure developers, the simpler way of talking about sequences is useful at this stage.) A sequence is a Clojure abstraction, a unified way to look at many different types of collections. The data structures you've seen so far--vectors, lists, and maps--are not the only things to be represented as sequences. Many other things, such as lists of files, lines of text, or records in a database, can be represented as sequences, and so you can use all sequence functions with them.

Here is one example. The sequence function `take` makes a new sequence with the first however-many items from a provided collection.

```clj
(take 3 [1 2 3 4 5 6 7 8])
;=> (1 2 3)
```

We will see many other functions that can be used with sequences as we move forward.


## Collections of Collections

Simple values such as numbers, keywords, and strings are not the only types of things you can put into collections. You can also put other collections into collections, so you can have a vector of maps, or a list of vectors, or whatever combination fits your data.


### EXERCISE: Modeling your classmates

First, take the map you made about yourself.

Then, find two or three other classmates around you. Ask them their first and last name and their hometown. Make a vector of maps with their information.

Lastly, add your information to their information using `conj`.
Make a vector of the average high temperatues in each month of the year in the town where you live. Then use the `nth` function to get the average high temperature for the current month from the vector.
113 changes: 113 additions & 0 deletions outline/data_structures2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
More Data Structures
===============

* Maps
* Collections of Collections

## Maps

Maps hold a set of keys and values associated with them. You can think of it like a dictionary: you look up things using a word (a key) and see the definition (its value). If you've programmed in another language, you might have seen something like maps--maybe called dictionaries, hashes, or associative arrays.

![Map](../img/map.png)

We write maps by enclosing alternating keys and values in curly braces, like so:

```clj
{:first "Sally", :last "Brown"}
{:a 1 :b "two"}
{}
```

Maps are useful because they can hold data in a way we normally think about it. Take our made up example, Sally Brown. A map can hold her first name and last name, her address, her favorite food, or anything else. It's a simple way to collect that data and make it easy to look up. That last example? It is an empty map. It is a map that is ready to hold some things, but doesn't have anything in it yet.

Let's look at some functions we can use with maps:

```clj
(map? {:first "Sally" :last "Brown"})
;=> true

(get {:first "Sally" :last "Brown"} :first)
;=> "Sally"

(get {:first "Sally"} :last :MISS)
=> :MISS

(assoc {:first "Sally"} :last "Brown")
;=> {:first "Sally", :last "Brown"}

(dissoc {:first "Sally" :last "Brown"} :last)
;=> {:first "Sally"}

(merge {:first "Sally"} {:last "Brown"})
;=> {:first "Sally", :last "Brown"}

(count {:first "Sally" :last "Brown"})
;=> 2

(keys {:first "Sally" :last "Brown"})
;=> (:first :last)

(vals {:first "Sally" :last "Brown"})
;=> ("Sally" "Brown")

(into {} [[1 2] [3 4]])
;=> {1 2, 3 4}
```

We don't have nearly as many functions here in common as vectors and lists did.

`get` works a lot like `nth` did with vectors, but takes a key instead of a number. It uses the supplied key to look up a value in the map. What do you think is happening with the second example of `get`? We can supply a value for `get` to return if it can't find the key we asked for. In this case, we supplied the key `:MISS`.

`assoc` and `dissoc` are paired functions: they associate and disassociate items from a map. See how we add the last name "Brown" to the map with `assoc`, and then we remove it with `dissoc`. `merge` merges two maps together to make a new map.

We have `count`, like we have with every collection. Why do you think the answer is two? `count` is returning the number of associations.

Then we have `keys` and `vals`, which are pretty simple: they return the keys and values in the map. The order is not guaranteed, so we could have gotten `(:first :last)` or `(:last :first)`.

The last one - into - works with any kind of collection. It takes everything from one collection and puts it into another one, so it's useful for converting from one type of collection to another.

Let's look at one more thing about maps before we move on. You can always use `get` to get values out of maps, but you'll more often see something like the following:

```clj
(:first {:first "Sally" :last "Brown"})
=> "Sally"
(:last {:first "Sally"})
=> nil
(:last {:first "Sally"} :MISS)
=> :MISS
```

You can use a keyword like you would use a function in order to look up values in a map. Feel free to use `get` in your own code: it can be more clear and less confusing as you're learning Clojure.


### EXERCISE: Modeling Yourself

Make a map representing yourself. Make sure it contains your first name and last name. Then, add your hometown to the map using `assoc` or `merge`.

## Collections of Collections

Simple values such as numbers, keywords, and strings are not the only types of things you can put into collections. You can also put other collections into collections, so you can have a vector of maps, or a list of vectors, or whatever combination fits your data.


### EXERCISE: Get the names of people

Create a function called `get-names` that takes a vector of maps of people and returns a vector of their names.

Here is an example of how it should work:

```clj
(get-names [{:first "Margaret" :last "Atwood"}
{:first "Doris" :last "Lessing"}
{:first "Ursula" :last "Le Guin"}
{:first "Alice" :last "Munro"}])

;=> ["Margaret Atwood" "Doris Lessing" "Ursula Le Guin" "Alice Munro"]
```

### EXERCISE: Modeling your classmates

First, take the map you made about yourself.

Then, find two or three other classmates around you. Ask them their first and last name and their hometown. Make a vector of maps with their information.

Lastly, add your information to their information using `conj`.
Loading

0 comments on commit e805245

Please sign in to comment.