Skip to content

Commit

Permalink
Completes the second case study
Browse files Browse the repository at this point in the history
  • Loading branch information
evhub committed Feb 1, 2016
1 parent 77a3363 commit 9688a41
Showing 1 changed file with 12 additions and 10 deletions.
22 changes: 12 additions & 10 deletions HELP.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ and much more!
At its very core, Coconut is a compiler that turns Coconut code into Python code. That means that anywhere where you can use a Python script, you can also use a compiled Coconut script. To access that core compiler, Coconut comes with a command-line utility, which can:
- compile single Coconut files or entire Coconut projects
- interpret Coconut code on-the-fly
- launch IPython / Jupyter notebooks that use Coconut as the kernel
- hook into existing Python applications like IPython / Jupyter

Installing Coconut, including all the features above, is drop-dead simple. Just
1. install [Python](https://www.python.org/downloads/),
Expand Down Expand Up @@ -154,11 +154,11 @@ coconut --jupyter console
```
or equivalently, `--ipython` can be substituted for `--jupyter` in either command.

To use Coconut as an extension inside of the IPython kernel, add the code
To use Coconut as an extension inside of the IPython kernel, type the code
```python
%load_ext coconut
```
and then to run Coconut code, use
into your IPython notebook or console, and then to run Coconut code, use
```python
%coconut <code>
```
Expand All @@ -182,7 +182,7 @@ To start off with, we're going to have to decide what sort of an implementation

### Imperative Method

The imperative approach is the way you'd write `factorial` in a language like C. Imperative approaches involve lots of state change, where variables are regularly changed and loops are liberally used. In Coconut, the imperative approach to the `factorial` problem looks like this:
The imperative approach is the way you'd write `factorial` in a language like C. Imperative approaches involve lots of state change, where variables are regularly modified and loops are liberally used. In Coconut, the imperative approach to the `factorial` problem looks like this:
```python
def factorial(n):
"""Compute n! where n is an integer >= 0."""
Expand All @@ -196,7 +196,7 @@ def factorial(n):

```

Since the imperative approach is a fundamentally non-functional method, Coconut can't help us improve this example very much. Even here, though, the use of Coconut's infix notation in `` n `isinstance` int `` makes the code slightly clearer and easier to read.
Since the imperative approach is a fundamentally non-functional method, Coconut can't help us improve this example very much. Even here, though, the use of Coconut's infix notation (where the function is put in-between its arguments, surrounded in backticks) in `` n `isinstance` int `` makes the code slightly cleaner and easier to read.

### Recursive Method

Expand Down Expand Up @@ -300,13 +300,13 @@ Now let's take a look at what we do to `reduce` to make it multiply all the numb

First, the operator function. In Coconut, a function form of any operator can be retrieved by surrounding that operator in parentheses. In this case, `(*)` is roughly equivalent to `lambda x, y: x*y`, but much cleaner and neater. In Coconut's lambda syntax, `(*)` is also equivalent to `(x, y) -> x*y`, which we will use from now on for all lambdas, even though both are legal Coconut, because Python's `lambda` statement is too ugly and bulky to use regularly. In fact, if Coconut's `--strict` mode is enabled, it will raise an error whenever Python `lambda` statements are used.

Second, the partial application. In Coconut, if a function call is prefixed by a `$`, like in this example, instead of actually performing the function call, a new function is returned with the given arguments already provided to it, so that when it is then called, it will be called with both the partially-applied arguments and the new arguments, in that order. In this case, `reduce$(*)` is equivalent to `(*args, **kwargs) -> reduce((*), *args, **kwargs)`.
Second, the partial application. In Coconut, if a function call is prefixed by a `$`, like in this example, instead of actually performing the function call, a new function is returned with the given arguments already provided to it, so that when it is then called, it will be called with both the partially-applied arguments and the new arguments, in that order. In this case, `reduce$((*))` is equivalent to `(*args, **kwargs) -> reduce((*), *args, **kwargs)`.

Putting it all together, we can see how the single line of code
```python
range(1, n+1) |> reduce$((*))
```
is able to compute the proper factorial, without using any state or loops, only higher-order functions, in true functional style.
is able to compute the proper factorial, without using any state or loops, only higher-order functions, in true functional style. By supplying the tools we use here like partial application (`$`), pipeline-style programming (`|>`), higher-order functions (`reduce`), and operator functions (`(*)`), Coconut enables this sort of functional programming to be done cleanly, neatly, and easily.

## Case Study 2: Quick Sort

Expand All @@ -315,7 +315,7 @@ In the second case study, we will be implementing the quick sort algorithm. Our
Our method for tackling this problem is going to be a combination of the recursive and iterative approaches we used for the `factorial` problem, in that we're going to be lazily building up an iterator, and we're going to be doing it recursively. Here's the code, in Coconut:
```python
def quick_sort(l):
"""Return a sorted iterator of l, using the quick sort algorithm, and without using any data until necessar."""
"""Return a sorted iterator of l, using the quick sort algorithm, and without using any data until necessary."""
match [head] :: tail in l:
tail, tail_ = tee(tail)
yield from (quick_sort((x for x in tail if x <= head))
Expand All @@ -325,7 +325,7 @@ def quick_sort(l):
```
This `quick_sort` algorithm uses a bunch of new constructs, so let's go over them.

First, the `::` operator, which appears here both in pattern-matching, and by itself. In essence, the `::` operator is `+` for iterators. On its own, it takes two iterators and concatenates, or chains, them together. In pattern-matching, it inverts that operation, destructuring the beginning of an iterator into a pattern, and binding the rest of that iterator to a variable.
First, the `::` operator, which appears here both in pattern-matching and by itself. In essence, the `::` operator is `+` for iterators. On its own, it takes two iterators and concatenates, or chains, them together. In pattern-matching, it inverts that operation, destructuring the beginning of an iterator into a pattern, and binding the rest of that iterator to a variable.

Which brings us to the second new thing, `match ... in ...` notation. The notation
```python
Expand All @@ -346,7 +346,7 @@ that avoids the need for an additional level of indentation when only one `match

The third new construct is the built-in function `tee`. `tee` solves a problem for functional programming created by the implementation of Python's iterators: whenever an element of an iterator is accessed, it's lost. `tee` solves this problem by splitting an iterator in two (or more if the optional argument `n` is passed) independent iterators that both use the same underlying iterator to access their data, thus when an element of one is accessed, it isn't lost in the other.

Finally, although it's not a new construct, since it exists in Python 3, the use of `yield from` here deserves a mention. In Python, `yield` is the statement used to construct iterators, function much like `return`, with the exception that multiple `yield`s can be encountered, and each one will produce another element. `yield from` is very similar, except instead of adding a single element to the produced iterator, it adds another whole iterator.
Finally, although it's not a new construct, since it exists in Python 3, the use of `yield from` here deserves a mention. In Python, `yield` is the statement used to construct iterators, functioning much like `return`, with the exception that multiple `yield`s can be encountered, and each one will produce another element. `yield from` is very similar, except instead of adding a single element to the produced iterator, it adds another whole iterator.

Putting it all together, here's our `quick_sort` function again:
```python
Expand All @@ -364,6 +364,8 @@ The function first attempts to split `l` into an initial element and a remaining

The advantages of the basic approach used here, heavy use of iterators and recursion, as opposed to the classical imperative approach, are numerous. First, our approach is more clear and more readable, since it is describing _what_ `quick_sort` is instead of _how_ `quick_sort` could be implemented. Second, our approach is _lazy_ in that our `quick_sort` won't evaluate any data until it needs it. Finally, and although this isn't relevant for `quick_sort` it is relevant in many other cases, an example of which we'll see later in this tutorial, our approach allows for working with _infinite_ series just like they were finite.

And Coconut makes programming in such an advantageous functional approach significantly easier. In this example, Coconut's pattern-matching lets us easily split the given iterator, and Coconut's `::` iterator joining operator lets us easily put it back together again in sorted order.

## Case Study 3: Vectors

```python
Expand Down

0 comments on commit 9688a41

Please sign in to comment.