RUBY程式語言

入門導覽
鄧慕凡
2014.03.29@實踐⼤大學
irb: Interactive Ruby

   馬上動手練習

開啟終端機:
ryudo@ryudo-x61:~$ irb
irb(main):001:0> 1+3
=> 4
irb(main):002:0> 'hello' + ' world'
=> "hello world"
IRB(Interactive Ruby Shell)
• 互動式交談	
  
o 每次指令的結果都會在下一行輸出	
  
• 簡單測試的最佳工具
Ruby 是直譯式強型別語言
• 直譯式語言	
  
o Ruby/PHP/Perl/Python/VB	
  
• 編譯式語言	
  
o Java/C/C++	
  
• 強型別	
  
o Ruby/JAVA	
  
• 弱型別	
  
o PHP
Ruby是強型別(Strongly typed)語言
irb(main):003:0> i=1
=> 1
irb(main):004:0> 'hello' + i
TypeError: can't convert Fixnum into String from
(irb):4:in `+' from (irb):4
整數 Integer
10
5
10000
9999999999999999999999
0
-100000
浮點數 Float
1.1
-100.28
3.14159
輸出
• PUTS	
  
– 輸出結果值的.to_s,然後換行	
  
• P	
  
– 輸出結果值的.inspect,然後換行	
  
– 通常拿來debug
輸入
• gets	
  
– 得到按下enter前的內容	
  
– 內容是字串	
  
– 後接.chomp避免把換行也加進來
整數四則運算(結果整數)
1+1 #= 2
200*3 #= 600
10-999 #= -989
100/20 #= 5
100/30 #= 3
浮點數四則運算(結果Float)
irb(main):001:0> 5 * 2.0 => 10.0
irb(main):002:0> 2.0 + 100.2 => 102.2
irb(main):003:0> 2.2 * 5.5 => 12.1
irb(main):004:0> 100.0 / 4.0 => 25.0
irb(main):008:0> 9.0 / 2.0 => 4.5
更多運算
puts 5 * (10 + 5) -13
puts 999 + (31456 / (13 * 7) ) * -70
字串 String
puts 'Hello, world!'
puts '' puts 'Good-bye.'
puts '⾺馬上就會好'
Try it out!
字串的建構
• 'abc'單引號

– 裡面可包含"	
  
• "def"雙引號

– 可內插變數	
  
– 可內插控制字元(rnt等..)
字串可+不可-
irb(main):008:0> 'abc' + 'def'
=> "abcdef"
irb(main):010:0> 'abc'.concat('def')
=> "abcdef"
irb(main):009:0> 'abc' - 'def'
NoMethodError: undefined method `-' for
"abc":String
concat和+的差異
2.0.0p353 :036 > str1 = 'foo'
=> "foo"
2.0.0p353 :037 > str1 + 'bar'
=> "foobar"
2.0.0p353 :038 > str1
=> "foo"
2.0.0p353 :039 > str1 << 'bar'
=> "foobar"
2.0.0p353 :040 > str1
=> "foobar"
更多字串函式
var1 = 'stop'
var2 = 'foobar'
var3 = "aAbBcC"
var4 = ' 那很好哇 '
puts var1.reverse # 反轉
puts var2.length # ⻑⾧長度
puts var3.upcase #轉⼤大寫
puts var3.downcase #轉⼩小寫
puts var4.strip #左右去空⽩白
格式化字串
• sprintf(輸出變數)	
  
• printf(輸出畫面)	
  
• 風格類似C的同名函式

• %s字串,	
  %d整數,	
  %f浮點數..
格式化字串
i1 = 123
f1 = 456.789
printf('我有 %d 元', i1) #我有 123 元
printf('%0.5d', i1) #00123
printf('%3.5f', f1) #456.78900
練習1
• 輸出詢問名稱和年紀的訊息,再於畫
面中show出對話
練習1
puts 'Hello there, and what's your name?'
name = gets.chomp
puts 'Your name is ' + name + '? What a nice
name!'
puts 'Pleased to meet you, ' + name + '. :)'
完全物件導向的語言

包括字串和數字都是物件,無內建型別

irb(main):001:0> 'abc123'.upcase
=> "ABC123"
irb(main):002:0> 'abc123'.class
=> String
irb(main):003:0> 'abc123' ==
String.new('abc123')
=> true
irb(main):004:0> 3.times{puts '你累了嗎?'}
你累了嗎?
你累了嗎?
你累了嗎?
=> 3
(區域)變數 Variable

