高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Ruby 1.8 v.s. 1.9

image

其實Ruby 1.9已經推出N年,也算有點年紀的東西了。我想Ruby 1.8跟1.9的版本差別,對一般的developer來說,比較關心的點也許是這兩個版本在語法上有沒有哪邊不同? 是不是在1.8版寫的程式到1.9版都能完全不出問題? 讓我們繼續看下去…

Gems變內建的了

Rubygems在1.8版需要另外安裝,但在1.9安裝的時候也會一併裝進去,另外在1.8版可能偶爾會需要來一行:

1
require "rubygems"

這個在1.9也可以不需要了。

載入的路徑”有一點”不

在1.9版,預設的載入路徑把”.”(小數點一點,也就是目前的同錄)給拿掉了,所以在require的時候可能要注意一下。

支援Unicode

因為1.9起開始支援unicode,所以tr跟Regexp也都開始看得懂unicode了

老祖宗變了

直接來段程式碼:

1
2
3
4
5
6
7
8
class Test
end

# ver 1.8
p Test.ancestors # [Test, Object, Kernel]

# ver 1.9
p Test.ancestors # [Test, Object, Kernel, BasicObject]

Ruby 1.9版在Object上面還多了個BasicObject:

1
2
3
4
5
6
# ver 1.8
p Object.superclass # nil

# ver 1.9
p Object.superclass # BasicObject
p BasicObject.superclass # nil

BasicObject變成 1.9版的最上層類別了。

字串處理

1
2
3
4
5
6
7
# ver 1.8
p "hello"[1]   # 得到101
p ?a           # 得到97

# ver 1.9
p "hello"[1]   # 得到"e"
p ?a           # 得到"a"

另外,String#each這個方法在1.9已經被拿掉囉,請改用String#each_byteString#each_char以及String#each_line

陣列

1
2
3
4
5
# ver 1.8
p [1, 2, 3, 4, 5].to_s  # 得到"12345"

# ver 1.9
p [1, 2, 3, 4, 5].to_s  # 得到"[1, 2, 3, 4, 5]"

印出來的結果不一樣了

Hash

1
2
3
4
5
6
7
me = {:name => 'eddie', :age => 18, :gender => 1}

# ver 1.8
p me # 得到 {:age=>18, :gender=>1, :name=>"eddie"}

# ver 1.9
p me # 得到 {:name=>"eddie", :age=>18, :gender=>1}

在1.9版的hash,不管是新建立或是後來再附加上去,它都變得有”順序”了

另外1.9版有支援新的寫法,上面這段就可以改寫成:

1
me = {name: "eddie", age: 18, gender: 1}

它會很聰明的自動把key轉成symbol。在Rails裡常用到的redirect_to,原本這樣寫:

1
redirect_to :action => index

也可以改寫成:

1
redirect_to action: index

Block

看一下這段程式碼:

1
2
3
4
5
6
i = 0
p i
(1..5).each do |i|
  # do something
end
p i

在1.8版本,最後一行會印出數字5,但在1.9則是印出數字0,block裡的區域變數不會影響到外部的變數了。

Method

使用methods方法可以印出該類別或物件的方法:

1
2
3
# ver 1.8
p String.methods.sort
["<", "<=", "<=>", "==", "===", "=~", ">", ">=", "__id__", "__send__", "allocate", "ancestors", "autoload", "autoload?", "class", "class_eval", "class_exec", "class_variable_defined?", "class_variables", "clone", "const_defined?", "const_get", "const_missing", "const_set", "constants", "display", "dup", "enum_for", "eql?", "equal?", "extend", "freeze", "frozen?", "hash", "id", "include?", "included_modules", "inspect", "instance_eval", "instance_exec", "instance_method", "instance_methods", "instance_of?", "instance_variable_defined?", "instance_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_of?", "method", "method_defined?", "methods", "module_eval", "module_exec", "name", "new", "nil?", "object_id", "private_class_method", "private_instance_methods", "private_method_defined?", "private_methods", "protected_instance_methods", "protected_method_defined?", "protected_methods", "public_class_method", "public_instance_methods", "public_method_defined?", "public_methods", "respond_to?", "send", "singleton_methods", "superclass", "taint", "tainted?", "tap", "to_a", "to_enum", "to_s", "type", "untaint"]

回傳的是方法的字串陣列。

1
2
3
p String.methods.sort
# ver 1.9
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :allocate, :ancestors, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :constants, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include?, :included_modules, :initialize_clone, :initialize_dup, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :private_class_method, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :respond_to?, :respond_to_missing?, :send, :singleton_class, :singleton_methods, :superclass, :taint, :tainted?, :tap, :to_enum, :to_s, :trust, :try_convert, :untaint, :untrust, :untrusted?]

在1.9變成回傳symbol的陣列了。那會有什麼影響? 如果你有寫到這樣的程式碼來判斷某個類別是否有實作某個方法的話:

1
p String.methods.include? "to_s"

在1.8會是true,但在1.9就會是false了。所以如果你要做這樣的判斷,請使用respond_to?method_defined?

1
2
p String.respond_to? "to_s"
p String.method_defined? "to_s"

Lambda

1
2
3
4
5
6
7
# ver 1.8
my_calc = lambda { |a, b| a * b }
p my_calc.call(10, 20)

# ver 1.9
my_calc2 = -> a, b { a * b }
p my_calc2.(10, 20)

在1.9版的lambda可以用上面這個方法來寫,而且call方法也可以省略。

預設參數

1
2
3
4
5
6
7
def hello(a, b = 10, c)
  p a, b, c
end

p hello(1)        # ArgumentError
p hello(1, 2)     # [1, 10, 2]
p hello(1, 2, 3)  # [1, 2, 3]

在1.9版在參數個數不夠的時候,可以直接填給沒有預設參數的部份。

結論

其實這兩個版本的差異當然不只這樣,我僅就我個人比較常用到的部份做個筆記,順便也提醒自己。以上內容若有任何錯誤再請讓我知道,感謝。

參考資料:

Comments