Day8 讀書筆記&心得體會

一、心得體會

今天完成了什么

  • 看了20頁的鎬頭書
  • 學了bag的10個controller

收獲什么?

  • 新增、編輯都放在show視圖里
  • 活動平臺
  • 訂單活動
  • 地址、地址快照、品牌

二、讀書筆記
回顧昨天學到內容

  • 肯定匹配 =~
    舉個栗子:
name = "ni shi da sha gua"
name =~ /a/   -> 8

匹配操作符返回匹配發(fā)生的字符位置,它們也有副作用,會設置一些Ruby變量???

$`得到匹配之前的那部分字符串,$'得到匹配之后的那部分字符串。

所以,可以這么寫show_regexp方法,以說明具體的模式在何處發(fā)生匹配。

舉個栗子:

def show_regexp(a, re)
  if a =~ re
    "#{$` } <<#{$&}>>#{$' }"
  else
    "no match"
  end
end
show_regexp('very interesting', /t/) -> very in <<t>>eresting

這個匹配也設置了線程局部變量(thread-local variables)???,$~與$1直到$9。

$~變量是MatchData對象,它持有你想知道的有關匹配的所有信息。???

$1等持有匹配各個部分的值,我們在后面會談到這些。對那些看到類似Perl變量名就心懷畏懼的人來講,請稍安勿躁得啦!

關于模式

每個正則表達式包含一種模式,用來對字符串進行正則表達式的匹配。

在模式內,除了.,|,(,),[,],{,},+,\,^,$,*和?字符之外,所有字符串匹配它們本身。

注意:在這些特殊字符之前放置一個反斜線便可以匹配它們的字面量。舉個栗子:

show_regexp("yes | no", /\|/) -> yes <<|>> no

如果不加反斜線的話:

show_regexp("yes | no", /|/) -> <<>>yes | no

返回的竟然是:

<<>>yes | no

為什么會這樣呢?
這是因為|是特別字符,未轉義的豎線要么匹配在它之前的正則表達式,要么匹配在它之后的正則表達式。
這里的豎線(|)前后都是空,所以匹配的也是空,匹配的順序是從左到右,所以就是上面那個樣子。

但是,有些同學會疑惑,那到底是先匹配模式中豎線(|)的左邊還是豎線的右邊呢?讓我們來做個對照試驗。
舉個栗子:

show_regexp(" red ball blue sky", /red|/) -> <<red>> ball blue sky
show_regexp("red ball blue sky", /|red/) -> <<>>red ball blue sky

red放在左邊的時候,能匹配出字符串中的red
而red放在右邊的時候,則不能匹配出字符串的red,反而匹配的是空。
這說明,前者在匹配的時候,是從模式中豎線的左邊和字符串中的最左邊的空開始匹配。

show_regexp("red ball blue sky", /red|blue/)  -> <<red>> ball blue sky
show_regexp("red ball blue sky", /blue|red/)  -> <<red>> ball blue sky

show_regexp("blue ball red sky", /red|blue/)  -> <<blue>> ball red sky
show_regexp("blue ball red sky", /blue|red/)  -> <<blue>> ball red sky

從上面的例子中,我們可以發(fā)現(xiàn),無論匹配模式里的red和blue放在哪邊,結果都是一樣的,所以。

但是,如果把匹配字符串里的blue和red交換位置,我們會發(fā)現(xiàn),匹配出來的不是red,竟然是blue。

所以,無論是先匹配模式中豎線的左邊還是右邊,都不會影響結果。

5.4.2基于模式的替換(Pattern-Based Substitution)

在字符串中能夠發(fā)現(xiàn)模式有時就已經(jīng)足夠好了,如果你的朋友給出難題,讓你找出順序包含字母a,b,c,d和e的詞,你可能會使用模式/a.b.cd.e/去查找詞表并找出abjectedness,absconded,ambuscade和carbacidometer這些詞等等,這種事情肯定有價值的。