必需是小寫英文字開頭
a = 100
b = 10.5
a * b #= 1050.0
c = 'SO'
d = 'GO'
c+d #= "SOGO"
全域變數
• 以$開頭的變數
字串內插變數
i1 = 123
puts "Iam#{i1}" #"Iam123"
型別轉換 Conversions
var1 = '2'
var2 = 4
var3 = 4.5
var1.to_i * var2 #= 8
var1.to_f * var3 #= 9.0
var3.to_s + var1 #= 注意是字串
常數 Constant

大寫開頭

irb(main):003:0> Foo = 1
=> 1
irb(main):004:0> Foo = 2
(irb):4: warning: already initialized
constant Foo
=> 2    #雖然警告但其實OK
irb(main):005:0> RUBY_PLATFORM
=> "i686-linux"
irb(main):006:0> ENV    #會噴出⼀一堆環境變數
Nil 空值
• 表示為空

• 不等於false/0/空字串('')
irb(main):001:0> nil
=> nil
irb(main):002:0> nil == false
=> false
irb(main):003:0> nil == ''
=> false
註解 #
• 以#開頭之後為註解
陣列 Array
[1,2,3,4]
[1,'abc',false,[nil,nil]] #多維陣列
[1,2,3,4].reverse # [4, 3, 2, 1]
[1,'abc',false,[nil,nil]].size # 4
[1,'abc',false,[nil,nil]].flatten # [1,
"abc", false, nil, nil]
索引操作
• [n]	
  取得第n個

• [n..m]	
  取得第n至m個

• [n...m]	
  取得n至m-­‐1個

• [n,len]取得n開始的len個
索引操作
arr = [1,2,3,4,5,6,7,8,9]
p arr[2..3] #[3, 4]
p arr[2...4] #[3, 4]
p arr[2,2] #[3, 4]
陣列相加
[1,2,3] + [4,5,6] # [1, 2, 3, 4, 5, 6]
[1,2,3].concat [4,5,6] # [1, 2, 3, 4, 5, 6]
更多陣列函式(1)
ossf=['whoswho','openfoundry','org']
ossf << 'tw'
# ["whoswho", "openfoundry", "org", "tw"]
ossf.push 'www2'
# ["whoswho", "openfoundry", "org", "tw", "www2"]
ossf.unshift 'www2'
# ["www2", "whoswho", "openfoundry", "org", "tw",
"www2"
更多陣列函式
ossf.pop #返回從後⾯面取出的值, 但陣列本⾝身已被改變
#= ["www2", "whoswho", "openfoundry", "org",
"tw"]
ossf.shift ##返回從前⾯面取出的值, 但陣列本⾝身已被改變
#= ["whoswho", "openfoundry", "org", "tw"]
字串陣列共通的方法
• 操作索引

– [n]、[m..m]、[n,len]等	
  
• Enumerable類方法

– each、map等	
  
• 連接、逆轉相關方法

– reverse、concat、delete
取得字串的字元碼
'Abcde123HG'.bytes.to_a
=> [65, 98, 99, 100, 101, 49, 50, 51, 72, 71]
雜湊 Hash
h = {:abc => 123, 'def' => 456}
#= {"def"=>456, :abc=>123}
h[:abc]
#= 123
h['def']
#= 456
字串符號 Symbols
• 不會變動的唯一辨識符
h2 = { :abc => 123, 'abc' => 456}
#= {"abc"=>456, :abc=>123}
:abc.object_id
#= 263138 #不論宣告幾次都是⼀一樣的實體
'abc'.object_id
#= 84140440
'abc'.object_id
#= 84137350 #字串重複宣告後就不⼀一樣實體
PUTS/P 範例
irb(main):011:0> h = {:abc => 123}
=> {:abc=>123}
irb(main):012:0> p h
{:abc=>123}
irb(main):013:0> puts h
abc123
irb(main):014:0> puts :ossf
ossf
irb(main):015:0> p :ossf
:ossf
流程控制
• 大於 >	
  
• 小於 <	
  
• 大於等於 >=	
  
• 等於 ==	
  	
  
• 不等於 !=	
  
• AND	
  &&	
  
• OR	
  ||	
  
• NOT	
  !
結構控制
• If	
  …	
  end	
  
• Unless	
  …	
  end	
  
• Case	
  when	
  end
if
puts "請輸⼊入你的⽉月薪"
your_money = gets.to_i
if your_money >= 90000
puts "百萬年薪"
elsif your_money >= 22000
puts "普通上班族"
else
puts '總統德政'
end
unless
• 除了沒有elsif外都跟if一樣,只是
條件相反
CASE
aaa = [1 ,'abc', 1.3]
p aaa
printf('你要確認哪⼀一個?')
idx = gets.to_i
case aaa[idx]
when String
puts "這是⼀一個字串"
when Integer
puts "這是⼀一個整數"
when Float
puts "這是⼀一個浮點數"
when Numeric
puts '這是⼀一個數字'
else
puts "這是其它類型的物件"
end
三元運算子
EXPRESSION	
  ?	
  (True	
  Condition):(False	
  Condition)
a = 10; b = 100
a > b ? ("#{a} > #{b}"):("#{a} < #{b}") #=> "10 <
100"
迴圈
!48
10.times do
puts '那很好啊'
end
迴圈
!49
for i in 1..10
puts i
end
迴圈
!50
x = 100
while x > 10
x = x - 10
puts x
end
迴圈
!51
x = 100
until x <= 10
x = x – 10
next if x == 50 #跳過下⼀一步繼續
puts x
end
迴圈
!52
abc = [1,2,3]
loop do
abc.pop
p abc
break if abc.empty? #跳出
end
真或假
• 只有nil	
  和 false為假,其它為真

• 所以空字串和0也是真
練習
1. 建⽴立1-100的陣列
2. 將1的結果全部x10倍
練習2 : 猜數字
• 亂數產生一個數字

– rand(20)會產生0~20間的隨機數字	
  
– 避免0	
  
• 輸入一個數字

• 猜對了就跳出

• 猜錯了就顯示猜錯了

– 差10顯示還差很多	
  
– 10比內顯示接近了
randnum = 0
loop do
randnum = rand(20)
break if randnum > 0
end
5.times do
print '請輸⼊入⼀一個數字:'
num = gets.chomp.to_i
if num == randnum
puts '猜對了~YEAH!'
break
end
if num - randnum >= 10
puts "猜的⼤大很多"
elsif (num - randnum) < 10 && (num - randnum) >= 1
puts '猜的⼤大⼀一點'
elsif randnum - num >= 10
puts '猜的⼩小很多'
else
puts '猜的⼩小⼀一點'
end
end
正規表示式
phone = "002-2882-5252"
if phone =~ /(d+)-(d+)-(d+)/
area = $1
first = $2
second = $3
end
p [area, first, second]
Method 方法
依接收者分為三大類

• 實體方法(Instance	
  Method)	
  
• 類別方法(Class	
  Method)	
  
• 函式性的方法
實體方法(Instance Method)
• 屬於某個類別new出來的「物件實
體」之方法

• 接收者(self)為物件實體
'abcdef'.reverse #=> "fedcba"
類別方法(Class Method)
• 類別本身的方法

• 接收者(self)為類別

• 建立物件的.new即為類別方法
irb(main):009:0> a = Date.today
=> #<Date: 4910677/2,0,2299161>
irb(main):010:0> puts a
2010-05-22
函式性的方法
• 沒有接收者也能執行的方法

• 其實並非沒有接收者,只是被省略的
情形
sprintf("%d %04x", 123, 123) #=> "123 007b
定義函式
• Def	
  函式名稱..end	
  
• 以return返回值

• 如無設定return,以最後一個運算式
的結果為return
def hello(name)
sprintf('哈囉 %s', name)
end
定義函式之參數
• def(參數..)	
  
• 可設定預設值,表示可省略

• 可省略者「必需」在不可省略者之前
def showmanytimes(text2show, times = 10) #=> OK!
times.times{puts text2show}
end
? 與 ! 的慣例
• 函式定義以?結尾者

– 表示回傳值為boolean	
  
• 函式定義以!結尾者

– 表示會改變接收者本身
irb(main):001:0> [1,2,3,4].empty?
=> false
irb(main):002:0> [1,2,3,4,[5,6]].flatten!
=> [1, 2, 3, 4, 5, 6]
類別(Class)與實體(Instance)
• 類別 →	
  模子

• 實體 →	
  模子印出來的

