Blankego's Humble Home
Tags: tutorial

零起點程序設計

挖個深坑,不定期更新,填一點算一點,大夥誰要是有興趣且估摸着自個兒IQ夠用的就跟着我學。「知之者不如好之者,好之者不如樂之者」--本教程的目標就是要把有志於降服電腦使之成為任我差遣的忠僕的朋友帶入Hacker的樂園。沒有彎彎繞,沒有廢話。跟得上的說明你有悟性,那就請堅持到底,爭取早日由 user 進身為 power user 甚而 hacker;若跟不上的,也不要勉強,電腦只是奴才,人纔是主宰,奴才不聽使喚那是他的罪愆,誰也不能怪到主子的頭上。

閑言少敍,這就開講:

第一課: 從數說起

講之前咱們先得制備教具。該用甚麼作為入門的語言呢? C 嗎? BASIC? Java? 說實在的,這些都不適合初學。當然,我們後面會介紹一些 C 和 JAVA 的常識,但剛起步的時候不用它們。入門最好是用立竿見影的傢什。既然假設讀者是零起點的,我就替大夥拿主意,選用 ruby -- 一種靈活、實用的動態語言(甚麼叫動態 dynamic,先按下不表),關鍵是看中它的中規中矩,在編程語言的世界裡跟 ruby 同等地位的還有一種叫 python 的,google 自己用得很多,比ruby更為流行,然而它的語法有點奇異,愚見不太適合初學,且跟後面要講到的 c java 也不大容易銜接。(哈哈,這些都是藉口,我講,當然由着我的喜惡來!)

安裝RUBY

假設你用的是 windows xp (用linux和OS 的都懂怎麼安裝,不用我囉唆),這是下載地址。點擊自動安裝,過程中勾選下圖所示的選項。

安裝成功後,按住 win 鍵(空格之右)的同時點 R鍵,屏幕左下角會彈出「運行程序」小窗口,在輸入欄內鍵入「cmd」,回車。此時一個黑黢黢的窗口會彈出來。在閃爍游標的提示處輸入 irb [回車],ruby 的交互介面就出來了,詳下圖。

IRB

圖中已然給出了一個算式:

irb(main):001:0> 37 % 3

>」之前的是 ruby repl 的輸入提示符,main001:0的含義我們不必去理它(用一用你自然就明白了),為避免囉唆,下文中我就只寫一個 「>」 表示每一行的提示。

下一行就是前面式子運算的結果

=> 1

%」是「取餘數」的算符,37 除以 3 等於 12 餘 1,用 % 號就能直接得出餘數 1。

取餘數幹嘛呢?餘數其實是很有用的,比如我們想在網頁上做一個table,讓它的奇數行顯示為白地,偶數行顯示為淡藍地,奇偶如何確定呢?

1 % 2 = 1
2 % 2 = 0
3 % 2 = 1
#……

能被 2 整除的不就是偶數嗎。

「有是哉,子之迂也!奚其正?」

呵呵,嫌餘數迂遠,我們來點切實的。比如求平面上兩點間的距離,我們知道公式是√(x1-x2)2 + (y1-y2)2,假設A點在原點上,B點的座標是 (3,4),那麼折合成ruby的算式就是

Math.sqrt((0-3)**2 + (0-4)**2)
=> 5.0

Math是數學模塊, sqrt 是該模塊中的平方根(square root)函數,** 是乘方算符(你可以試一下 2**10 看看得多少)。為甚麼要把X差之方與Y差之方的和括起來呢? Algol 一系的語言,包括c/c++/java和ruby一般都將求函數值的表達式寫成 f(x) 的形式,把自變量圈在括號中。

甚麼是函數?

計算機語言的函數跟數學上的函數沒有本質區別,無非就是定義域、陪域、映射。只是定義域和陪域不限於數的集合,可以是一切計算機能處理的東西,即所謂的數據(data)。說到這兒就算扣題了。甚麼是數?