當然,有時候待根據(jù)模式匹配去改變一些東西,讓我們回到歌曲列表文件中,不管是誰,都會以小寫方式輸入歌曲演唱者的名字,以混合大小寫方式在點唱機的屏幕上顯示它們會更好看,怎么樣才能把每個詞的首字符改成大寫呢?

String#sub和String#gsub方法超找出能夠匹配第一個參數(shù)的那部分字符串,同時用第二個參數(shù)替換這它們。String#sub執(zhí)行一次替換,而String#gsub在匹配每次出現(xiàn)都進行替換。

兩個方法都返回了對含有這些替換字符串的新的拷貝。Mutator版本的String#sub!和String#gsub!會直接修改原先的字符串。

a = "the quick brown fox"
a.sub(/[aeiou]/, '*')
a.gsub(/[aeiou]/, '*')
a.sub(/\s\S+/, '')
a.gsub(/\s\S+/, '')

兩個函數(shù)的第二個參數(shù)可以是String或block。如果使用block,匹配的字符串會被傳遞被block,同時block的結果值會被替換到原先的字符串中。

a = "the quick brown fox"
a.sub(/^./){|match| match.upcase}
a.gsub(/[aeiou]/) {|vowel| vowel.upcase}

所以,對如何轉換歌曲演唱者名字的這個問題,我們給出了大難。匹配詞的首字符的模式是\b\w——它尋找后面跟著詞字符(word character)的詞邊界。把這個模式和gsub結合起來,可以改變歌曲演唱者的名字了。

def mixed_case(name)
  name.gsub(/\b\w/) {|first| first.upcase}
end
mixed_case("fats waller")
mixed_case("louis armstrong")
mixed_case("strength in numbers")

替換中的反斜線序列

早些時候我們注意到序列\(zhòng)1和\2等在模式中可用,它們代表至今為止第n個已匹配的組,相同的序列也可以作為sub和gsub的第二個參數(shù)。

"fred:smith".sub(/(\w+):(\w+)/, '\2, \1')
"nercpyitno".gsub(/(.)(.)/, '\2\1')

其他的反斜線序列在替換字符串中起的作用:&(最后的匹配),+(最后匹配的組),'(匹配之前的字符串),'(匹配之后的字符串)和\(字面量反斜線)。

如果想在替換中包含字面量反斜線,我們會變得很困惑,顯然可以寫成:

str.gsub(/\\/, '\\\\')

很清楚,代碼正試圖用兩個反斜線替換str中的每個反斜線,程序員在替換文本中用了雙倍的反斜線,因為知道它們在語法分析階段中會被轉化成\。但是當替換發(fā)生時,正則表達式引擎對字符串又執(zhí)行了一遍,并把\轉換成\,所以最終結果是使用另外一個反斜線替換每個反斜線。需要把它寫成gsub(/\/, '\\\\')!
str = 'a\b\c'
str.gsub(/\/, '\\\\')
但是,如果利用&被替換為已匹配的字符串這個事實,也可以寫成:
str = 'a\b\c'
str.gsub(/\/, '&&')

如果使用gsub的block形式,用來替換的字符串會被和僅被分析一次(在語法分析階段),這正是所希望看到的結果。

str = 'a\b\c'
str.gsub(/\/) { '\\' }
最后,下面的例子極好的表達了結合使用正則表達式和block,請參見由Wakou A編寫的CGI庫模塊的代碼片段,這段代碼接受包含HTML轉義序列的字符串并把它轉換成普通的ASCII。

為了支持日本用戶,它在正則表達式上使用了n修飾符關閉了寬字符處理,它也說明了Ruby的case表達式。

def unescapeHTML(string) 
  str = string.dup
  str.gsub!(/&(.*?); /n) {
    match = $1.dup
    case match
    when /\Aamp\z/ni  then '&'
    when /\Aquot\z/ni then  '*'
    when /\Agt\z/ni     then '>'
    when /\Alt\z/ni       then '<'
    when /\A#(\d+)\z\/n then Integer($1).chr
    when /\A#x([0-9a-f]+)\z/ni  then $1.hex.chr
    end
  }
  str