• Ruby的萬物皆類別!
'foo'.class => String
定義類別
class Duck
def initialize(name)
@name = name
end
def quack #實體⽅方法
"#{@name} is quacking!"
end
end
d = Duck.new('唐⽼老鴨!')
puts d.quack #唐⽼老鴨! is quacking!
定義類別
class Car
@@amount = 0
def initialize(name)
@name = name
@@amount += 1
end
def name=(val)
@name = val
end
def name
@name
end
def self.amount
@@amount
end
end
c1 = Car.new('霹靂⾞車' ); c2 = Car.new('⽕火戰⾞車' )
c3 = Car.new('Focus'); c3.name = 'Focus ST'
puts "現在有 " + Car.amount.to_s + " 台⾞車了"
類別定義內也可以執行程式
!68
class Car
def initialize(name)
@name = name
end
def name=(val)
@name = val
end
def name
@name
end
end
類別定義內也可以執行程式
class Car
def initialize(name)
@name = name
end
attr_accessor :name
end
建立物件變數的存取函式
• attr_accessor	
  
– 等於下面兩者相加	
  
• attr_writer	
  
– 只有寫入 代替def	
  xxx=(val)	
  end	
  
• attr_reader	
  
– 只有讀取 代替 def	
  xxx	
  end
Public/Protected/Private
• Public	
  
– 預設值,無限制呼叫對象	
  
• Protected	
  
– 繼承者可以呼叫,外部禁止,但同類別(含子
類別)的其它實體可以呼叫	
  
• Private	
  
– 繼承者可以呼叫,外部禁止同類別(含子類
別)的其它實體不可以呼叫
私有/保護 定義方式
class HogeSuper
protected
def protected_method
puts "protected"
end
private
def private_method
puts "private"
end
end
私有/保護 定義方式
class Hoge < HogeSuper
def hoge
protected_method # OK
private_method # OK
a = Hoge.new
a.protected_method # OK
a.private_method # Error
end
end
Hoge.new.hoge
類別之繼承
• 萬物皆繼承Object這個類別的子類

• 被繼承者為父類別(superclass)	
  
• 繼承者為子類別(subclass)
繼承類別
class Vehicle
attr_accessor :tires
end
class Car < Vehicle
def initialize(name)
@tires = []
4.times{@tires << Tire.new}
end
end
class Motorcycle < Vehicle
def initialize(name)
@tires = []
2.times{@tires << Tire.new}
end
end
探索繼承類別
c = Car.new('Mondeo 2.0')
puts c.is_a?(Vehicle) #true
puts c.class.superclass #Vehicle
走訪迴圈
langs = ['VB', 'C#', 'C', 'JavaScript']
langs.each do |lang|
puts "我會 #{lang}"
end
輸出:
#我會 VB
#我會 C#
#我會 C
#我會 JavaScript
迭代器 iterator
• 不同於for或while,迭代器是一種走
訪接收者(通常是陣列或陣列的子類)
元素的「方法」

• do..end是迭代器方法的參數,可以
將其代換為{},此類參數通稱為
「code	
  block」,也稱作匿名函式
Code Block
• Ruby主要特色之一

• 由於是一種匿名函式,故可接收參數

• 習慣Ruby的人不會常用for/while,會
使用iterator和code	
  block