在計算機中,數就是兩態開關的序列,也就是所謂的二進制數值。兩態開關跟邏輯值的真偽相對應(true = 1, false = 0),而計算機的算術運算實質上都是通過邏輯運算來實現的,所以二進制對計算機而言是一種必然的選擇。

一個算式由算符(operator)和算項(operand)組成,一符一項的叫單目運算,一符兩項的叫雙目運算。如 -3 是對 3 取負值的運算,|-3| 是求 -3 的絕對值, √3 是求3的平方根,這些都是一個算符一個算項(||只對夾在中間的數起作用,所以合起來看成一個算符);而 1 + 3, 1 - 3, 1 * 3 , 1 / 3 之類都是雙目運算。複雜的算式都是由單、雙目的簡單算式推演出來的。所謂推演,就是遞歸(recursion)定義。如前面求平面上兩點間距離的式子,我們看到最外一層是Math.sqrt(□) -- 求方根,括號裡的那一串,可以合起來看成一個算項,把這個算項拆解開,先是雙目的加法 ★ + ☆ , 拆開左邊的 ,是單目的求平方 ○ ^ 2 ,再把底數 ○ 拆開,(x1-x2)是雙目減法。

那麼,邏輯算式,我們也就只講最簡單的單目、雙目式,其他的都是這兩類式子的推演(合格的推演,我們就稱其為WFF= well formed formula)。

因為邏輯值只有true,false兩種(下文中會徑行以 1,0 或 首字母 T F 代替true,false,讀者只須牢記對應關係,不妨礙理解、領會),設定義域為A:{true,false},值域為B:{true,false},函數為f:A→B,那麼A、B間映射只有兩種情況,要麼true 對 true 、false 對false,不變,要麼true 對false ,false 對true ,取反。

插叙:為甚麼我一忽兒說算符,一忽兒說函數,怎麼這麼亂呢? 其實一點都不亂。說白了一切算符都是函數。比如前面舉例中的求負值、求絕對值,你把 - 和 |○|看成算符也好,函數也好,實質都是一樣的。可是你要問了,x + y這樣的雙目運算呢,怎麼能有兩個自變量呢?你可以把它看成函數的函數,比如把 3 + y 看成(3+)(y),任何實數+3都能得出一個固定的值,那麼 3 + y就無疑是一個函數。而 x + y 是 A → B → C 這樣一種結構,A是定義域,(B → C)的一群函數是它的值域。A → B → C 就等同於 A → (B → C),即 +:A → (B → C). 將3代入x就得出函數(3+)(y),若將 42代入,就得出函數(42+)(y)。所以所謂的多自變量的函數,從數學角度去看,都是返回(輸出)函數的函數。然而現實中,c/java/ruby之類的編程語言都不是純粹的函數式語言(純粹的函數式語言也有,最出名的一種叫作Haskell),因而在編程的語境中,我們就拓寬定義,把含有兩個或多個自變量的結構也看成是函數,在跟它們打交道時也不要總是去想它們是返回函數的函數。(事實上,除了Haskell,在其他語言中,它們的運作機理也不能與數學上的定義完全吻合。) 不管怎麼說,加減乘除等運算符號都可與函數等量齊觀,這跟數學抑或計算機科學的語境無觀。另外,在編程語境中,我們也不說自變量、因變量,而說參數和返回值(return value),函數的變量參數稱作形參(parameters),調用時實際代入的值則稱作實參(arguments)。

回過頭來說邏輯運算。

前面說到從{T,F}到{T,F}只有兩種對應,一種不變,不變者論其實質也是一個函數,一如四則運算中的(x)。另一種取反, ruby 中相應的算符,如果算項是邏輯值true false,就用 not ,若算項為數值,就用~ : not true = false, not false = true; ~1 = 0, ~0 = 1。

你可以在irb上 親自試一試。然而因為irb 自動給出的數值結果都是10進制,須寫成如下的形式,纔能把二進制數顯示出來。

>"%b" % ~1
=> "..10"

