Skip to content

Commit

Permalink
Merged from upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
bridgethillyer committed Mar 7, 2014
2 parents 8c572f1 + f2ab762 commit 9ff0baf
Show file tree
Hide file tree
Showing 11 changed files with 643 additions and 105 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ Outline
* [Getting Set Up](outline/setup.md) *DONE*
* [Introduction to Programming with Clojure](outline/intro.md) *DONE*
* [Data Structures](outline/data_structures.md) *DONE*
* [Functions](outline/functions.md) **TODO**
* [Flow Control](outline/flow_control.md) **IN-PROGRESS**
* [Making Your First Program](outline/first-program.md) **TODO**
* [Introduction to Web Applications](outline/web.md) **TODO**
* [Functions](outline/functions.md) *DONE*
* [Flow Control](outline/flow_control.md) *DONE*
* [Making Your First Program](outline/first-program.md) *DONE*
* [Introduction to Web Applications](outline/web.md) *DONE*
* [Making Your Own Web Application](outline/app.md) **IN-PROGRESS**
* [Putting Your Application Online](outline/deploy.md) **TODO**

Expand Down
7 changes: 5 additions & 2 deletions outline/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ Turn it into a function.

Some supporting functions
-------------------------
Just type these into the REPL. We aren't talking about these.
Just copy/paste these into the REPL. We aren't talking about these.

(def list-size 10)

Expand All @@ -155,7 +155,7 @@ Call get-indicator-all to get the pump price indicator for 2012.

(def inds (get-indicator-all "EP.PMP.SGAS.CD" "2012" :country :value))

Go through the results, making sure that they are actually countries by matching up against the country-ids, then ...
Go through the results, making sure that they are actually countries by matching up against the country-ids, then get the value from the country and convert the value from a string.

