實踐大學教案20140329

121
RUBY程式語言 入門導覽 鄧慕凡 2014.03.29@實踐學

Upload: mu-fan-teng

Post on 19-May-2015

832 views

Category:

Documents


6 download

TRANSCRIPT

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"雙引號

– 可內插變數  – 可內插控制字元(\r\n\t等..)

字串可+不可-

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一樣,只是

條件相反

CASEaaa = [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 block10.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 Blockarr = [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_methodclass 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 Missingclass 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