Programming Foundations Flashcards
Are variables accessible outside an ‘if condition’?
Yes, they are - since an ‘if condition’ does not create a block.
Variables created inside a block aren’t visible to the outer scope.
Where are Kernel methods available in Ruby?
Everywhere. The Kernel module is loaded by ‘Object’, giving us access to all of its methods.
Why can we omit the Kernel. in front of method names?
When we write Ruby code that's not in a class, we are working with an object called 'main', which is an instance of 'Object'. The 'Kernel' module is automatically loaded by 'Object , giving us access to all of its methods.
What are some good guidelines on how to write good methods?
- It shouldn’t display something to the output as well as return a value
- Decide whether the method should return something with no side effects OR only perform side effects with no return. The naming should also reflect this.
‘
What is ‘truthiness’ in Ruby?
Ruby is very liberal about what ‘true’ means.
In Ruby, everything is truthy except nil and false.
Because of this, we don’t have to compare an expression to ‘true’ or ‘false’, and can rely on the expression’s ‘truthiness’ directly.
Example:
if user_input == true
could be just
if user_input
What determines variable scoping?
Whether you’re working with blocks (a piece of {..} or do..end following a method invocation.
Example:
3.times do |n|
a = 3
end
Things like loops also have an inner scope. However, they can access outer scope variables and even make changes to them (for example, if you re-assign an outer scope variable in a loop, that change will persist in the outer scope!”
Can methods access outer scope variables?
No, they create their own variable scoping that’s entirely out of the execution flow.
The one exception to this is Constants. They tend to behave like global variables and can be accessed by methods and blocks.
What is variable shadowing?
Variable shadowing:
We’ve been using the loop do…end, which doesn’t take a parameter, but some blocks do take a parameter. Take for example:
[1, 2, 3].each do |n|
puts n
end
The block is the do…end, and the block parameter is captured between the | symbols. In the above example, the block parameter is n, which represents each element as the each method iterates through the array.
But what if we had a variable named n in the outer scope? We know that the inner scope has access to the outer scope, so we’d essentially have two local variables in the inner scope with the same name. When that happens, it’s called variable shadowing, and it prevents access to the outer scope local variable. See this example:
n = 10
[1, 2, 3].each do |n|
puts n
end
The puts n will use the block parameter n and disregard the outer scoped local variable. Variable shadowing also prevents us from making changes to the outer scoped n:
n = 10
1.times do |n|
n = 11
end
puts n # => 10
You want to avoid variable shadowing, as it’s almost never what you intended to do. Choosing long and descriptive variable names is one simple way to ensure that you don’t run into any of these weird scoping issues. And if you do run into these issues, you’ll have a much better chance of debugging it if you have clear variable names.
How do blocks and methods interface with each other?
Technically any method can be called with a block, but the block is only executed if the method is defined in a particular way.
A block is part of the method invocation; essentially the block acts as an argument to the method.
In the same way that a local variable can be passed as an argument to a method at invocation, when a method is called with a block it acts as an argument to that method.
Important to know is that, while methods can not access local variables initialized outside the method scope, blocks CAN do this. They can also reassign those variables!
Key takeaways about methods and blocks :
- The def..end construction in Ruby is method definition
- Referencing a method name, either of an existing method or subsequent to definition, is method invocation
- Method invocation followed by {..} or do..end defines a block; the block is part of the method invocation
- Method definition sets a scope for local variables in terms of parameters and interaction with blocks
- Method invocation uses the scope set by the method definition
What does it mean when a method is mutating the caller?
This will affect the original object that is passed in.
Is Ruby pass by reference or pass by value?
Some languages make copies of method arguments, and pass those copies to the method — since they are merely copies, the original objects can’t be modified. Objects passed to methods in this way are said to be passed by value, and the language is said to be using a pass by value object passing strategy.
Other languages pass references to the method instead — a reference can be used to modify the original object, provided that object is mutable. Objects passed to methods in this way are said to be passed by reference, and the language is said to be using a pass by reference object passing strategy
Pass by value, as you’ll recall, means copying the original objects, so the original object cannot be modified. Since immutable objects cannot be changed, they act like Ruby passes them around by value. This isn’t a completely accurate interpretation of how Ruby passes immutable objects, but it helps us determine why the following code works like it does
So, is that our final answer to the question of whether ruby is pass by reference or pass by value? It’s neither? Yes. Well, maybe not entirely; there are actually three answers to the question of what object passing strategy ruby uses:
- pass by reference value is probably the most accurate answer, but it’s a hard answer to swallow when learning ruby, and isn’t particularly helpful when trying to decide what will happen if a method modifies an argument – at least not until you fully understand it.
- pass by reference is accurate so long as you account for assignment and immutability.
- Ruby acts like pass by value for immutable objects, pass by reference for mutable objects is a reasonable answer when learning about ruby, so long as you keep in mind that ruby only appears to act like this.
How do mutable vs non-mutable objects get passed into Ruby methods?
def increment(a) a = a + 1 end
b = 3 puts increment(b) # prints 4 puts b # prints 3
Here, the numeric object 3 is immutable. You can reasonably say that b is not modified by #increment since b is passed by value to #increment where it is bound to variable a. Even though a is set to 4 inside the method, and returned to the caller, the original object referenced by b is untouched.
Mutable objects, on the other hand, can always be modified simply by calling one of their mutating methods. They act like Ruby passes them around by reference; it isn’t necessary for a method to modify an object that is passed by reference, only that it can modify the object. As you’ll recall, pass by reference means that only a reference to an object is passed around; the variables used inside a method are bound to the original objects. This means that the method is free to modify those objects. Once again, this isn’t completely accurate, but it is helpful.
Is indexed assignment mutating or non-mutating?
It’s mutating.
Indexed assignment, such as that used by String, Hash, and Array objects can be confusing:
str[3] = ‘x’
array[5] = Person.new
hash[:age] = 25
This looks exactly like assignment, which is non-mutating, but is, in fact, mutating. #[] modifies the original object (the String, Array, or Hash). It doesn’t change the binding of each variable.
What is Object Passing?
In ruby, nearly everything is an object. When you call a method with some expression as an argument, that expression is evaluated by ruby and reduced, ultimately, to an object.
The expression can be an object literal, an variable name, or a complex expression; regardless, it is reduced to an object. Ruby then makes that object available inside the method.
This is called passing the object to the method, or, more simply, object passing.
Why is Object Passing strategy important?
Most computer languages that employ strict evaluation use pass by value by default. Most of those languages also make it possible to pass by reference when needed.
Few languages are purely pass by value or pass by reference. Understanding which strategy is used (and when) is key to understanding what happens to an object that gets passed to a method.
For example, if the method does something that appears to change the object, is that change local to the method, or does it result in changes to the original object?