(for [[k v] inds
:when (and v (country-ids (:id k)))]
Expand Down Expand Up @@ -196,3 +196,6 @@ Make that into a function.




[The rest of this will be to plug calls to the above functions into provided code.]

167 changes: 161 additions & 6 deletions outline/first-program.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,166 @@
Making Your First Program
=========================

* What is the difference between source code and the REPL?
* Structure of a Clojure project
* Namespaces
* TODO
Now that you know a bit about how to write Clojure code, let's look at how to create a standalone application.

Design first program. Make sure it contains data structures and explain them.
In order to do that, you'll first create a *project*. You'll learn how to organize your project with *namespaces*. You'll also learn how to specify your project's *dependencies*. Finally, you'll learn how to *build* your project to create the standalone application.

Make sure it uses at least one dependency and show how to add that and use it.
## Create a Project

Up until now you've been experimenting in a REPL. Unfortunately, all the work you do in a REPL is lost when you close the REPL. You can think of a project as a permanent home for your code. You'll be using a tool called "Leiningen" to help you create and manage your project. To create a new project, run this command:

```clojure
lein new app world-bank
```

This should create a directory structure that looks like this:

```
| .gitignore
| doc
| | intro.md
| project.clj
| resources
| README.md
| src
| | world_bank
| | | core.clj
| test
| | world_bank
| | | core_test.clj
```

There's nothing inherently special or Clojure-y about this project skeleton. It's just a convention used by Leiningen. You'll be using Leiningen to build and run Clojure apps, and Leiningen expects your app to be laid out this way. Here's the function of each part of the skeleton:

- `project.clj` is a configuration file for Leiningen. It helps
Leiningen answer questions like, "What dependencies does this
project have?" and "When this Clojure program runs, what function
should get executed first?"
- `src/world_bank/core.clj` is where we'll be doing our
Clojure coding for awhile. In general, your source code will fall
under `src/{world_bank}`
- The `test` directory obviously contains tests, which we won't be covering.
- `resources` is a place for you to store assets like images; we won't
be using it for awhile.

Now let's go ahead and actually run this project. Enter this at the command line:

```
cd world_bank
lein run
```

You should see this:

```
Hello, world!
```

## Modify Project

Pretty cool! But also pretty useless. To change the behavior of this project, open up `src/world_bank/core.clj` and monkey around with the `-main` function. Try changing it so that it reads:

```clojure
(defn -main
[& args]
(println "Hello, ClojureBridge!"))
```

If you run `lein run` again, you should see `Hello, ClojureBridge!` printed.

The `-main` function is the *entry point* to your program. Other than that there's nothing special about it. It acts just like any other Clojure function. It just happens to be the function which gets called first when you run your program. For example, you can write your own functions have `-main` call them:

```clojure
(defn quotify
[quote author]
(str quote "\n\n-- " author))
(defn -main
[& args]
(println (quotify "A man who carries a cat by the tail learns something he can learn in no other way."
"Mark Twain")))
```

This should output:

```
A man who carries a cat by the tail learns something he can learn in no other way.
-- Mark Twain
```

So, you can write programs of arbitrary complexity. Just make sure to use `-main` to kick them off.

## Organization

As your programs get more complex, you'll need to organize them. You organize your Clojure code by placing related functions and data in separate files. Clojure expects each file to correspond to a *namespace*, so you must *declare* a namespace at the top of each file.

Until now, you haven't really had to care about namespaces. Namespaces allow you to define new functions and data structures without worrying about whether the name you'd like is already taken. For example, you could create a function named `println` within the custom namespace `my-special-namespace`, and it would not interfere with Clojure's built-in `println` function. You can use the *fully-qualified name* `my-special-namespace/println` to distinguish your function from the built-in `println`.

Let's create a new namespace for making world bank API calls. First, create the file `src/world_bank/api.clj`. Then write this in it:

```clojure
(ns world-bank.api)
```

This line establishes that everything you define in this file will be stored within the `world-bank.api` namespace. For example, add this to your file:

```clojure
(def base-uri "http://api.worldbank.org")
```

You should be able to access that from your `core.clj` file. Change that file so that it reads:

```clojure
(ns world-bank.core)
(require '[world-bank.api :as api])

(defn -main
[& args]
(println api/base-uri))
```

If you run the `-main` function it should print `http://api.worldbank.org`.

There are couple things going on here. First, you use `require` to tell Clojure to load another namespace. The `:as` part of `require` allows you to *alias* the namespace, letting you refer to its definitions without having to type out the entire namespace. In this case, you can use `api/base-uri` instead of `world-bank.api/base-uri`.

## Dependencies

The final part of working with projects is managing their *dependencies*. Dependencies are just code libraries that others have written which you can incorporate in your own project.

To add a dependency, open `project.clj`. You should see a line which reads

```
:dependencies [[org.clojure/clojure "1.5.1"]]
```

That's where you specify your dependencies. You can add a dependency by adding another vector with the dependency's name and its version. Let's add the `clj-http` and `cheshire` projects. `clj-http` will let us make HTTP calls so that we can get data using the World Bank API. `cheshire` will let us decode the results. Update the dependencies line in your `project.clj` so that it looks like this:

```
:dependencies [[org.clojure/clojure "1.5.1"]
[clj-http "0.9.0"]
[cheshire "5.3.1"]]
```

Now you can require the namespaces defined in `clj-http` within your own project. Update `src/world_bank/api.clj` so that it looks like this:

```clojure
(ns world-bank.api)
(require '[clj-http.client :as client])
(require '[cheshire.core :as json]

(def base-uri "http://api.worldbank.org")
(def query-params {:format "json" :per_page 10000})

(defn get-api
"Returns map representing API response."
[path qp]
(let [base-path (str base-uri path)
query-params (merge qp {:format "json" :per_page 10000})
response (parse-json (:body (client/get base-path {:query-params query-params})))
metadata (first response)
results (second response)]
{:metadata metadata
:results results}))
```

Ta-da!
155 changes: 147 additions & 8 deletions outline/flow_control.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,150 @@
Flow Control
============

* Review of booleans
* cond
* do
* if (maybe?)
* doseq (?)
* dotimes (?)

I'm not even sure we should touch `doseq` or `dotimes`. Recursion is completely unnecessary for this course.
"Flow control" is the programming term for deciding how to react to a given circumstance. We make decisions like this all the time. *If* it's a nice day out, *then* we should visit the park; *otherwise* we should stay inside and play board games. *If* your car's tank is empty, *then* you should visit a gas station; *otherwise* you should continue to your destination.

Software is also full of these decisions. *If* the user's input is valid, *then* we should save her data; *otherwise* we show an error message. The common pattern here is that you test some condition and react differently based on whether the condition is *true* or *false*.

## if

In Clojure, the most basic tool we have for this process is the `if` operator. Here's how you might code the data validation scenario:

```clojure
(if (valid? data)
(save! data)
(error "Your data was invalid"))
```

The general form of the `if` operator is:

```clojure
(if conditional-expression
expression-to-evaluate-when-true
expression-to-evaluate-when-false)
```

When testing the truth of an expression, Clojure considers the values `nil` and `false` to be false and everything else to be true. Here are some examples:

```clojure
(if (> 3 1)
"3 is greater than 1"
"3 is not greater than 1")
;=> "3 is greater than 1"

(if (> 1 3)
"1 is greater than 3"
"1 is not greater than 3")
;=> "1 is not greater than 3"

(if "anything other than nil or false is considered true"
"A string is considered true"
"A string is not considered true")
;=> "A string is considered true"

(if nil
"nil is considered true"
"nil is not considered true")
;=> "nil is not considered true"

(if (get {:a 1} :b)
"expressions which evaluate to nil are considered true"
"expressions which evaluate to nil are not considered true")
;=> "expressions which evaluate to nil are not considered true"
```

### EXERCISE: Even more name formatting

Write a function `format-name` that takes a map representing a user, with keys `:first`, `:last`, and possibly `:middle`. It should return their name as a string, like so:

```clj
(format-name {:first "Margaret" :last "Atwood"})
;=> "Margaret Atwood"

(format-name {:first "Ursula" :last "Le Guin" :middle "K."})
;=> "Ursula K. Le Guin"
```

### BONUS: Flexible name formatting

Change `format-name` to take a second argument, `order`. If `order` equals `:last`, then the format should be "Last, First Middle"; otherwise, it should be "First Middle Last."


## Boolean logic with and, or, and not

`if` statements are not limited to testing only one thing. You can test multiple conditions using boolean logic. _Boolean logic_ refers to combining and changing the results of predicates using `and`, `or`, and `not`.

If you've never seen this concept in programming before, remember that it follows the common sense way you look at things normally. Is this _and_ that true? Only if both are true. Is this _or_ that true? Yes, if either -- or both! -- are. Is this _not_ true? Yes, if it's false.

Look at this truth table:

| x | y | (and x y) | (or x y) | (not x) | (not y) |
| ----- | ----- | --------- | -------- | ------- | ------- |
| false | false | false | false | true | true |
| true | false | false | true | false | true |
| true | true | true | true | false | false |
| false | true | false | true | true | false |

`and`, `or`, and `not` work like other functions (they aren't exactly functions, but work like them), so they are in _prefix notation_, like we've seen with arithmetic.

`and`, `or`, and `not` can be combined. This can be hard to read. Here's an example:

```clj
(defn leap-year?
"Every four years, except years divisible by 100, but yes for years divisible by 400."
[year]
(and (zero? (mod year 4))
(or (zero? (mod year 400))
(not (zero? (mod year 100))))))
```


## cond

Sometimes you might want to do multiple conditional checks. For example, you might want to check whether a number is within a certain range. Here's the general form of `cond`:

```clojure
(cond
test-condition-1 expression-to-evaluate-when-test-condition-1-is-true
test-condition-2 expression-to-evaluate-when-test-condition-2-is-true
test-condition-3 expression-to-evaluate-when-test-condition-3-is-true
:else expression-to-evaluate-when-no-test-conditions-are-true)
```

Here's how you could use it to check that a number is within a range:

```clojure
(let [breaths-taken-today 100]
(cond
(and (> breaths-taken-today 0) (< breaths-taken-today 50))
"That's a good start! You probably want to breathe more, though."

(and (> breaths-taken-today 51) (< breaths-taken-today 100))
"Wow, you're breathing like a pro!"

:else
"Hold on there buddy, that's a lot of breathing. Might want to hold off on that a bit."))
```

Clojure has other conditional operators, but they're just there to make your code slightly more concise. `if`, `do`, and `cond` will allow you to express the behavior you want for every condition!

### EXERCISE: Revisit leap year

Rewrite the `leap-year?` function shown earlier to use `cond` instead of nested boolean logic.


## do

One thing you may have noticed is that you are only allowed to evaluate one expression for each branch of the `if` expression. The `do` operator allows you to "bundle up" multiple expressions so that you can "do" multiple things. Here's an example:

```clojure
(if (> 5 0)
(do
(println "5 is indeed greater than 0!")
(println "I wonder if 0 has low self-esteem"))
(do
(println "0 is indeed greater than 5!")
(println "Because numbers have been broken I guess")))
; => "5 is indeed greater than 0!"
; => "I wonder if 0 has low self-esteem"
```

Loading

0 comments on commit 9ff0baf

Please sign in to comment.