.. 表省略,那前一位的1呢。因隱含地 1 就等於 0000...00001 ,對1取反的同時,也會對前面各位的零取反。

兩個算項呢? 又有幾個甚麼樣的運算符?

邏輯值、算符同結果的關係,我們通常用真值表(truth table)來展現。

真值表

A B (A and B) (A or B) (A xor B)...
T T     T        T         F    
T F     F        T         T    
F T     F        T         T    
F F     F        F         F   

真值表的列法由算項的個數決定。我們知道,每個邏輯變量有T F兩個值,那麼兩個邏輯變量就有 2 × 2 = 4 種值的組合,如上表頭兩列所示,若有三個算項,就有 2 × 2 × 2 = 8種組合,餘皆依此類推。其運算結果呢? 每種組合都可以有T F兩種結果(如第一行 T and T = T,而 T xor T = F),那麼四種組合,就是2的4次冪16種可能的結果組合(表中算符下的各列)。每一種結果組合對應一個算符(或函數),那麼滿打滿算就該有16個算符。然而編程語言中一般二目的邏輯算符只有 and 和 or,因為含其他的算符的式子都可以轉化成只用 not and or 三個算符(或者and/or任選其一,只用兩算符)的式子。

如上表的 A xor B 就等價於 (A and (not B))or((not A)and B),如下表所示。

A B|(A and(not B))or((not A)and B)
T T| T  F   F     F   F     F  T
T F| T  T   T     T   F     F  F
F T| F  F   F     T   T     T  T
F F| F  F   T     F   T     F  F