end

puts unescapeHTML("1<2 & 4>3")
puts unescapeHTML(""A" = A = A")

輸出結果:
1<2 && 4>3
"A" = A = A

dup:復制對象沒有分配ID,并被視為新記錄。請注意,這是一個“淺”副本,因為它僅復制對象的屬性,而不是其關聯(lián)。 “深”副本的范圍是特定于應用程序的,因此由應用程序根據(jù)需要進行實施。 dup方法不保留時間戳(已創(chuàng)建|更新)_(at | on)。

5.4.3面向對象的正則表達式(Object-Oritended Regular Expressions)

必須承認,盡管所有這些古怪的變量用起來十分方便,但是它們不是面向對象的,同時它們很神秘,難道我們沒有說Ruby的所有物體都是對象嗎?哪里出錯了?

真的沒有錯,在Matz設計Ruby時,他設計了一個完全面向對象的正則表達處理系統(tǒng),然后在它之上包裝(wrap)所有這些$變量,使它們看起來對Perl程序員很熟悉。對象和類還在這里,只不過就在表面下,讓我們花點時間把它們挖掘出來吧。

實際上已經(jīng)遇到一個類:正則表達式字面量創(chuàng)建了Regexp類的實例。

re = /cat/
re.class -> Regexp

Regexp#match方法對字符串匹配正則表達式。如果失敗了,它返回nil。如果成功,則返回MatchData類的一個實例。MatchData對象讓你訪問關于這次匹配的所有可用信息,所有這些好東西通過$變量得到的,它們綁定在一個小巧方便的對象。

re = /(\d+):(\d+)/ # match a time hh:mm
md = re.match("Time: 12:34am")
md.class 
md[0] # == $&
md[1] # == $1
md[2] # == $2
md.pre_match # == $`
md.post_match # == $'

匹配數(shù)據(jù)存儲在它自己的對象里,這樣可以同時保留兩個或多個模式匹配的結果,這些事情使用那些$變量是做不出來的。在下面的例子中,對兩個字符串匹配相同的Regexp對象,每次匹配返回唯一的MatchData對象,通過檢驗兩個子模式字段來驗證它。

re = /(\d+):(\d+)/
md1 = re.match("Time: 12:34am")
md2 = re.match("Time: 10:30pm")
md1[1, 2]

第6章

關于方法的更多細節(jié)(more about Methods)

6.1 定義一個方法

  • 以小寫字母開頭

如果你使用大寫字符,并不會立即得到一個錯誤,但是當Ruby看到你調用這個方法時,它首先猜測這是一個常量,而不是一個方法調用,且結果是Ruby可能錯誤地解析這個調用。

  • 表示查詢的方法名通常以?結尾,例如instance_of?
  • “危險的”或者會修改接收對象(receiver)的方法,可以用!結尾,例如String提供了chop和chop!方法,第一個返回一個修改后的字符串;第二個則就地修改對象。

舉個栗子:

我們已經(jīng)新方法指定了一個名字,可能還需要聲明某些參數(shù)(parameter,形參),它們就是括號中列出的局部變量。(方法參數(shù)兩邊的括號是可選的;我們的慣例是,當方法有參數(shù)時則使用括號,否則忽略它們。)

def my_new_method(arg1, arg2, arg3)
  #Code for the method would go there
end

Ruby可以讓你指定方法參數(shù)(argument,實參)的默認值——如果調用者在傳入?yún)?shù)時沒有明確指定所使用的的值。為此你可以使用賦值操作符。

def cool_dude(arg1="Miles", arg2="Coltrane", arg3="Roach")
    "#{arg1}, #{arg2}, #{arg3}."
end

方法體內是普通的Ruby表達式,你不能在方法內定義非單件(nonsingleto)類或模塊。如果你在一個方法內定義另一個方法,內部的方法只有在外部執(zhí)行時才得到定義,方法的返回值是執(zhí)行的最后一個表達式的值,或者return表達式顯示返回的值。