Code block
10.times{|x| puts x} #輸出0到9
#
(1..9).each{|x| puts x} #1到9
#
(1...9).each{|x| puts x} #1到8
#
['ggg', 123, Time.now].each_with_index
do |x, i|
puts "第#{i+1}個元素的類別
是:#{x.class.to_s}"
end
#第1個元素的類別是:String
#第2個元素的類別是:Fixnum
#第3個元素的類別是:Time
進階Code Block
arr = [1,2,3,4,5,6]
new_arr = arr.map{|a| a+10}
#=> 造出新陣列 [11, 12, 13, 14, 15, 16]
Enumerator
arr = [1,2,3,4,5,6]
arr.select{|a| a > 3}
#=> 只挑選⼤大於3者 [4, 5, 6]
Enumerator
arr = [1,2,3,4,5,6]
arr.reject{|a| a>3}
#=> 和select相反
Enumerator
[4,5,1,2,3].sort{|a, b| a <=> b} #=> 基
本排序
Enumerator
arr1 =
['abc','whoswho.openfoundry.org','OSSF
']
arr1.sort_by{|w| w.size} #⽤用字數排序
arr1.sort{|a,b| a.size <=> b.size } #
和上⾯面相同
Enumerator
arr2 = [5,10,15,20,25]
arr2.inject{|sum, x| sum += x } #計算總
合
=> 75
練習
• 之前建⽴立的1-100陣列,⽤用rand將順序打亂
• rand(n)的作⽤用為產⽣生0-n的亂數
練習3
• 將多個浮點數的陣列中每個元素四捨
五⼊入後再相加	
  
• 四捨五⼊入:Float#round	
  
• 使⽤用inject
練習3
[3.33, 4.5, 5.10].inject(0){|sum, x|
sum = sum + x.round } #=> 13
練習
1. 請定義⼀一個Hash像這樣
• wday[:sunday] = '星期⽇日'
• wday[:monday] = ‘星期⼀一'
2. 將這個Hash如下印出
• sunday 是 星期⽇日
• monday 是 星期⼀一
Yield and Method

在函式中使用 yield 來執行 code block
def test_block
puts "I love Ruby ,"
yield
end
test_block{ puts 'Ruby loves
programmers!'}
#顯⽰示
I love Ruby ,
Ruby loves programmers!
Yield and Method

在函式中使用 yield 來執行 code block
def to_div(times)
buffer = '<DIV>'
times.times{|x| yield(buffer, x)}
buffer.concat '</DIV>'
end
divhtml = to_div(3) do |buf, x|
buf.concat "<p>No.#{x+1}</p>"
end
puts divhtml
#
<DIV><p>No.1</p><p>No.2</p><p>No.3</p></DIV>
Proc object

將 code block 明確轉成物件
def test_block(&block) #注意&
printf "I love Ruby ,"
block.call
end
def to_div(times, &block) #注意&
buffer = '<DIV>'
times.times{|x| block.call(buffer, x)}
buffer.concat '</DIV>'
end
blk1 = Proc.new{ puts('Ruby loves programmers!')}
blk2 = lambda{|buf, x| buf.concat "<p>No.#{x+1}</p>"}
test_block(&blk1)
puts to_div(3,&blk2)
傳遞不定參數
• Def	
  xxx(*args)	
  
• 在方法內被視為陣列
def abc(*args)
buf = ''
args.each{|x| buf.concat yield(x)}
return buf
end
abc('I','LOVE','OSSF') do |inner_text|
"<p>#{inner_text}</p><BR/>"
end
#輸出
#<p>I</p><BR/><p>LOVE</p><BR/><p>OSSF</p>
參數尾 Hash 可省略 { }
def tag_with_html(tag_name, inner_text, html_options)
……
end
puts tag_with_html('td', 'Ruby Programming', :bglocor
=> 'Red', :width => 100)
puts tag_with_html('td', 'Ruby Programming', {:bglocor
=> 'Red', :width => 100})
#結果相同
例外 Exception
begin
.....
rescue StandardError => ex #ex為例外物件
p ex #秀出error的詳細
ensure
..... #無論如何都要執⾏行的內容
end
例外 Exception
• rescue後若有接例外類別名稱,代表
只處理該例外類別的例外,不指定則處
理一切例外

• 「$!」為最後發生例外物件,「$@」
為發生錯誤的行號等資訊
Module
• 只有方法部份的集合體

• 無實體

• 用途:	
  
– 類似單例(Singleton)類別	
  
– 命名空間(Name	
  Space)	
  
– Mix	
  in
Module for Singleton Class
module HtmlHelper
HTML_ESCAPE = { '&' => '&amp;', '>' =>
'&gt;', '<' => '&lt;', '"' => '&quot;' }
def self.h(s)
s.to_s.gsub(/[&"><]/){ |special|
HTML_ESCAPE[special] }
end
end
puts HtmlHelper.h('<img src="abc.gif"/>我是圖⽚片')
#&lt;img src=&quot;abc.gif&quot;/&gt;我是圖⽚片
Module for Namespace
module Forum
class Member
#類別全名為 Forum::Member
....
end
class Topic
#Forum::Topic
end
end
Module for Mix-in

多重繼承之實現
module ShareMod
def subject
...
end
end
class Forum
include ShareMod
end
class SubForum
include ShareMod
end
#Foum和SubForum都會有subject的instance method
動態型別 (duck typing)

不管黑貓白貓,會抓老鼠的都是好貓
class PersianCat
def find_mice
#抓⽼老⿏鼠
end
end
class RussianBlueCat
def find_mice
#抓⽼老⿏鼠
end
end
動態型別 (duck typing)

不論出身正確(參數型別),只論達成目標(方法)
def fetch_and_reverse(arr, idx)
return arr[idx].reverse if arr[idx] &&
arr[idx].respond_to?(:reverse)
end
aa = ['abc', 'RyudoAwaru', 'Linux']
ha = {0 => 'abc', 1 => 'Ubuntu'}
hb = {:first => 'RedHat', :second => 'Suse'}
p fetch_and_reverse(aa, 1) #"urawAoduyR"
p fetch_and_reverse(ha, 0) #"urawAoduyR"
p fetch_and_reverse(hb, :second) #"esuS"
Metaprogramming
用程式寫程式
define_method
class Movie
def initialize(id, name)
@id = id;@name = name
end
def movie_file(quality)
"/movies/#{quality}/#{@id}.mp4"
end
end
a = Movie.new(123,'阿凡達')
puts a.movie_file(:hd) #/movies/hd/123.mp4
puts a.movie_file(:sd) #/movies/sd/123.mp4
class Movie
QualityNames = [:fullhd, :hd, :sd]
#定義 fullhd_movie_file, hd_movie_file,
sd_movie_file
#三個⽅方法
QualityNames.each do |qt|
define_method "#{qt.to_s}_movie_file".to_sym
do
"/movies/#{qt.to_s}/#{@id}.mp4"
end
end
end
a = Movie.new(123,'阿凡達')
puts a.hd_movie_file #/movies/hd/123.mp4
puts a.sd_movie_file #/movies/sd/123.mp4
Domain-Specific Language

領域特定語言
Class MyApp < Sinatra::Base
get '/books/*.*' do
# matches /books/ruby-guide.html
end
get '/rooms/:id/index.html' do
# matches '/rooms/123/index.html
end
end
Method Missing
class Car
attr_accessor :wheels
def initialize
@wheels = []
4.times{@wheels << Wheel.new(30)}
end
def method_missing(mname, *args)
if mname.to_s =~ /wheel_(d)/
return @wheels[$1.to_i]
end
end
end
my_car = Car.new
p my_car.wheel_1 #<Wheel:0x8f6dea4 @radius=30>
物件方法查詢
object.methods #列舉該物件的所有⽅方法列表
object.private_methods #列舉該物件的所有⽅方法列表
object.protected_methods #列舉該物件的所有⽅方法
object.public_methods #列舉該物件的所有⽅方法列表
object.respond_to?(:method_name) #列舉是否⽀支援某⽅方
法
檔案與IO操作
Code Block in File operation
#⼀一般檔案操作寫法
fc = open('abc.html','w')
fc.puts('<html>')
.....
fc.puts('</html>')
fc.close #關閉檔案指標
檔案開啟模式
r 讀取
r+ 讀
w 寫⼊入,不存在時建⽴立新檔
w+ 讀取
a append
a+ append + read
Code Block in File operation
#使⽤用Code Block
open('abc.html','w') do |fc|
fc.puts('<html>')
.....
fc.puts('</html>')
end #無需呼叫close
讀取全部
data = open('abc.log').read
open-uri
把網路當檔案讀取
require 'open-uri'
puts open('http://www.google.com.tw').read
基本I/O操作
open('logs/access.log') do |io|
while line = io.gets
line.chomp!
...
end
end
#
open('logs/access.log') do |io|
io.each do |line|
line.chomp!
...
end
end
基本I/O操作
open('development.log') do |f|
f.each do |line|
puts f.lineno
end
end
基本I/O操作
open('development.log') do |f|
f.each_char do |char|
print char
end
end
練習
• 開啟⼀一個檔案(英⽂文),計算:
1. ⾏行數
2. 字元數
3. 單字數(提⽰示,⽤用 string.scan(/w+/))
CSV檔案讀取
require 'csv'
arr = []
CSV.parse(File.read('fakedata-dup.csv'),
headers: :first_row).each do |row|
arr << row.to_hash
end
練習—從CSV建⽴立物件
• 讀取CSV
• 把row轉成Hash
• 建⽴立Contact class
• 將row -> hash的資料再轉成Contact class的實體
• ⽤用p輸出object

實踐大學教案20140329