2008年4月6日

關於Block的操作

【什麼是Block】
利用block,可以將多個值聚在一起。如下所示:

[ 1 "one" "uno" "一" ]

一個block內的值,型別可以不一致。以上面的例子來說,1是integer!,而另外三個是string!。

下面的block,是Jerry的個人資料:

user-info: [ "Jerry" "Tsai" JerryTsai@gm.com ]

你可以利用下面的方式,取得Jerry的電子郵件:

user-info/3

其實上面的寫法和下面的意義完全一樣:

pick user-info 3

除了pick之外,這篇文章與這篇文章所提到的函數,也都可以用來處理block。

【使用標籤】
上面的block組織方式有一個缺點,例如,某天你可能會想要在last-name與email之間新增「語言」欄位,變成:

user-info: [ "Jerry" "Tsai" "Chinese" JerryTsai@gm.com ]

如此一來,你就必須把「user-info/3」改成「user-info/4」,否則取得的不是電子郵件,而是語言。如果有這樣的顧慮,可以在每個資料前面加上一個word,當作標籤:

user-info: [ last-name "Jerry" first-name "Tsai" language "Chinese" email JerryTsai@gm.com ]

這麼一來,就可以用下面的方式來取得電子郵件資料:

user-info/email

上面的寫法和下面的作用一樣:

select user-info 'email

對於select來說,任何值都可以當作「標籤」,不一定要是word!。select單純就是在陣列中找出相同的元素,然後傳出「下一個元素」。如下面所示:

>> select [ 1 "one" 2 "two" 3 "three" ] 2
== "two"

由於select處理block資料時,是從頭開始一個一個比對,所以當block內的元素個數很多時,select的效率可能會很差。這個時候不要再使用block!,要改用map!(我以後會寫文章介紹。)

【套疊的block】

如果我的電子郵件帳號不只一個,可以寫成下面這樣:
user-info: [ "Jerry" "Tsai" [JerryTsai@gm.com jtsai@hmail.com]]

這個時候,使用user-info/3就會取得[JerryTsai@gm.com jtsai@hmail.com]。像這種block內還有block的套疊(nested)組織方式,在REBOL程式中很常見。

你可能會想利用套疊的方式,做出簡單的資料庫。下面的例子中,Users裡面有三筆紀錄:

; 方法1
users: [
[ first-name "Jerry" last-name "Tsai" email JerryTsai@gm.com ]
[ first-name "Terry" last-name "Gao" email tgao@gmail.com ]
[ first-name "Mary" last-name "Lee" email m.lee@gmail.com ]
]

當資料量一大,裡面重複出現first-name、last-name、email這些word,很浪費儲存空間。這個時候還是將它們省略比較好:

; 方法2
users: [
[ "Jerry" "Tsai" JerryTsai@gm.com ]
[ "Terry" "Gao" tgao@gmail.com ]
[ "Mary" "Lee" m.lee@gmail.com ]
]

【攤平套疊的block】
當每筆記錄的元素個數相同,且欄位意義都一樣時,我們可以將它們攤平,變成這樣:

; 方法3
users: [
"Jerry" "Tsai" JerryTsai@gm.com
"Terry" "Gao" tgao@gmail.com
"Mary" "Lee" m.lee@gmail.com
]

這麼做有一個很大的優點。REBOL有許多block相關的函數都有提供/skip修飾字,可以將固定數目的元素視為一筆記錄,進行處理。至於方法2,則無法做這樣的處理。如果想將每三個元素當成一筆記錄,以每筆記錄的第一個欄位(first-name)做比較,進行排序,作法是這樣:

sort/skip users 3

如果想比較的欄位不是第一個,而是第二個(last-name),則使用/compare,作法是這樣:

sort/skip/compare users 3 2

不幸的是,有一些函數應該要具有/compare修飾字,但事實卻不然(例如FIND)。還有一些函數應該要具有/compare修飾字,但事實卻不然,甚至連/skip都沒有支援(例如maximum-of)。我已經將這些函數整理出來,向REBOL公司提出要求,接下來就看他們是否要支援了。

你可以利用extract函數,固定每隔幾個位置,就取出元素,做出一個block:

>> first-names: extract users 3
== ["Jerry" "Terry" "Mary"]

請注意,extract並不會破壞原始的block,所以在執行完上面之後,users的內容還是一樣。

利用extract的/index修飾字,可以在一開始產生一個位移,取出別的欄位。例如,下面分別取出全部的last-name和全部的電子郵件:

last-names: extract/index users 3 2
emails: extract/index users 3 3

(介紹我向REBOL公司提議的interleave函數)

沒有留言: