Tealeaf Academy C1W1 | Ruby Style | Variable Scope | Pass by value vs. Pass by reference

ruby style

屬於"照著做就對了"的慣例,沒why可言,就不多解釋了。
https://github.com/bbatsov/ruby-style-guide

tl;dr
http://www.gotealeaf.com/books/ruby/read/preparations#stylishruby

variable scope

引自Tealeaf Academy的開放教材 http://www.gotealeaf.com/books/ruby/read/variables

A variable's scope determines where in a program a variable is available for use. A variable's scope is defined by where the variable is initialized or created. In Ruby, variable scope is defined by a block. A block is a piece of code following a method invocation, usually delimited by either curly braces {} or do/end. Be aware that not all do/end pairs imply a block*.

Inner scope can access variables initialized in an outer scope, but not vice versa.

Methods create their own scope that's entirely outside of the execution flow.

The key distinguishing factor for deciding whether code delimited by {} or do/end is considered a block (and thereby creating a new scope for variables), is seeing if the {} or do/end immediately follows a method invocation.

When we use each, times and other methods, followed by {} or do/end, that's when a new block is created.

以下都針對local variable而言!
  • 姑且把variable scope想像成資源的使用權,在block外的variable是公共財,公共財每個block都能拿來用;各個block內的variable則是私有財,只能在block內使用。

  • 如何判斷variable所在的scope? 看這個variable在哪產生的。

  • Nested block的scoping rules類似,把nested想成一層一層的blocks, 較外層blocks的variable可以給較內層的blocks使用,反之則否。

  • 可以在inner scope中改變outer scope的variable, 方法是在inner scope中re-assign那個variable.

a = 1

loop do
  puts a
  a = a + 1   # a被re-assigned一個新的值

  break
end

puts a   # => 2
  • 因為上面這個原因,我們要注意,不要不小心就在inner scope中re-assign了outer scope的variable.

  • blocks有各自的scope, 所以不同的blocks如果有相同名字的variables, 彼此並不衝突。

  • 如果outer scope和inner scope有相同名字的local variables呢?因為inner scope可以使用outer scope的local variable, 等於說inner scope現在有兩個相同名字的local variables, 這種情形叫做variable shadowing, 這時inner scope無法使用outer scope的同名variable. 這衍生出兩個特性:1. inner scope會使用自己的同名local variable. 2. outer scope的同名local variable不會被inner scope的指令所改變。來看下面這個例子:

n = 10

1.times do |n|
  n = 11
end

puts n          # => 10
  • 務必避免variable shadowing的情形,做法是給variable取較長的、能夠描述這variable特質的名字,一方面是不容易產生variable shadowing的情形,另一方面萬一有scoping issues會比較好debug.

  • 如果一個method內有block呢?block variable scoping rules仍然一樣。

  • method有自己的scope, 跟上面那種outer/inner scopes的概念不同,唯一能讓method scope以外的variable進到method的方式是"代入"variable. (記住!這裡仍然只討論local variable.) 因此像下方這兩個例子,同時有兩個叫做name的local variables, 但這並不是variable shadowing.

def change_name(name)
  name = 'bob'     
end

name = 'jim'
change_name(name)
puts name           # => jim


name = 'jim'

def change_name(name)
  name = 'bob'     
end

change_name(name)
puts name           # => jim
  • 那假如是下面這種情況呢?
def change_name(name)
  name = 'bob'     
end

name = 'jim'
name = change_name(name)    # 這一行re-assign了name

puts name           # => bob

pass by value vs. pass by reference

  • 只有一個重點,如果method中有指令mutate了caller, 就會連帶影響到原來的物件。
  • 如果真要計較什麼是pass by value, 什麼是pass by reference, 舉例說:
def cap(str)
  str.capitalize!   # pass by reference

end

name = "jim"
cap(name)
puts name           # => Jim
def cap(str)
  str.capitalize   # 拿掉!(bang) pass by value

end

name = "jim"
cap(name)
puts name           # => jim

所以Ruby很有彈性地可以是pass by value, 也可以是pass by reference, 有人把它叫做call by sharingpass by reference of the value.