Notes: Assessment 109

Variables

Variable Scope

  • Variable scope determines where in a program a variable is available for use.
    • In Ruby, a variable's scope is defined by a block, a piece of code following a method invocation, usually delimited by either curly braces {} or do/end.
  • Inner scope can access variables initialized in an outer scope, but not vice versa.
 

Types of Variables

  • Constants
    • Declared using ALL_CAPS
    • Used for storing data that never needs to change
    • Values should not change, though they can
MY_CONSTANT = 'I am available throughout your app.'
  • Global Variables
    • Declared by starting the variable name with $
    • Available throughout entire app, overriding scope boundaries
    • Used less often due to unforeseen/unexpected complications
$var = 'I am also available throughout your entire app.'
  • Class Variables
    • Declared by starting variable name with @@ and must be initialized at the class level, outside of any method definitions
    • Accessible by instances of your class as well as the class itself
    • Used when you need to declare a variable that is related to a class, but not needed by each instance of that class
@@instances = 0
  • Instance Variables
    • Declared by starting the variable name with @
    • Used throughout the current instance of the parent class
    • Can cross some but not all scope boundaries
@var = 'I am available throughout the current instance of this class.'
  • Local Variables
    • Obey all scope boundaries
var = 'I must be passed around to cross scope boundaries.'
 

Variables As Pointers

  • Variables are pointers to physical space in memory (i.e. labels we create to refer to some physical memory address)
  • Certain operations mutate the actual address space in memory, thereby affecting all variables that point to that address space (mutation)
  • Other operations will not mutate the address space in memory, and instead will re-point the variable to new address space in memory (reassignment)
  • Remember: arguments passed into methods are essentially variables being assigned to another variable (reassignment) and thus may or may not modify the original outer scope variable depending on whether we modify the address space in memory that the outer variable to which the outer variable points
 Credit: Launch School, References & Mutability

Credit: Launch School, References & Mutability

 

Mutability

  • Immutable objects -- numbers and boolean values
    • Any class can establish itself as immutable by not providing any methods that alter its state
  • Mutable objects -- most other objects in ruby
# Example 1: Immutable Objects
a = 5.2
b = 7.3
a.object_id == b.object_id # false
a = b
a.object_id == b.object_id # true
b += 1.1 # 8.4
a.object_id == b.object_id # false

# Example 2: Mutable Objects
a = %w(a b c)
a.object_id # 123
a[1] = '-'
a # ['a', '-', 'c']
a.object_id # 123 -- a is not changed, but a[1] is

 

Methods

Method Definition & Invocation

  • Begin with the reserved word 'def' and end with reserved word 'end'
  • Parameters are used when you have data outside of a method definition's scope but need to access it within the method
  • Arguments are pieces of information that are sent to a method invocation to be modified or used to return a specific result
  • When arguments are passed to methods, they are assigned to local variables with names defined as parameters in the method definition
  • Parentheses are optional when invoking a method
  • Methods create their own scope outside of regular execution flow
    • Local variables within a method definition cannot be referenced from outside the method
    • Local variables within a method definition cannot access data outside the method (unless passed as a parameter)
  • Be careful not to mix up method invocation with a block with method definition -- different local variable scoping
  • Methods can be invoked/called with some_method(obj) or obj.some_method
# defining say method, words as parameter with 'hello' as default value 
def say(words='hello')
  puts words
end 
say('hi') # invoking say method with 'hi' as argument 
say 'hi'  # same as above

# Method invocation with a block
[1, 2, 3].each do |num|
  puts num
end

# Method definition
def print_num(num)
  puts num
end
 

Collections

  • Strings
    • Reference
      • Integer-based index that represents each character in the string, starting at 0 and incrementing by 1
      • Multiple characters: string[index_starting_point, number_of_characters]
        • str[2, 3] is actually a call to the #slice method of String and is an alternative syntax for str.slice(2, 3)
    • Assignment -- uses element assignment notation
  • Arrays
    • Reference
      • Like strings, indexes are ordered, zero-indexed collections
      • #fetch can be used to avoid confusion over out-of-bounds references
    • Assignment -- uses element assignment notation
  • Hashes
    • Reference
      • Data structure comprised of key-value pairs, where keys and values can be any type of Ruby object
      • Keys must be unique -- if there are duplicate keys, the originals will be overwritten
      • Symbols are commonly used as keys -- can be thought of as immutable strings
      • #fetch can be used to avoid confusion over invalid keys
    • Assignment -- uses key assignment notation
# Strings
str = 'abcdefg'
str[2] # => "c"
str[2, 3] # => "cde"
str[2, 3][0] # => "c"

str = "joe's favorite color is blue"
str[0] = 'J'
str # => "Joe's favorite color is blue"

# Arrays
arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
arr[2] # => "c"
arr[2, 3] # => ["c", "d", "e"]
arr.fetch(3) # => IndexError: index 3 outside of array bounds: -3...3 etc

arr = [1, 2, 3, 4, 5]
arr[0] += 1 # => 2
arr # => [2, 2, 3, 4, 5]

# Hashes
hsh = { 'fruit' => 'apple', 'vegetable' => 'carrot' }
hsh['fruit'] # => "apple"
hsh['fruit'][0] # => "a"
hsh.fetch('c') # => KeyError: key not found: "c" etc

country_capitals = { uk: 'London', france: 'Paris', germany: 'Berlin' }
country_capitals.keys # => [:uk, :france, :germany]
country_capitals.values # => ["London", "Paris", "Berlin"]

hsh = { apple: 'Produce', carrot: 'Produce', pear: 'Produce', broccoli: 'Produce' } 
hsh[:apple] = 'Fruit'
hsh # => { :apple => "Fruit", :carrot => "Produce", :pear => "Produce", :broccoli => "Produce" }



 
def add_three(number)
  return number + 3 # 7 is returned
  number + 4        # This line is not executed
end

returned_value = add_three(4)
puts returned_value # 7 is printed to the screen

puts vs return

  • Ruby returns the evaluated result of the last executed line unless an explicit return comes before it.
    • return reserved word is not required to return something from a method
  • puts returns nil
 

Mutating the Caller

  • Mutation -- when an argument passed to a method is altered permanently
  • Be careful: it's important to understand the implications of mutation
    • Reassignment to a variable doesn't change object referenced by variable. Instead, variable is bound to completely new object and original object is "disconnected" from variable
# Example
greeting = 'hello'
wazzup = greeting
wazzup.object_id == greeting.object_id # true
greeting.upcase! # 'HELLO' (mutation)
wazzup # HELLO
greeting = 'Dude!' # reassignment
puts greeting # 'Dude!'
puts wazzup # 'HELLO'
greeting.object_id == wazzup.object_id # false
 

Object Passing

  • Ability of method to modify arguments depends on:
    1. Mutability/immutability of object represented by argument
    2. How argument is passed
  • "Pass by Value" (PBV)
    • Actual arguments are copies of original objects represented by arguments
    • Original arguments are not modified
  • "Pass by Reference" (PBR)
    • Arguments are references to original objects
    • Objects can be changed assuming they are mutable
    • In PBR, assignment is a mutating operation
      • Not true in ruby
      • ruby variables and constants aren’t objects, but are references to objects
  • Because ruby shares characteristics of both Pass by Reference and Pass by Value, we call it a "Pass by Reference Value" language
 

Tips

  • Pay attention to outputs, returns, and mutations to objects
  • Integer, fixnum, and bignum have all been consolidated under integer, which should be used for whole numbers