一怀挠、心得體會
1、完成了什么懂诗?
- 看了20頁鎬頭書
- 看了10個controller
2、收獲了什么苗膝?
- sub與gsub的區(qū)別(都是查找能匹配第一個參數(shù)的那部分字符串殃恒,同時用第二個參數(shù)替換它們,sub執(zhí)行替換一次,gsub每次出現(xiàn)時都進行替換)
- 替換中的反斜線
- 面向?qū)ο蟮恼齽t表達式
- preload 允許預先加載參數(shù)离唐,與包含的相同
- 取消原因隆嗅、分類、城市侯繁、顏色
- 投訴胖喳、投訴原因
- 咨詢、顧客日志
- 訂單優(yōu)惠(coupe)
- 顧客(customer)
二贮竟、讀書筆記
7.2 命令展開(Command Expansion)
7.3 賦值 (Assignment)
到目前為止丽焊,本書給出的每個例子幾乎都有賦值語句的影子,或許到該說說它的時候了咕别。
賦值語句將左側(cè)的變量或者屬性(左值)設(shè)置為右側(cè)的值(右值)技健,然后返回該值作為賦值表達式的結(jié)果,這意味著你可以鏈接賦值語句惰拱,并可以在某些特殊的地方執(zhí)行賦值語句雌贱。
a = b = 1 + 2 + 3
a -> 6
b -> 6
a = (b = 1 + 2) + 3
a -> 6
b ->3
File.open(name = gets.chomp)
Ruby的賦值語句有兩種基本形式,第一種是將一個對象引用賦值給變量或者常量偿短。
這種形式的賦值在Ruby語言中是直接執(zhí)行的(hardwired)欣孤。
instrument = "piano"
MIDDLE_A = 440
第二種形式等號左邊是對象屬性或者元素的引用。
song.duration = 234
instrument["ano"] = "CCOLO"
這種形式的特殊之處在于昔逗,它是通過調(diào)用左值的方法來實現(xiàn)的降传,這意味著你可以重載它們。
我們已經(jīng)看了如何定義一個可寫的對象屬性:只要簡單地定義一個以等于號結(jié)尾的方法即可勾怒。這個方法以其右值作為它的參數(shù)婆排。
class song
def duration = (new_duration)
@duration = new_duration
end
end
這種設(shè)置屬性的方法不必和內(nèi)部的實例變量相對應,并且具有賦值方法的屬性也并非必須要有讀取該屬性的方法笔链。
class Amplifier
def volume = (new_volume)
self.left_channel = self.right_channel = new_volume
end
end
在老版本的Ruby中段只,賦值語句的返回值是設(shè)置該屬性的方法的返回值,在Ruby1.8中鉴扫,賦值語句的值總是參數(shù)的值而方法的返回值將被丟掉赞枕。
class Test
def val=(val)
@val = val
return 99
end
end
t = Test.new
a = t.val = 2
a -> 2
在老版本中,a將被賦值語句設(shè)置為99幔妨,而在Ruby1.8中鹦赎,它的值是2谍椅。
7.3.1 并行賦值(Parallel Assignment)
舉個栗子:
int a = 1;
int b =2;
int temp;
temp = a;
a = b;
b = temp;
在Ruby中你可以用下面的簡潔的代碼來實現(xiàn)误堡。
a, b = b, a
Ruby的賦值實際上是以并行的方式執(zhí)行的,所以賦值語句右邊的值不受賦值語句本身的影響雏吭,在左邊ed任意一個變量或者屬性被賦值之前锁施,右邊的值按它們出現(xiàn)的順序被計算出來。下面這個人為設(shè)計的例子說明了這一點。
第二行將表達式x悉抵,x+=1和x+=1的值分別賦值給變量a肩狂、b和c。
x = 0
a, b, c = x, (x +=1), (x +=1)
當賦值語句有多于一個左值時姥饰,賦值表達式將返回由右值組成的數(shù)組傻谁。如果賦值語句的左值多于右值,那么多余的左值將被忽略列粪,如果右值多于左值审磁,那么額外的右值將被忽略。如果賦值語句僅有一個左值而多個右值岂座,那么右值將被轉(zhuǎn)換成數(shù)組态蒂,然后賦值給左值。
使用Ruby的并行賦值操作费什,你可以疊起來和展開數(shù)組钾恢,如果最后一個左值有一個""前綴,那么所有多余的右值將被集合在一起鸳址,并作為一個數(shù)組賦值給左值瘩蚪。同樣的,如果最后一個右值是一個數(shù)組稿黍,你可以在它的前面加一個""募舟,它將被適當?shù)卣归_成其元素的值(如果右邊只有一個值,那么這就沒有必要了——數(shù)組會自動展開的)闻察。
嵌套賦值
并行賦值還有一個值得一提的特性:賦值語句的左邊可以含有一個由括號括起來的變量列表拱礁。Ruby視這些變量為嵌套賦值語句,在處理更高層級的賦值語句前辕漂,Ruby會提取出對應的右值呢灶,并賦值給括起來的變量。
7.3.2 賦值語句的其他形式(Other Forms of Assignment)
和許多其他語言一樣钉嘹,Ruby有一個句法的快捷方式:a = a + 2可以寫成a+=2鸯乃。
內(nèi)部處理時,會將第二種形式縣轉(zhuǎn)換成第一種形式跋涣,這意味著在你自己類中作為方法定義的操作符缨睡,和你預期的效果一樣的。
class Bowdlerize
def initialize(string)
@value = string.gsub(/[aeiou]/, '*')
end
def +(other)
Bowdlerize.new(self.to_s + other.to_s)
end
def to_s
@value
end
end
Ruby不支持C或Java中的自加(++)和自減(--)運算符陈辱,如果想用奖年,使用 +=和-=替代之。
7.4 條件執(zhí)行(Conditional Execution)
Ruby對條件代碼的執(zhí)行有幾種不同的機制:大多數(shù)的形式比較常見沛贪,并且對許多形式做了一些簡化陋守,不過我們在學習它們之前震贵,我們還是需要多花點時間來看看布爾表達式。
7.4.1 布爾表達式(Boolean Expression)
Ruby對“真值”(truth)的定義很簡單:任何不是nil或者常量false的值都為真水评,那你會發(fā)現(xiàn)Ruby庫程序常常利用這已事實猩系。例如IO#gets方法返回文件的下一行,如果遇到文件尾則返回nil中燥,利用它可以寫出如下循環(huán)寇甸。
while line = gets
#process line
end
然而,C疗涉,C++和Perl程序員可能會落入陷阱:數(shù)字0不被解釋為假值幽纷,長度為0的字符串也不是假值。這可是一個難以克服的頑固的習慣博敬。
Defined?友浸、與、或和非
Ruby支持所有標準的布爾操作符偏窝,并引入一個新操作符defined?收恢。
僅當and和&&的兩個操作數(shù)為真時結(jié)果才為真。并且僅當?shù)谝粋€操作數(shù)為真時才求解第二個操作數(shù)的值(這也稱為短路求解祭往,shortcircuit evaluation)伦意。這兩種形式的唯一區(qū)別在于優(yōu)先級不同(and低于&&)。
同樣硼补,如果有一個操作數(shù)為真驮肉,那么or和||的結(jié)果為真,且僅當?shù)谝粋€操作數(shù)為假時才求解第二個操作數(shù)已骇,和and一樣离钝,or和||的唯一區(qū)別是它們的優(yōu)先級。
需要注意的是and和or有相同的優(yōu)先級褪储,而&&的優(yōu)先級高于||卵渴。
not和!返回它們的操作數(shù)的相反值(如果操作數(shù)為真鲤竹,則返回假浪读;如果操作數(shù)為假,則返回真)辛藻。你可能已經(jīng)想到了碘橘,是的,not和吱肌!的唯一區(qū)別是優(yōu)先級不同痘拆。
如果參數(shù)未被定義,defined岩榆?操作符返回nil错负;否則返回對參數(shù)的一個描述坟瓢,如果參數(shù)是yield勇边,而且有一個block和當前上下文相關(guān)聯(lián)犹撒,那么defined?返回字符串"yield"粒褒。
defined?1 -> expression
defined?dummy -> nil
defined?printf -> "method"
defined?String -> "constant"
defined?$_ -> "global-variable"
defined?Math::PI -> "constant"
defined?a = 1 -> "assignment"
defined?42.abs -> "method"
除了布爾表達式识颊,Ruby對象還支持如下比較方法:==, ===,<=>,=~,eql?和equal?。除了<=>奕坟,其他方法都是在類object中定義的祥款,但是經(jīng)常被子類重載以提供適當?shù)恼Z義。例如月杉,類Array重定義了==:當兩個數(shù)組對象有相同的元素個數(shù)刃跛,且對應的元素也都相等時,才認為它們相等苛萎。
==和=都有相反的形式:!=和!桨昙。不過,在Ruby讀取程序的時候腌歉,會對它們進行轉(zhuǎn)換:a!=b等價于!(a=b); a !~b等價于!(a =b)蛙酪。這意味著如果你寫的類重載了==或者=,那么也會自動得到!=和!~翘盖。
你還可以用Ruby的range作為布爾表達式桂塞,像exp1..exp2這樣的range,在exp1變?yōu)檎嬷扳裳保闹禐榧俑笪#辉趀xp2變?yōu)檎嬷埃瑀ange被求解為真汰瘫。一旦欲芹,exp2變?yōu)檎妫瑀ange將重置吟吝,準備再次重新計算实辑。
Ruby1.8之前的版本中,可以用裸正則表達式(bare regular expression)作為布爾表達式地回,現(xiàn)在這種方式已經(jīng)過時了稍坯,不過你仍然可以用~操作符將$_和一個模式進行匹配。
7.4.2 邏輯表達式的值(The Value of Logical Expression)
在上面蛹磺,我們說“如果兩個操作數(shù)都為真粟瞬,and語句的值為真∮├Γ”但實際上比這更微妙裙品。操作符and俗批,or,&&和||實際上返回首個決定條件真?zhèn)蔚膮?shù)的值市怎。聽著很復雜岁忘,那到底是什么意思?
比方說区匠,表達式"vall and val2"干像,如果val1為假或者nil,我們知道這個表達式不可能為真驰弄。在這個例子中麻汰,vall的值決定了表達式的值,所以返回它的值戚篙,如果vall為其他值五鲫,那么表達式的值依賴于val2,所以返回它的值岔擂。
nil and true -> nil
false and true ->false
99 and false -> false
99 and nil -> nil
99 and "cat" -> "cat"
注意:不管怎樣位喂,表達式的最終值是正確的。
or的值的計算與此類似(除了當vall的值為真時智亮,才能提前知道or表達式的值)忆某。
false or nil -> nil
nil or false -> false
99 or false -> 99
Ruby的一個慣用技法利用了這一特性。
words[key] ||= []
word[key] << word
第一行等價于words[key] = words[key] || []阔蛉。如果散列表words中key對應的項沒有值(nil)弃舒,那么||的值是第二個操作數(shù):一個新的空數(shù)組。這樣状原,這兩行代碼將會把一個數(shù)組賦值給沒有值的散列表元素聋呢,對已經(jīng)有值的元素原封不動。有時颠区,你也會看到它們寫到一行的情況:
(word[key] ||=[]) << word
7.4.3 If和Unless表達式(If and Unless Expressions)
Ruby的if表達式和其他語言的“if”語句非常類似削锰。
if song.artist == "Gillespie" then
handle = "Dizzy"
elseif song.artist == "Parker" then
handle = "Bird"
else
handle = "unkown"
end
如果將if語句分布到多行上,那么可以不用then關(guān)鍵字毕莱。
if song.artist == "Gillespie"
handle = "Dizzy"
elseif song.artist == "Parker"
handle = "Bird"
else
handle = "unkown"
end
不過器贩,如果你想讓你的代碼更緊湊些,你可以使用then關(guān)鍵字來區(qū)分布爾表達式和它后面的語句朋截。
if song.artist == "Gilleapie" then handle = "Dizzy"
elseif song.artist == "Parker" then handle = "Bird"
else handle = "unkown"
end
使用冒號(:)來替代then可以使得代碼更簡潔蛹稍。
if song.artist == "Gillespie": handle = "Dizzy"
elseif song.artist == "Parker": handle = "Bird"
else handle = "unknown"
end
你可以用零個或者多個elseif從句和一個可選的else從句。
正如我們前面所說部服,if是表達式而不是語句——它返回一個值唆姐,你不必非要使用if表達式的值,但它遲早能派上用場廓八。
handle = if song/artist == "Gillespie" then "Dizzy"
elseif song.artist == "Parker" then
"Bird"
else
"unknown"
end
Ruby還有一個否定形式的if語句奉芦。
unless song.duration > 180
cost = 0.25
else
cost = 0.35
end
最后赵抢,給C的愛好者一個驚喜,Ruby也支持C風格的條件表達式声功。
cost = song.duration > 180 ? 0.35 : 0.25
條件表達式返回的是冒號前面表達式的值還是冒號后面表達式的值烦却,依賴于問好前面布爾表達式值的真?zhèn)巍T谶@個例子中减噪,如果歌曲的長度大于3分鐘短绸,則表達式返回0.35车吹。不管結(jié)果筹裕,是什么,它將被賦給cost變量窄驹。
if和unless修飾符
Ruby和Perl都有一個靈活的特性:語句修飾符允許將條件語句附加到常規(guī)語句的尾部朝卒。
mon, day, year = $1, $2, $3, if date =~ /(\d\d)-(\d\d)-(\d\d)/
puts "a = #{a}" if debug
print total unless total.zero?
對于if修飾符,僅當條件為真時才計算前面的表達式的值乐埠,unless與此相反抗斤。
File.foreach("/etc/fstab") do |line|
next if line =~ /^#/
parse(line) unless line =~ /^$/
end
因為if本身也是一個表達式,下面的用法會讓人感到晦澀難懂:
if artist == "John Co"
artist = "'tr"
end unless use_nickname == "no"
7.5 Case表達式 (Case Expression)
Ruby的case表達式非常強大:它相當于多路的if丈咐。而它有兩種形式瑞眼,使得它更加強大。
第一種形式接近于一組連續(xù)的if語句:它讓你列出一組條件棵逊,并執(zhí)行第一個為真的條件表達式所對應的語句伤疙。
leap = case
when year % 400 == 0: true
when year % 100 == 0: false
when year % 4 == 0
end
case語句的第二種形勢可能更常見。在case語句的頂部指定一個目標辆影,而每個when從句列出一個或者多個比較條件徒像。
case input_line
when "debug"
dump_debug_info
dumo_symbols
when /p\s+(\w)/
dump_variable($1)
when "quit", "exit"
exit
else
print "Illegal command: #{input_line}"
end
和if一樣,case返回執(zhí)行的最后一個表達式的值蛙讥;而且如果表達式和條件在同一行的話锯蛀,你可以用then關(guān)鍵字來加以區(qū)分。
kind = case year
when 1850..1889 then "Blues"
when 1890..1909 then "Ragtime"
when 1910..1929 then "New Orleans Jazz"
when 1930..1939 then "Swing"
when 1940..1950 then "Bebop"
else "Jazz"
end
和if語句一樣次慢,也可以使用冒號(:)來替代then關(guān)鍵字旁涤。
kind = case year
when 1850..1889 : "Blues"
when 1890..1909 : "Ragtime"
when 1910..1929 : "New Orleans Jazz"
when 1930..1939 : "Swing"
when 1940..1950 : "Bebop"
else "Jazz"
end
case通過比較目標(case關(guān)鍵字后面的表達式)和when關(guān)鍵字后面的比較表達式來運作。這個測試通過使用comparison === target來完成迫像。只要一個類為 ===(內(nèi)建的類都有)提供了有意義的語義劈愚。那么,該類的對象就可以在case表達式中使用侵蒙。
例如造虎,正則表達式將===定義為一個簡單的模式匹配。
case line
when /title=(.*)/
puts "Title is #$1"
when /track=(.*)/
puts "Track is #$1"
when /artist=(.*)/
puts "Artist is #$1"
end
Ruby的所有類都是類Class的實例纷闺,它定義了==以測試參數(shù)是否為該類或者其弗雷的一個實例算凿。所以份蝴,放棄了多態(tài)的好處,并把重構(gòu)的“福音”帶到你的耳側(cè)氓轰,你可以測試對象到底屬于哪個類婚夫。
case shape
when Square, Rectangle
# ..
when Circle
# ..
when Triangle
# ..
end
7.6 循環(huán)
不要告訴任何人,Ruby內(nèi)建的循環(huán)結(jié)構(gòu)相當原始署鸡。
只要條件為真案糙,while循環(huán)就會執(zhí)行循環(huán)體。比如靴庆,下面這個常用法讀取輸入直到輸入結(jié)束时捌。
while line = gets
# ...
end
until循環(huán)與此相反,它執(zhí)行循環(huán)體直到循環(huán)條件變?yōu)檎妗?/p>
until play_list.duration > 60
play_list.add(song_list.pop)
end
與if與unless一樣炉抒,你也可以用這兩種循環(huán)體做語句的修飾符奢讨。
a = 1
a *= 2 while a <100
a -= 10 until a < 100
a -> 98
我們之前說過range可以作為某種后空翻:當某個事件發(fā)生時變?yōu)檎妫⒃诘诙€事件發(fā)生之前一直保持為真焰薄。這常被用在循環(huán)中拿诸。在下面的例子中,我們讀一個含有前十個序數(shù)(first塞茅,second等等)的文本文件亩码,但只輸出匹配“third”和“fifth”之間的行。
file = File.open("ordinal")
while line = file.gets
puts(line) if line =~ /third/ .. line =~ /fifth/
end
輸出結(jié)果:
third
fourth
fifth
你可能會發(fā)現(xiàn)熟悉的Perl的人野瘦,它們的寫法和前面的代碼稍有不同描沟。
file = File.open("ordinal")
while file.gets
print if ~/third/ .. ~/fifth/
end
這段代碼背后有點小戲法:gets將讀取的最后一行賦值給全局變量$, ~操作符對$執(zhí)行正則表達式匹配,而不帶參數(shù)的print將輸出$_缅刽。在Ruby社區(qū)中啊掏,這種類型的額代碼已經(jīng)過時了。
用在布爾表達式中的range的起點和終點本身也可以是表達式衰猛,每次求解總體布爾表達式時就會求解起點和終點表達式的值迟蜜。
例如,下面的代碼利用了變量$.包含當前輸入行號這一事實啡省,來顯示1到3行以及位于/eig/和/nin/之間的行娜睛。
File.foreach("ordinal") do |line|
if (($. == 1) || line =~ /eig/) .. (($. == 3) || line =~ /nin/)
print line
end
end
當使用while和until做語句修飾符時,有一點要注意:如果被修飾的語句是一個begin/end塊卦睹,那么不管布爾表達式的值是什么畦戒,快內(nèi)的代碼至少會執(zhí)行一次。
print "Hello\n" while false
begin
print "Goodbye\n"
end while false
7.6.1 迭代器(lterators)
如果你讀了前面小節(jié)的開頭部分结序,當時可能會很失望障斋,因為我們在那兒提到:“Ruby內(nèi)建的循環(huán)結(jié)構(gòu)很原始”。
舉個栗子,Ruby沒有"for"循環(huán)——至少沒有類似與C垃环,C++和Java中的for循環(huán)邀层。但是Ruby使用各種內(nèi)建的類中定義的方法來提供類似健壯的功能。
讓我們看幾個例子遂庄。
3.times do
print "Ho!"
end
這種代碼易于避免長度錯誤和差異:這種循環(huán)會執(zhí)行3次寥院,除了函數(shù),通過調(diào)用downto和upto函數(shù)涛目,整數(shù)還可以在指定的range循環(huán)秸谢,而且數(shù)字都可以使用step來循環(huán)。例如霹肝,傳統(tǒng)的從0到9的”for“循環(huán)(類似i=0; i<10; i++)可以寫成:
0.upto(9) do |x|
print x, " "
end
從0到12估蹄,步長為3的循環(huán)可以寫成:
0.step(12, 3) {|x| print x, " "}
類似的,使用each方法使得遍歷數(shù)組和其他容器變得十分簡單阿迈。
[1, 1, 2, 3, 5].each {|value| print val, " "}
并且如果一個類支持each方法元媚,那么也會自動支持Enumerable模塊中的方法轧叽,例如苗沧,F(xiàn)ile類提供了each方法,它依次返回文件中的行炭晒,使用Enumerable中的grep方法待逞,我們可以只迭代那些滿足某些條件的行:
File.open("Ordinal").grep(/d$/) do |line|
puts line
end
最后也可能是最簡單的:Ruby提供了一個內(nèi)建的稱為loop的迭代器。
loop do
# block
end
loop循環(huán)一直執(zhí)行給定的塊(或者直到跳出循環(huán)网严,后面會降到如何做)识樱。
7.6.2 For ... In
前面我們說到Ruby內(nèi)建的循環(huán)原語只有while和until。那么for呢震束?是的怜庸,for基本上是一個語法塊,當我們寫:
for song in songlist
song.play
end
Ruby把它轉(zhuǎn)成:
songlist.each do |song|
song.play
end
for循環(huán)和each形式的唯一區(qū)別是循環(huán)體中局部變量的作用域垢村。
你可以使用for去迭代任意支持each方法的類割疾,例如Array或者Range。
for i in ['fee', 'fi', 'fo', 'fum']
print i, " "
end
for i in 1..3
print i, " "
end
for i in File.open("ordinal").find_all {|line| line =~ /d$/}
print i.chomp, " "
end
只要你的類支持each方法嘉栓,你就可以使用for循環(huán)去遍歷它的對象宏榕。
class Periods
def each
yield "Classical"
yield "Jazz"
yield "Rock"
end
end
periods = Periods.new
for genre in periods
print genre, ""
end