6.1.1 可變長度的參數(shù)列表(Variable-Length Argument Lists)

但是如果你希望傳入可變個數(shù)的參數(shù)(argument)、或者想用一個形參(parameter)接收多個參數(shù),在“普通”的參數(shù)名前放置一個星號(*)即可。

def varargs(arg1, *rest)
  "Got #{arg1} and #{rest.join(', ')}"
end
varargs("one")
varargs("one", "two")

在這個栗子中,和往常一樣第一個參數(shù)賦值給方法的第一個形參,不過,第二個形參的前綴為星號,因此所有剩余的參數(shù)被裝入到一個新的Array中,然后賦值給第二個形參。

6.1.2 方法和Block(methods and Blocks)

之前講過“Block和迭代器“,調用一個方法是,可以用一個block與之相關聯(lián),通常,您可以使用yield從方法內部調用這個block。

def take_block(p1)
  if block_given?
    yield(p1)
  else
    p1
  end
end

take_block("no block")
take_block("no block") {|s| s.sub(/no /, '')}

不過如果方法定義的最后一個參數(shù)前綴為&,那么所關聯(lián)的block會被轉換為一個Proc對象,然后賦值給這個參數(shù)。

class TaxCalculator
  def initialize(name, &block)
    @name, @block = name, block
  end
  def get_tax(amount)
    "#@name on #{amount} = #{ @block.call(amount) }"
  end
end
tc = TaxCalculator.new("Sales tax") { |amt| amt * 0.075 } 
tc.get_tax(100)

調用方法(Calling a Method)

你可以通過指定接受者、方法的名稱、可選的參數(shù)及block,來調用一個方法。

connection.download_MP3("jitterbug") {|p| show_progress(p)}

在這個栗子中,connection對象是接收者,download_MP3是方法的名稱,”jitterbug“是參數(shù),花括號中的內容是相關聯(lián)的block。

對類方法或者模塊來說,接收者是類或模塊的名字。

File.size("testfile")
Math.sin(Math::PI/4)

如果你省略了接收者,其默認為self,也就是當前的對象。

self.class
self.frozen

Ruby正是用這種默認的機制實現(xiàn)私有方法調用的,我們無法調用某個接受對象的私有方法,它們只是在當前對象(self)中是可用的。

而在前面的栗子中,我們調用self.class時必須要指定一個接收對象,這是因為class在Ruby中是一個關鍵字(它引入類的定義),因此單獨使用它會產(chǎn)生語法錯誤。

可選的參數(shù)跟隨在方法名之后,如果沒有二義性,在調用方法時,你可以省略參數(shù)列表兩側的括號,不過,除非最簡單的情況,我們不推薦這樣做——某些微妙的問題可能會讓你犯錯誤,我們的規(guī)則很簡單:只要你有任何疑慮,就使用括號。

a = obj.hash
a = obj.hash()
obj.some_method

6.2.1 方法返回值(Method Return Values)
每個被調用的方法都會返回一個值(盡管沒有規(guī)定說你必須要使用這個值)。方法的值,是在方法執(zhí)行中最后一個語句執(zhí)行的結果,Ruby有一個return語句,可以從當前的執(zhí)行的方法中退出,return的返回值是其參數(shù)的值,如果不需要return就省略之,這是Ruby的一個慣用技法。

def meth_one
  "one"
end
meth_one
def meth_three
  100.times do |num|
    square = num*num
    return num, square if square >1000
  end
end

最后一種情況演示了,如果你給定return多個參數(shù),方法會將它們以數(shù)組的形式返回。你可以使用并行賦值來收集返回值。

num, square = meth_three

在方法調用中的數(shù)組展開

我們最早看到,在方法定義中,如果你在一個正規(guī)參數(shù)前加一個星號,那么傳入這個方法調用的多個參數(shù)將會被裝入一個數(shù)組中,當然,也有某些操作是相反的方式。