將邏輯值的組合分別寫在各變量和算符之下,就是以真值表解邏輯算式的算法。上表中,我們先將(not B)和(not A)的值分別標在兩個not 下,然後將(A and(not B))與((not A)and B)的值求出,分別標在兩個and 下,最後再跟據這兩列值,求出最外一層的or值,將之記在or下。除了and(∧),or(∨),not(¬ ) ,xor(⊕),還有一些常用的邏輯算符,如→,↑,↓,等,屬於邏輯學的範疇,本文不擬展開了講,誰有興趣,我這兒給地址(http://class.htu.cn/lisanshuxue/index.htm),自己去進修。

前文說到算術運算在計算機中實際上是靠邏輯運算來實現的。下文就講一講電腦是如何用and、or、not來做自然數加法的。

先說說進位制的之間的轉換。

這個很簡單,就是除進位數取餘法。二進制的進位數是二,滿二了進一位;八進制的進位數是八,餘類推。

比如一個十進制數三十七:37,轉二進制的方法就是連續除以二,除到零為止,取各步的餘數,從右往左一排,就是相應的二進制數。

37/2 = 18 餘 1
18/2 = 9 餘 0
9/2 = 4 餘 1
4/2 = 2 餘 0
2/2 = 1 餘 0
1/2 = 0 餘 1

得出的二進制數就是100101,用 irb 驗算一下,"%b" % 37 =>"100101",正確,加十分

還原更容易,就是各位的數值乘以2的所在位次冪,然後將這些冪加起來。

100101  1 * 2 ** 5 + 1 * 2 ** 2 + 1 * 2 ** 0

(注意,最右位是二的〇次冪。而〇乘以任何數都等於〇,自然不用把零位也加進來)。把式子copy到irb上,回車 => 37,哈哈,正確,再加十分!

十六進制也是一樣。 十六進制的基數,0-9 同十進制, A-F (大小寫皆可)則分別表示10-15。16 呢? 16就進位了呀!

那麼,如 365,

365 / 16 = 22 餘 (13 = D)
22 / 16 = 1 餘 6 
1 / 16 = 0 餘 1

用 ruby 驗算一下: "%X" % 365 => "16D".

Piece of cake!

Ruby 中,二進制記作 0byyyyy... (y = 1/0),八進制記作 0yyyy...(y = 0-7),十六進制記作 0Xyyyyy... (y = 0-F : 字母不計大小寫)。

好了,現在可以講加法了。

0+0 = 00
0+1 = 01
1+0 = 01
1+1 = 10

大家看出其中的關竅來沒有?

本位的加法實際上就是xor,進位與否看的是and。二進制加法的每一位運算都涉及到三個值。被加數A的i位的數值 Ai,加數B i位的數值Bi,進位值shift.

Ai,Bi不進位直加:   Ri  = Ai xor Bi
再加上前一位shift:  Ri' = Ri xor shift
                  new shift = (Ai and Bi) or (Ri and shift)

new shift 將參與 i+1 位的加法。

shift的最高值是1,因為 Ai,Bi,shift 三個數的和的最大值是十進制的3,二進制的11。

好比,5 = 0b101, 13= 0b1101,把這兩個數相加:

Ai Bi shift_i Resulti shift_i+1
1  1   0        0       1
0  0   1        1       0
1  1   0        0       1
0  1   1        0       1
0  0   1        1       0

下面附上了一個做二進制加法的小程序。把它隨便保存到某個目錄下,比如 C:\learnruby. 在irb 中 輸入 exit 退出到 cmd介面。

C:
cd \ 
mkdir learnruby
cd learnruby
binadd.rb 101 1101

該程序會顯示出運算步驟和結果。

binadd.rb.zip

用記事本把 binadd.rb打開,看一看,這個黑匣子裡究竟裝了些甚麼古怪。看不懂是應該的,但是試試看能不能 找到一些前文講到的看着眼熟的小片斷。

def 
    ...
end

就是本程序員自己寫的函數。前面提到的Math.sqrt就是ruby 系統自帶的函數。接下來,跟着我,咱們也寫一個有用的小函數。

inch2cm

再把 irb 打開,就在這裡頭寫,現炒現賣。

def inch2cm(i) #inch2cm 是函數名 i 是它的parameter,形參
  i * 2.54
end

done!

試一試!

irb(main):033:0> inch2cm(66)
=> 167.64000000000001
irb(main):034:0> inch2cm(88)
=> 223.52

這個函數是幹甚麼用的,顧名思義,我就不解釋了。那麼請你自己動手,再 DIY 一個 cm2inch。注意在算符兩邊加上空格,這是好習慣,眉目清朗利於閱讀,而且ruby有一些奇特的語法(後面會講到),你不加空格的話,有時會被解釋器(interpreter)誤讀,得不出你預想的結果。

你可以把這函數黏到文本編輯器中,保存成 「文件名.rb」的ruby 源代碼文件。好比你的這個文件存在 c:\learnruby\下。你就在該目錄下打開irb,輸入 load "文件名.rb",回車。一切正常的話,irb的提示行會給出「=> true」,說明你的寶貝源碼已經被加載到了irb的環境中。輸入 inch2cm(55),武大郎的身材就得出來了。這就叫代碼的可重用 ^_^

Okay,今天就講到這裡,起碼我們學會了拿irb當記算器用--甚麼?沒講!怎麼沒講,鼓搗了半天還沒明白irb是可以當計算器用的嗎!別嚇我。

我們認識了電腦中一切運算的基礎--邏輯運算。加法的原理我已經告訴大夥了,減法和乘、除法留給大夥自己琢磨。電腦中一切都是數,一切數歸根結蒂都是二進制數,一切對數據的處理歸根結蒂都是邏輯運算。加減法怎麼個機理無關緊要,關鍵是樹立一種觀念:編程就是跟邏輯打交道,跟數打交道。

課後作業

華氏溫度和攝氏溫度的轉換公式如下,請編寫函數f2c 和 c2f 分別實現華氏到攝氏和攝氏到華氏之間的轉換。

[°C] = ([°F] − 32) × 5⁄9         [°F] = [°C] × 9⁄5 + 32


第二課: 文字處理初階

字符編碼

文字在計算機中也用數來表現,其基本單位是字符(character:簡寫為char,讀作/tʃar/)。即便是零起點的駭客預備役大概也聽說過ASCII(American Standard Code for Information Interchange:美國信息交換標準碼),這個碼就是給128個常用的西文字符編上從0到127的序號。序號就是自然數,所以ASCII碼都是數。ruby中char的表記法是 「 ?x 」 如 ?a 就是字母"a"?強 就是漢字 「強」。在irb上輸入?a.ord?強.ord,回車,就會分別出得數值 9724375,它們就是相應字符的序號,亦即編碼。我們上http://asciitable.com/ 查對一下,果然,97正是字母「a」的ASCII碼。可是剛剛不是說,ASCII只對128個字符編了碼嗎!「強」字的24375是怎麼回事呢?

上一課講了,電腦中的數據實質上都是二進制數,二進制的一位(一個兩態開關)就叫一個bit,而8個bits就是一個byte(字節),而CPU的位數,今天流行的臺式機CPU是64位,win xp上軟件用的是32位,就是一個word(字)。 為甚麼8位一個字節?因為8/2 = 4, 4/2=2,用2的冪作為數據盛載的基本單位,翻番减半,處理起來方便。ASCII用了7個bits(27=128),加上一個標記位(flag)剛好是一個字節,一個32位的word就能裝4個ASCII字符。採用這種8、16、32、64..的單位制就能避免處理半個字符的麻煩。但那是對英文而言,因為128個碼位,裝英文的字母和符號勉強夠用,對數以萬計的漢字來說這點空間就太狹小了。可是誰讓電腦是人家美國人發明的呢!其他國家電腦的軟、硬件架構必須以美式的電腦為基準,也就是要做到與之兼容(compatible)。不然,人家先進的軟硬件你不能拿來就用,互聯網上萬國大同的信息交流也不能實現。所以,其他文字的編碼就不能與ASCII衝突,得做到任意一段文本(text),都能被軟件輕易地識別出是自定義的編碼還是ASCII。這時,ASCII預留的一個標誌bit就派上用場了。ASCII編碼格式是0XXXXXXX,其他編碼想與之兼容,就不可以出現連續多個字節的0XXXXXXXX 0XXXXXXXX ...,否則軟件就分辨不出誰是誰來。現今通行的支持多種文字共存的字符編碼是unicode,unicode的 utf-8 就是利用ASCII預留的第8bit標記位以實現與之相兼容的一種最流行的格式。其具體的設計如下

UTF-8

0XXXXXXX - 1 byte = ascii > U+007F (U+YYYY是十六進制的unicode碼點:code point)
110XXXXX 10XXXXXX - 2 bytes code  > U+07FF
1110XXXX 10XXXXXX 10XXXXXX - 3 bytes code  > U+FFFF
11110XXX 10XXXXXX 10XXXXXX 10XXXXXX 4 bytes code > U+1FFFFF

漢字都安排在3字節和4字節碼的區域內

說了半天了,玩玩irb,增加點感性認識。

irb(main):001:0> "%X" % ?A.ord
=> "41"
irb(main):002:0> "%X" % ?火.ord
=> "706B"
irb(main):003:0> "%X" % ?𤳳.ord
=> "24CF3"

還記得 「"%X" % 某數」的式子嗎,上一課講到過的? %X就表示將數值轉換為16進制的格式。記不得了就溫習一下。二進制呢? 對,是 %b ! 十六進制的英文是heXadecimal, 二進制是Binary,只要你的英文夠靈光,記起來絲毫不難。

我們這就試試二進制的格式。

irb(main):005:0> "%b" % ?a.ord
=> "1100001"  #數一數,只佔了七位
irb(main):006:0> "%b" % ?火.ord
=> "111000001101011"

咦! 漢字編碼不大對吧,1110000 01101011 - 這纔兩個字節不到,也不是你方才所說的 1110XXXX 10XXXXXX 10XXXXXX 的格式呀。

原因是 ruby會自動把 1110 、10、10的標誌位剔除。因為就utf-8而言只有XXX所佔的bits纔是有效的編碼!現在內存和硬盤這麼便宜,就讓它浪費一點也不打緊,你說呢

irb(main):007:0> ?火.bytesize
=> 3

嘿,果然是三個字節哦!可是,我想看看它的原始格式該怎麼看呢?

這樣看:

irb(main):008:0> ?火.bytes.map{|b|"%08b"%b}
=> ["11100111", "10000001", "10101011"]

Wow! 核對一下! 把 1110 10 10剔除 0111, 000001, 1010011 =”111000001101011"

怎麼樣,我沒說瞎話吧!

句法

好了,該講一講句法(syntax)了。 ?火.ord?火.bytesize?火.bytes.map,這些 .blah都是些甚麼玩意?

這些在ruby中就叫做message(消息),點前面的那個東西(eg. ?火?A)就叫做 receiver(接收者)。這種句式的含意就是 「那個誰呀, 你如此這般!」

比如 ?火.ord 可以翻譯成:「『火』字,把你的『序號』(ordinal)給我。」同理,?火.bytesize 就是「『火』字,把你的『字節長度』給我。」

那麼上一課求兩點距離的式子中所用到的Math.sqrt 就是:「『數學模塊』,把甚麼甚麼的『平方根』給洒家求出來!」有意思吧。我早說嘛,電腦是人類的忠僕,你讓它幹啥,它就幹啥,無條件地凜遵恭行。美!

?X是單個的字符,“XXX”是甚麼呢?“XXX”的名字叫,羊肉串的串「string」,就是把字符連成串的意思,不然,?武 ?昌 ?起 ?義 一個一個碼,會把人累死。

串可以寫成"XXX"的形式,也可以用單引,寫成'XXX',區別是前者里邊可以夾零碎兒,好比超市裡雞肉脆骨串美其名曰「骨肉相連」,後者呢,就是本本份份、表裡如一的串。夾東西有個學名叫作 interpolation,究竟能夾些啥呢?比如,我想在一個串裡寫兩行字

irb(main):001:0> puts "Hello\nWorld!"
Hello
World

換成單引,\n仍是\n\n是newline(換行符)的escape(轉寫),常用的escape還有\t:製表符、\'\",「"」「'」是串的起始、結束標記,我們想在""串中加上",就得用escape。

puts是甚麼? puts(put string) 也是一個 message,接收者是不露面的神秘人,後面的 "xxx" 是該message的參數。參數?那不是函數嗎?對呀!所有的message都是函數。怎麼不加括號?加上也行啊!你試試看! 上一課說C一系的語言,其參數(arguments)一般是括寫來的,然而ruby考慮到程序員一個比一個懶,只要參數不是特別複雜的式子,不會被interperter誤讀的,就可以把括號省了。可是上一課求兩點距離的函數偏偏省不得。因為 Math.sqrt((x1-x2)2 + (y1-y2)2)中,+ 的優先級(precedence:如先乘除後加減,說的就是 + - × ÷ 的優先級)太低,若不加括號,interpreter 會誤會成先求 (x1-x2)**2的方根,再把結果與(y1-y2)**2相加。(優先級的具體規定,請參考http://phrogz.net/programmingruby/language.html#table_18.4)。

我們已經結識了函數的自變量(參數),在其他的場合也可以引入變量。ruby的變量名以小寫字母開頭,後續的字符可以是任意字母、數字或_,但不能有空格。

比如

irb(main):009:0> magic_number = 42

就是聲明了變量magic_number,並將數值42賦給它。

irb(main):010:0> puts magic_number
42
=> nil
irb(main):011:0> super_magic = magic_number ** magic_number
=> 150130937545296572356771972164254457814047970568738777235893533016064

Wow!

irb(main):012:0> super_magic.to_s.length
=> 69

數據類型

前一課說數,本課說字符和串,它們都是ruby預置的「數據類型」(data types),所以要講一講他們之間的相互轉換,纔好銜接。先要知道,數,不只一種,自然數、整數、實數、複數,ruby中常用的數的類型有 Fixnum(小整數), Bignum(大整數),Float(浮點數,即帶小數部分的實數),和 Complex(複數)四種。在物件的後面加上「.class」就可以看它的數據類型:「那個誰呀,把你的『class』告訴我。」

irb(main):005:0> 1.class
=> Fixnum
irb(main):006:0> 3.14.class
=> Float
irb(main):007:0> super_magic.class
=> Bignum
irb(main):008:0> Complex(1,3).class
=> Complex
irb(main):009:0> "string".class
=> String
irb(main):010:0> ?a.class
=> String           # 哦!字符原來也是串

所有的物件都能對消息 .to_s (轉成string),做出響應。如果是整數類,還可以給它加上進位制的參數。

irb(main):011:0> "a".to_s
=> "a"
irb(main):012:0> 42.to_s
=> "42"
irb(main):013:0> 42.to_s 2
=> "101010"
irb(main):014:0> 42.to_s 16
=> "2a"
irb(main):015:0> super_magic.to_s 16
=> "5919417cd6a11dbdf2f413657bbc03bc842ac76ef3932640000000000"

反過來,串也可以轉成數,前提是串上穿的委實得是數。to_i:轉成整數,to_f:轉成Float。

irb(main):016:0> "a2".to_i
=> 0
irb(main):017:0> "a2".to_i 16
=> 162
irb(main):018:0> "ff".to_i 16
=> 255
irb(main):019:0> "3.14".to_i
=> 3
irb(main):020:0> "3.14".to_f
=> 3.14
irb(main):021:0> "36.5C".to_f
=> 36.5    # ruby 會自動把多餘的部份略掉

對數我們可以進行加減乘除乘方開方等運算,對串呢!

irb(main):001:0> "喫葡萄不吐葡萄皮".length  # 求所含字符數
=> 8
irb(main):002:0> "喫葡萄不吐葡萄皮"[4]        # [x] 相當於下標,抽取相應位置的字符,由0數起
=> "吐"
irb(main):003:0> "喫葡萄不吐葡萄皮"[-1]
=> "皮"
irb(main):004:0> "喫葡萄不吐葡萄皮"[-2]       # 倒着數(右起左行)也成
=> "萄"
irb(main):005:0> "喫葡萄不吐葡萄皮"[1..-2]    # n..m 是從n到m(包含m)的閉區間
=> "葡萄不吐葡萄"
irb(main):006:0> "喫葡萄不吐葡萄皮"[1...-1]   # 三個點是開區間
=> "葡萄不吐葡萄"
irb(main):007:0> "喫葡萄不吐葡萄皮".reverse
=> "皮萄葡吐不萄葡喫"
irb(main):008:0> "喫葡萄不吐葡萄皮".split('')  # 劈成數組
=> ["喫", "葡", "萄", "不", "吐", "葡", "萄", "皮"]
irb(main):009:0> "aplle,orange,banana".split(',') # , 為分隔符
=> ["aplle", "orange", "banana"]
irb(main):010:0> "some pig!".capitalize  
=> "Some pig!"                # 首字母大寫
irb(main):011:0> "some pig!".upcase          # 全轉成大寫
=> "SOME PIG!"            
irb(main):012:0> "SOME PIG!".downcase.capitalize   # 先全轉小寫,再大寫首字母
=> "Some pig!"
irb(main):013:0> "喫葡萄不吐葡萄皮".gsub "葡萄","核桃" # globally substitue 全替換
=> "喫核桃不吐核桃皮"                  # .gsub a, b:以b換a,注意用「,」隔開參數。
irb(main):014:0> "喫葡萄不吐葡萄皮".sub "葡萄","核桃"  # 只替換第一個match
=> "喫核桃不吐葡萄皮"
irb(main):015:0> "喫葡萄不吐葡萄皮".delete ?不    # 刪掉match的片段
=> "喫葡萄吐葡萄皮"

好了,教程不是參考書,String的函數多得數不過來(誇張啦),想怎麼樣、能怎麼樣、該怎麼樣,自己去查文檔(http://www.ruby-doc.org/core/)

上一課留的作業想必大家都已經做完成了。甚麼?沒寫!沒寫就沒寫吧,放心,俺不會留你補作業.現在咱們就用本課所學的知識將c2f f2c兩函數合併起來,寫一個美化版。使它能自動識別串形式的 "36.6°C" 和 "100°F", 自動把°C 轉成°F,把 °F轉成 °C

def cf_convert(temp) 
  unit = temp[-1].upcase   # 取末尾字符,單位,轉成大寫
  value = temp.to_f        # 將串轉成實數,單位符號會自動略掉
  if unit == ?C            # 條件語句,若 unit 等於(==) 字符C則如何
    puts "#{value}°C = #{value * 9 / 5 + 32}°F"
  elsif unit == ?F         # 若 unit == 字符F則如何
    puts "#{value}°F = #{(value - 32) * 5 / 9 }°C"
  end
end

==」是怎麼回事? 「==」是等於號,「=」則是賦值號unit = temp[-1].upcase 是把輸入串的末字符轉成大寫後賦予變量 unit. unit == ?C 則是拿 unit 的值,那個末尾字符,去跟字符C比較,看兩者是否相等。是和否--哦,這是邏輯值啊!沒錯, == 就是一個二目邏輯算符,與 not (A xor B) 等價。看看上一課的真值表,看我說的對不對。當然,ruby中有not and or,卻沒有xor 只有 ==。

if 
[... 塊A]
else
[... 塊B]
end

是根據邏輯值 T 或 F 產生分支。若if後為T則執行A塊,若為F則執行B塊。我說的執行是借用傳統編程語言中的概念,即所謂的執行指令(instruction)或語句(statement)。在ruby中,指令、語句其實都是式子,表達式(expression)。兩者的區別留到後文書再表。

分支有起始有終結,if是起始,end是終結。def .. end也用end。Ruby中語句塊(即〇個或一組語句:block)的終結往往以end為標誌。分成兩支,以一個end來收束,就說明兩條支流最終又匯合到了一處。究竟怎麼一回事,請看下面流程圖的直觀解釋。

elsif是 else 和 if的縮略形。本例分支的實際結構為:

if unit == ?C
  #輸出C轉F
else
  if unit == ?F
    #輸出F轉C
  else
    #甚麼也不做,故可略掉此塊
  end
end

應該懂了吧! Okay!「#{...}」--這是甚麼東東? 我不是說了嗎,""括起來的 的串中可以夾零碎,你可以直接把算式和變量插進去,只要以#{}括起來就行了,ruby會自動提出變量的值、對式子求解,然後將結果 interpolate 到串中。

試一試! 把上面的函數保持到源文件cf_convert.rb中,在最上面加上一行

# encoding: UTF-8

因為ruby 的interpreter將源文件都默認為ASCII,上面函數中有個淘氣的「°」,這一行(「#」起頭的是注釋,課文中已多次出現,表示從 # 到右面行末的文字不是代碼,)是告訢ruby,本程序用到了非 ascii 的 unicode 字符。當然,你得把文件保存成 utf-8 編碼 -- 這也不會嗎? 自己去google!

然後,打開cmd, cd 到 cf_convert.rb 所在的文件夾(目錄),打開irb。

irb(main):001:0> load "cf_convert.rb"
=> true
irb(main):002:0> cf_convert '37C'
37.0°C = 98.6°F
=> nil
irb(main):003:0> cf_convert '100 °f'
100.0°F = 37.77777777777778°C
=> nil

Awesome!

今天就講到這兒。留甚麼作業呢,等會想好了再帖上來。Have fun!


作業

白日依山盡
黃河入海流
欲窮千里目
更上一層樓

用上文介紹的messages轉換成下文的形式

盡山依日白
流海入河黃
目里千窮欲
樓層一上更

提示:\n是換行符;五絕各句固定為五個字。

blog comments powered by Disqus