實踐大學教案20140329
TRANSCRIPT
irb: Interactive Ruby 馬上動手練習
開啟終端機: ryudo@ryudo-x61:~$ irb irb(main):001:0> 1+3 => 4 irb(main):002:0> 'hello' + ' world' => "hello world"
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
浮點數四則運算(結果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
字串可+不可-
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 #左右去空⽩白
格式化字串
i1 = 123 f1 = 456.789 printf('我有 %d 元', i1) #我有 123 元 printf('%0.5d', i1) #00123 printf('%3.5f', f1) #456.78900
練習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
型別轉換 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]
更多陣列函式(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
字串符號 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
if
puts "請輸⼊入你的⽉月薪" your_money = gets.to_i if your_money >= 90000 puts "百萬年薪" elsif your_money >= 22000 puts "普通上班族" else puts '總統德政' end
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"
練習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]
類別方法(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
定義函式
• 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 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
建立物件變數的存取函式
• 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
繼承類別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
走訪迴圈
langs = ['VB', 'C#', 'C', 'JavaScript'] langs.each do |lang| puts "我會 #{lang}" end 輸出: #我會 VB #我會 C# #我會 C #我會 JavaScript
迭代器 iterator
• 不同於for或while,迭代器是一種走
訪接收者(通常是陣列或陣列的子類)元素的「方法」
• do..end是迭代器方法的參數,可以
將其代換為{},此類參數通稱為
「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
Enumerator
arr1 = ['abc','whoswho.openfoundry.org','OSSF'] arr1.sort_by{|w| w.size} #⽤用字數排序 arr1.sort{|a,b| a.size <=> b.size } #和上⾯面相同
練習
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
Module for Singleton Class
module HtmlHelper HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' } def self.h(s) s.to_s.gsub(/[&"><]/){ |special| HTML_ESCAPE[special] } end end puts HtmlHelper.h('<img src="abc.gif"/>我是圖⽚片') #<img src="abc.gif"/>我是圖⽚片
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"
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) #列舉是否⽀支援某⽅方法
Code Block in File operation
#⼀一般檔案操作寫法 fc = open('abc.html','w') fc.puts('<html>') ..... fc.puts('</html>') fc.close #關閉檔案指標
Code Block in File operation
#使⽤用Code Block open('abc.html','w') do |fc| fc.puts('<html>') ..... fc.puts('</html>') end #無需呼叫close
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
CSV檔案讀取
require 'csv' arr = [] CSV.parse(File.read('fakedata-dup.csv'), headers: :first_row).each do |row| arr << row.to_hash end