當你調用一個方法時,你可以分解一個數(shù)組,這樣每個成員都視為單獨的參數(shù)。在數(shù)組參數(shù)(必須在所有普通參數(shù)的后面)前加一個星號完成這一點。

def five(a, b, c, d, e)
  "I was passed #{a} # #{c} #u0z1t8os #{e}"
end

five(1, 2, 3, 4, 5)

讓block更加動態(tài)

我們已經(jīng)看到如何為方法調用關聯(lián)一個block。

list_bones("aardvark") do |bone|

...

end

一般來說,這已經(jīng)足夠好了——你可以將一個固定的block關聯(lián)到方法,就如同在if或while語句之后編寫的一大塊代碼。

但是,某些時候,你希望更加靈活一些,例如,我們希望教授一些算術技法,學生可能想要一個n次相加或相乘的表,如果學生需要一個2倍次的表,我們就輸出2,4,6,8等。

print "(t)imes or (p)lus:"
times = gets
print "number: "
number = Integer(gets)
if times =~ /^t/
  puts ((1..10).collect {|n| n*number}.join(",  "))
else
  puts ((1..10).collect {|n| n+number}.join(",  "))
end

這可以工作,不過在每個if語句之后重復實質上等價的代碼,頗為丑陋,如果我們可以將完成計算的block抽取出來,代碼就漂亮富多了。

print "(t)imes or (p)lus:"
times = gets
print "number: "
number = Integer(gets)
if times =~ /^t/
  puts (lambda{|n| n*number})
else
  puts (lambda {|n| n+number})
end

如果方法的最后一個參數(shù)前有&符號,Ruby將認為它是一個Proc對象,它會將其從參數(shù)列表中刪除,并將Proc對象轉換為一個block,然后關聯(lián)到該方法。

收集散列參數(shù)

某些語言支持關鍵字參數(shù)——也就是你可以任意順序傳入?yún)?shù)的名稱與值,而非按指定順序傳入?yún)?shù)。

因為我們在本書上一版中提到Ruby1.8將會支持這一特性,同時,人們可以使用散列表來取得相同的效果,例如,可以考慮為我們的SongList加入更強大的按名搜索功能。

class SongList
  def create_search(name, params)
    # ...
  end
end

list.create_search("short jazz songs", {
  'genre' => "jazz",
  'duration_less_than' => 270
})

第一個參數(shù)是搜索的名字,第二個是一個散列式,其中包括搜索的參數(shù)。使用散列表意味著我們可以模擬關鍵字:查詢類型為”爵士樂“, 且時長小于4分半的歌曲。不過這種方式有點笨重,并且一組大括號容易誤寫為一個和方法關聯(lián)的block。

因此,Ruby提供了一種快捷方式,只要在參數(shù)列表中,散列數(shù)組在正常的參數(shù)之后,并位于任何數(shù)組或block參數(shù)之前,你就可以使用鍵值對,所有的這些對會被集合到一個散列數(shù)組中,并作為方法的一個參數(shù)傳入。無須使用大括號。

list.create_search('short jazz songs',
  'genre'=>"jazz",
  'duration'=>270
)

最后,Ruby的慣用技法是,你可以使用符號(Symbol)而非字符串作為參數(shù),符號清楚地表達了你所引用的是某個事物的名字。

list.create_search('short jazz songs',
  :genre => :jazz,
  :duration = 270
)

一個潛心編寫的Ruby程序通常包括許多方法,每個都很短小,因此熟悉定義和使用Ruby方法的各種選擇是非常值得的。

第七章 表達式(Expression)

Ruby和其他語言的一個不同之處就是任何東西都可能返回一個值:幾乎所有東西都是表達式。

明顯的一個好處就是實現(xiàn)鏈式語句。

a = b = c = 0
[3, 1, 7, 0].sort.reverse

不太明顯的好處是,C和Java中的普通語句在Ruby中也是表達式,例如,if和case語句都返回最后執(zhí)行的表達式的值。

song_type = if song.mp3_type == MP3::Jazz
  if song.written < Date.new(1935, 1, 1)
    Song::TradJazz
  else
    Song::Jazz
  end
  else  
    Song::other
  end

