-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:turingschool/curriculum-site
- Loading branch information
Showing
18 changed files
with
790 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
--- | ||
layout: page | ||
title: Beat Box | ||
--- | ||
|
||
### Learning Goals | ||
|
||
* Follow an interaction pattern | ||
* Write readable code that adheres to Ruby convention | ||
* Write tests | ||
* Distinguishing between classes and instances of those classes | ||
* Use and implement iteration or recursion techniques | ||
* Host code on Github | ||
|
||
## Overview | ||
|
||
In this project we're going to do some silly things with sound. Specifically, we're going to make a very basic drum machine program. | ||
|
||
However to add some additional depth, let's also use this project as a chance to explore one of the fundamental data structures in computer science -- the Linked List. | ||
|
||
### Drum Machine 101 -- Making Sounds | ||
|
||
Go into your Terminal and try this: | ||
|
||
``` | ||
$ say -r 500 "ding, dah, oom, oom, ding, oom, oom, oom, ding, dah, oom, oom, ding, dah, oom, oom, ding, dah, oom, oom " | ||
``` | ||
|
||
Yeah. That's what we're looking for. Now try it from Ruby: | ||
|
||
``` | ||
$ pry | ||
> `say -r 500 "ding, dah, oom, oom"` | ||
``` | ||
|
||
Note that the backticks allow you to run terminal commands from within Ruby. | ||
|
||
The exact command that you need to run may differ based on what version of OS X | ||
you have installed on your computer. The commands above will work on 10.13. | ||
|
||
### Linked Lists | ||
|
||
Linked Lists are one of the most fundamental Computer Science data structures. A Linked List models a collection of data as a series of "nodes" which link to one another in a chain. | ||
|
||
In a singly-linked list (the type we will be building) you have a __head__, which is a node representing the "start" of the list, and subsequent nodes which make up the remainder of the list. | ||
|
||
The __list__ itself can hold a reference to one thing -- the head node. | ||
|
||
Each node can hold a single element of data and a link to the next node in the list. | ||
|
||
The last node of the list is often called its __tail__. | ||
|
||
Using sweet ASCII art, it might look like this: | ||
|
||
``` | ||
List -- (head) --> ["hello" | -]-- (link) --> ["world" | -]-- (link) --> ["!" | ] | ||
``` | ||
|
||
The three nodes here hold the data "hello", "world", and "!". The first two nodes have links which point to other nodes. The last node, holding the data "!", has no reference in the link spot. This signifies that it is the end of the list. | ||
|
||
In other lower level languages, something called a pointer is what is used to ensure that a single link knows about the next link. In Ruby, we don't use pointers, so the link is literally its node. When we get to a node which is the last node, we call it the tail, and its link is nil. | ||
|
||
A linked list should be able to do the following: | ||
|
||
|
||
* Insert elements | ||
* Pop an element from the end | ||
* Push an element onto the beginning | ||
* Remove the (first occurrence | all occurrences) of an element by data content | ||
* Remove an element by position | ||
* Add an element at an arbitrary position | ||
* Add an element after a known node | ||
* Find whether a data element is or is not in the list | ||
* Find the distance between two nodes | ||
|
||
## Tips | ||
|
||
* A linked list is not an array. While it may perform many of the same functions as an array, its structure is conceptually very different. | ||
* There are only 3 types of "state" that need to be tracked for a linked list -- the head of the list, the data of each node, and the "next node" of each node. | ||
* In object-oriented programming, "state" is generally modeled with instance variables | ||
* There are two main ways to implement Linked Lists: __iteration__ and __recursion__. Iterative solutions use looping structures (`while`, `for`) to walk through the nodes in the list. Recursive solutions use methods which call themselves to walk through nodes. It would be ideal to solve it each way. | ||
* Most of your methods will be defined on the `List` itself, but you will need to manipulate one or more `Node`s to implement them. | ||
* __TDD__ will be your friend in implementing the list. Remember to start small, work iteratively, and test all of your methods. | ||
* An __empty__ list has `nil` as its head. | ||
* The __tail__ of a list is the node that has `nil` as its next node. | ||
|
||
## Constraints | ||
|
||
* Make sure that your code is well tested for both *expected cases* and *edge cases*. Try popping more elements than there are in the list. Try seeing if an empty list includes anything. Try inserting elements at a position beyond the length of the list. | ||
* Avoid using other ruby collections (Arrays, Hashes, etc) for the storage of your beats. That's where you're supposed to use the linked list. But having Arrays elsewhere in your code, or using methods that return arrays (like `.split`) are totally ok. | ||
|
||
## Resources | ||
|
||
Need some help on Linked Lists? You can check out some of the following resources: | ||
|
||
* [Linked List in Plain English](https://www.youtube.com/watch?v=oiW79L8VYXk) | ||
* [Ruby's Missing Data Structure](http://www.sitepoint.com/rubys-missing-data-structure/) | ||
|
||
## Requirements | ||
|
||
* [Setup](./setup) | ||
* [Project Requirements](./requirements) | ||
* [Evaluation Rubric](./rubric) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
--- | ||
layout: page | ||
title: Beat Box | ||
--- | ||
# Iteration 1 | ||
|
||
## Node Basics | ||
|
||
Our Linked List will ultimately be composed of individual nodes, so in this iteration we'll start with building out these nodes. | ||
Note that they are quite simple -- a Node simply needs to have a slot for some data and a slot for a "next node". Eventually this | ||
`next_node` position will be what we use to link the multiple nodes together to form the list. | ||
|
||
For this iteration, build a simple node class that can perform these functions: | ||
|
||
```ruby | ||
pry(main)> require "./lib/node" | ||
#=> true | ||
|
||
pry(main)> node = Node.new("plop") | ||
#=> #<Node:0x007fbda8a88348 @data="plop", @next_node=nil> | ||
|
||
pry(main)> node.data | ||
#=> "plop" | ||
|
||
pry(main)> node.next_node | ||
#=> nil | ||
``` | ||
|
||
## Append, To String, and Count (Single Node / Element) | ||
|
||
Great! We have nodes. In this iteration we'll create the `LinkedList` class and start filling in the basic functionality needed to append our _first node_. | ||
|
||
We'll be adding the following methods: | ||
|
||
1. `append` - creates a new node with the data that we pass into this method and adds it to the end of the linked list | ||
2. `count` - tells us how many nodes are in the list | ||
3. `to_string` - generates a string containing the data from every node in the list, separated by spaces | ||
|
||
But for now, focus on building these functions so they work for just the __first__ element of data appended to the list (we'll handle multiple elements in the next iteration). | ||
|
||
Expected behavior: | ||
|
||
```ruby | ||
pry(main)> require "./lib/linked_list" | ||
#=> true | ||
|
||
pry(main)> require "./lib/node" | ||
#=> true | ||
|
||
pry(main)> list = LinkedList.new | ||
#=> #<LinkedList:0x000000010d670c88 @head=nil> | ||
|
||
pry(main)> list.head | ||
#=> nil | ||
|
||
pry(main)> list.append("doop") | ||
|
||
pry(main)> list | ||
#=> #<LinkedList:0x0000000110e383a0 @head=#<Node:0x0000000110e382d8 @data="doop", @next_node=nil>> | ||
|
||
pry(main)> list.head.data | ||
#=> "doop" | ||
|
||
pry(main)> list.head.next_node | ||
#=> nil | ||
|
||
pry(main)> list.count | ||
#=> 1 | ||
|
||
pry(main)> list.to_string | ||
#=> "doop" | ||
``` | ||
|
||
## Append, All/To String, and Insert (Multiple Nodes) | ||
|
||
Now that we can insert the first element of our list (i.e. the Head), let's focus on supporting these operations for multiple elements in the list. | ||
|
||
This iteration is really where we'll build out the core structure that makes up our linked list -- it will probably take you more time than the previous iterations. | ||
|
||
Update your `append`, `count`, and `to_string` methods to support the following interaction pattern: | ||
|
||
```ruby | ||
pry(main)> require "./lib/linked_list" | ||
#=> true | ||
|
||
pry(main)> require "./lib/node" | ||
#=> true | ||
|
||
pry(main)> list = LinkedList.new | ||
#=> #<LinkedList:0x000000010d670c88 @head=nil> | ||
|
||
pry(main)> list.head | ||
#=> nil | ||
|
||
pry(main)> list.append("doop") | ||
#=> "doop" | ||
|
||
pry(main)> list | ||
#=> #<LinkedList:0x0000000110e383a0 @head=#<Node:0x0000000110e382d8 @data="doop", @next_node=nil>> | ||
|
||
pry(main)> list.head | ||
#=> #<Node:0x0000000110e382d8 @data="doop", @next_node=nil> | ||
|
||
pry(main)> list.head.next_node | ||
#=> nil | ||
|
||
pry(main)> list.append("deep") | ||
|
||
pry(main)> list | ||
#=> #<LinkedList:0x00000001116213a0 @head=#<Node:0x00000001116212b0 @data="doop" @next_node=#<Node:0x00000001116210f8 @data="deep", @next_node=nil>>> | ||
|
||
pry(main)> list.head.next_node | ||
#=> #<Node:0x00000001116210f8 @data="deep", @next_node=nil> | ||
|
||
pry(main)> list.count | ||
#=> 2 | ||
|
||
pry(main)> list.to_string | ||
#=> "doop deep" | ||
``` | ||
|
||
Notice the key point here -- the first piece of data we append becomes the Head, while the second becomes the Next Node of that (Head) node. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
--- | ||
layout: page | ||
title: Beat Box | ||
--- | ||
|
||
# Iteration 2 | ||
|
||
## Additional Methods - `insert` and `prepend` | ||
|
||
Now we have nodes and a `LinkedList` class that manages the list. Next step is to add the `insert` and `prepend` methods. | ||
|
||
`prepend` will add nodes to the beginning of the list. | ||
|
||
`insert` will insert one or more elements at a given position in the list. It takes two parameters, the first one is the position at which to insert nodes, the second parameter is the string of data to be inserted. | ||
|
||
Expected behavior: | ||
|
||
```ruby | ||
pry(main)> require "./lib/linked_list" | ||
#=> true | ||
|
||
pry(main)> require "./lib/node" | ||
#=> true | ||
|
||
pry(main)> list = LinkedList.new | ||
#=> #<LinkedList:0x000000010d670c88 @head=nil> | ||
|
||
pry(main)> list.append("plop") | ||
|
||
pry(main)> list.to_string | ||
#=> "plop" | ||
|
||
pry(main)> list.append("suu") | ||
|
||
pry(main)> list.to_string | ||
# "plop suu" | ||
|
||
pry(main)> list.prepend("dop") | ||
|
||
pry(main)> list.to_string | ||
#=> "dop plop suu" | ||
|
||
pry(main)> list.count | ||
#=> 3 | ||
|
||
pry(main)> list.insert(1, "woo") | ||
|
||
pry(main)> list.to_string | ||
#=> "dop woo plop suu" | ||
``` | ||
|
||
|
||
<br> | ||
|
||
## Additional Methods - `find`, `pop`, `includes?` | ||
|
||
Perfect, we are almost there! Next is to add `find`, `pop` and `includes?` methods. | ||
|
||
`find` takes two parameters, the first indicates the first position to return and the second parameter specifies how many elements to return. | ||
|
||
`includes?` gives back true or false whether the supplied value is in the list. | ||
|
||
`pop` removes the last element from the list and returns it. | ||
|
||
Expected behavior: | ||
|
||
```ruby | ||
.... | ||
pry(main)> list.to_string | ||
#=> "deep woo shi shu blop" | ||
|
||
pry(main)> list.find(2, 1) | ||
#=> "shi" | ||
|
||
pry(main)> list.find(1, 3) | ||
#=> "woo shi shu" | ||
|
||
pry(main)> list.includes?("deep") | ||
#=> true | ||
|
||
pry(main)> list.includes?("dep") | ||
#=> false | ||
|
||
pry(main)> list.pop | ||
#=> "blop" | ||
|
||
pry(main)> list.pop | ||
#=> "shu" | ||
|
||
pry(main)> list.to_string | ||
#=> "deep woo shi" | ||
``` |
Oops, something went wrong.