Skip to content

Commit

Permalink
Add material, and rewrite ch 3 4 5
Browse files Browse the repository at this point in the history
* Make much more use of 'object does x and y' instead of 'computer does
x and y'. This pattern feels far more OO and decreases the bridge
between understanding computation and object-orientation

* Simplify a bunch of language
  • Loading branch information
sjmog committed Nov 23, 2017
1 parent e552c85 commit 75238dd
Show file tree
Hide file tree
Showing 7 changed files with 657 additions and 348 deletions.
139 changes: 131 additions & 8 deletions chapter10/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -794,30 +794,153 @@ end
#### What can see a local variable?

**Regular variables** are normally referred to as **local variables**. They can be 'seen' by any object _inside_ the object in which they were defined:
**Regular variables** are normally referred to as **local variables**. Here, we're in imperative-land: telling the computer what to do, line-by-line.

If a line defining that local variable _has already been executed_, that local variable is available to anything that wants it.

```eval-ruby
# Define a local variable in the main program object
my_variable = 1
# We can access the local variable here, because the line above was executed before this one
my_variable
```

<animation showing local variables>

This is the case even if the local variable was defined in a conditional branch that got executed:

```eval-ruby
if true
# This conditional branch will be executed
my_variable = 1
end
my_variable
```

One gotcha – strange things happen if you define variables in branches that _don't_ get executed:

```eval-ruby
if false
my_variable = 1
end
my_variable
```

The program can still read the name: but the value is set to `nil`.

There's one really tricky thing about local variables, and it has to do with methods. Here it is:

```eval-ruby
def my_method
my_variable = 1
end
my_variable
```

Why can't the main object see the `my_variable` variable, even though it was defined in the lines above? The answer that most makes sense: `my_method` didn't get executed yet. We only declared it, but we didn't call the method.

So we can solve it like this, right?

```eval-ruby
# define the method
def my_method
my_variable = 1
end
# run the method, executing the procedure that defines my_variable
my_method
my_variable
```

In programming, defining methods or variables on the main program object is called defining at **global scope**. Global, because it surrounds the entire program (sometimes it's called the '_universal_ scope'). But 'scope'?
Wrong. For some reason – even though the procedure `my_variable = 1` (inside the method `my_method`) has been executed – the main object still can't see `my_variable`. Why is this?

## Scope

In Ruby, **scope** is: 'whatever an object has access to'. Think of a sniper-rifle scope: when you look down it, you can only see a part of the world: the part of the world you can shoot.
Every time we write one of the following:

- `def`
- `class`

We 'open' something.

In general, we want to _minimise the scope_ of objects. We don't want them to have access to more of the program than they should. When we do something like this:
- `def` opens a method, so we can define a procedure inside.
- `class` opens a new class, so we can define methods inside.

The keyword `end` then 'closes' that something you just opened.

```eval-ruby
named_object = Object.new
def average
# we opened the average method, so we can define procedures in it
end
class Dog
# we opened the Dog class, so we can define methods in it
end
```

Variables that we define inside these things cannot be seen outside of them:

```eval-ruby
def average
accumulator = 0
end
class Dog
some_variable = 1
end
puts accumulator
puts some_variable
```

Everything in the program can now access and use the name `named_object`: so everything in the program can now access and use the object it references. This can make for a super-confusing message flow, as every part of the program tries to access names declared somewhere else. Grow beyond a few dozen lines of code, and it's going to get horrible.
The area of a program in which a variable can be read is called the variable **scope**. `def` and `class` are known as **scope gates**: when the program runs this instruction, it enters a new scope. Variables defined inside this scope cannot be read outside of the scope gate. Variables defined outside of the scope gate cannot be read inside it.

```eval-ruby
my_variable = 1
def my_method
# I'm in a new scope gate! I can't read my_variables
my_variable + my_variable
end
my_method
```

This chapter
Here's a visual representation of scope:

<diagram of code with highlighted scope and out of scope>

> Confused by the word 'scope'? Think of the scope on top of a sniper-rifle: when you look down it, you can only see a part of the world: the part of the world you can shoot.
## Scope and parameters

Scope is especially helpful when it comes to understanding method parameters. For each parameter, methods will define a local variable within their scope. The name of that variable will be set to the name of the parameter:

```eval-ruby
age = 22
def age_reporter(number)
# Whatever we pass as an argument to age_reporter will be assigned to a local variable named 'number'
return "Your age is " + number.to_s
end
age_reporter(age)
```

Since `def` is a scope gate, we can't read these local variables outside of them:

```eval-ruby
name = "Sam"
def say_hi_to(person)
return "Hi, " + person
end
* Creating your own object worlds
# We can't read this
person
```
18 changes: 18 additions & 0 deletions chapter2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,22 @@ warning: previous definition of ONE was here

This makes sense for our purposes: we don't want anyone – be it another programmer or a user of the numeral calculator – the reassign the variables `one` through `ten` to any other numbers. By naming them `ONE` through `TEN`, we're making our intention clear: don't reassign these names, please: they're meant to stay this way.

## Modulo

A peculiar math operation sometimes used in programming is the _modulo_. It divides one number by another, then gives the 'remainder':

```eval-ruby
5 % 2
```

5 divided by 2 is 2.5. Modulo says "5 divided by 2 is 2, with 1 remaining."

Here's another example of the modulo. Play around with it until you understand what it's doing:

```eval-ruby
6 % 3
```

> 6 divided by 3 is 2. Since 2 is a whole number, modulo says "6 divided by 3 is 2, with 0 remaining."
> I've lied a bit about how numbers work in Ruby. Numbers aren't actually created at the same time as the main program function. In reality, an smaller, sub-world (a 'function') is created. When we type `100`, that function is executed in such a way as to return the number 100 to the world on-the-fly. It's a small distinction: but why does Ruby do this klind of on-the-fly generation? The answer is: this is a way to avoid slow program start-up, where the program has to generate loads of numbers before it can show the prompt. The reason we're not covering this in detail here is because this function isn't actually a Ruby function: it's a C function, which Ruby executes. You can learn more [here](https://stackoverflow.com/questions/3430280/how-does-object-id-assignment-work) if you're interested.
Loading

0 comments on commit 75238dd

Please sign in to comment.