rating = case votes_cast
  when 0...10 then Rating::SkipThisOne
  when 10...50 then Rating::CouldDoBetter
else Rating::Rave
end

運算符表達式(Operator Expression)

Ruby提供了基本的運算符集(如+、-、*、/等等),也提供了幾個獨特的運算符。

實際上,Ruby中的許多運算符是由方法調用來實現(xiàn)的,例如,當你執(zhí)行 ab+c時,實際上你是請求a對象執(zhí)行方法,傳入的參數(shù)是b。然后請求返回的結果對象執(zhí)行+方法,傳入的參數(shù)是c。這等價于:

(a.*(b).+c)

因為任何東西都是對象,而且你可以重新定義實例方法,所以你可以重新定義任何不滿足你需求的基本算術方法。

class Fixnum
  alias old_plus + # we can reference the original '+' as 'old_plus'
  # Redefine addition of Fixnums. This
  # is a BAD IDEA
  def +(other)
    old_plus(other).succ
  end
end

更有用的是,你寫的類可以像內建那樣參與到運算符表達式中,比如,你可能香蔥歌曲中間剪輯一段,這可以用索引操作來實現(xiàn)。

class Song
  def [](from_time, to_time)
    result = Song.new(self.title + " [extract]"),
    self.artist
    to_time - from_time
    result.set_start_time(from_time)
    result
  end
end

這段代碼擴展了類Song:提供了[]方法,該方法有兩個參數(shù)(開始時間和結束時間)。它返回對應給定時間間隔的一個新的song對象。我們可以通過下面的代碼來試聽一首歌:

  song[0, 15].play

7.2 表達式之雜項
除了支持明顯的運算符表達式和方法調用,以及不太明顯的語句表達(例如if和case)外,Ruby的表達式還支持更多的東西。

7.2.1命令展開(Command Expansion)

如果你用反索引號(`), 或者以%x為前綴的分界形式,括起一個字符串,默認情況下它會被當做底層操作系統(tǒng)的命令來執(zhí)行。表達式的返回值就是該命令的標準輸出,由于沒有去處新行符,所以你獲得的返回值結尾可能會有回車符或者換行符。

`date`
`ls`.split[34]
%x{echo "Hello there"}

你也可以在命令字符串中使用表達式展開和所有普通的轉義序列。

for i in 0..3
  status = 'dbmanager status id=#{1}'
  # ...
end

命令的退出狀態(tài)(exit status)保存在全局變量$?中。

重定義反引號

在前面的描述中,我們說反引號括起來的字符串”默認“被當做命令執(zhí)行。實際上,字符串被傳遞給了名為Kernel.方法(單引號)。如果你愿意,可以重載它。

alias old_backquote
  def `(cmd)
    result = old_backquote(cmd)
    if $? !=0
      fail "Command #{cmd} failed: #$?"
    end
    result
  end
print `data`
print `data`
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 一、讀書筆記回顧昨天的收獲:什么是block、proc? block和proc是兩種不同的東西, block有形無...
    柳輝閱讀 437評論 0 0
  • 一、心得體會1、今天完成了什么? 看了Ruby核心、Duck typing、類與對象、RUby安全 看10個con...
    柳輝閱讀 276評論 0 0
  • 一、心得體會1、今天完成了什么? 看了6個小時的鎬頭書18章、21章、22章,我想快速把書看一遍,然后看看哪些是需...
    柳輝閱讀 478評論 0 0
  • 一、讀書筆記3.4 剩余部分當對象需要訪問同類的其他對象的內部狀態(tài)時,使用保護訪問(protected acces...
    柳輝閱讀 444評論 0 0
  • 酒店 都說巴厘島是一流的酒店,二流的沙灘。此行的酒店我們的領隊牛小晟是花了大功夫做的功課,我們一起住了四間酒店 檔...
    浮云小姐是造夢者閱讀 188評論 0 0

友情鏈接更多精彩內容