2008年12月26日

Get Windows Information in REBOL 2.x

寫了一段REBOL 2.x程式, 可以用來進行許多OS相關的判斷.

Kernel32.DLL: load/library %Kernel32.dll

MultiByteToWideChar: make routine! [
CodePage [ int ] ; 65001 for UTF-8
Flags [ int ]
MultiByteStr [ char* ]
MultiByteLen [ int ]
WideCharStr [ char* ]
WideCharLen [ int ]
return: [ int ]
] Kernel32.DLL "MultiByteToWideChar"

User-profile: dirize to-rebol-file get-env "Userprofile"

make-sure-c-string: func [ str [ string! ] ] [
if #"^@" <> last str [ append str #"^@" ]
]

get-wide-str: func [ str [string!] /local tmp ans ] [
make-sure-c-string str
insert/dup tmp: copy "" #" " 2 * length? str
MultiByteToWideChar 65001 0 str -1 tmp length? tmp
ans: copy/part tmp any [
attempt [ skip find tmp "^@^@^@" 3 ]
attempt [ skip find tmp "^@^@" 2 ]
tail tmp
]
]

GetWindowsDirectory: make routine! [
Buffer [ char* ]
Size [ int ]
return: [ int ]
] Kernel32.DLL "GetWindowsDirectoryA"

insert/dup buffer: copy "" #" " 200

GetWindowsDirectory buffer length? buffer
OS-drive: rejoin [ %/ copy/part buffer find buffer ":\" "/" ]

make-elements: func [name count type /local result][
if not word? type [type: type?/word type]
result: copy "^/"
repeat i count [
append result join name [i " [" type "]" newline]
]
to block! result
]

OS-VERSION-INFO-EX: make struct! OS-VERSION-INFO-EX-SPEC: compose/deep [
OSVersionInfoSize [ int ]
MajorVersion [ int ]
MinorVersion [ int ]
BuildNumber [ int ]
PlatformId [ int ]
(make-elements 'ch 128 #"@")
ServicePackMajor [ short ]
ServicePackMinor [ short ]
SuiteMask [ short ]
ProductType [ char ]
Reserved [ char ]
] none

OS-VERSION-INFO-EX/OSVersionInfoSize: 148 + 8

GetVersionEx: make routine! [
VersionInfo [ struct! [ (OS-VERSION-INFO-EX-SPEC) ]]
return: [ integer! ]
] Kernel32.dll "GetVersionExA"

GetSystemDefaultLCID: make routine! [
return: [ integer! ]
] Kernel32.dll "GetSystemDefaultLCID"

GetLastError: make routine! [
return: [ int ]
] Kernel32.DLL "GetLastError"

if zero? GetVersionEx OS-VERSION-INFO-EX [
alert rejoin [ {Error When Calling "GetVersionEx". Error Code =} GetLastError ]
exit
]

os-ver: rejoin [OS-VERSION-INFO-EX/MajorVersion "." OS-VERSION-INFO-EX/MinorVersion]
;SP?: OS-VERSION-INFO-EX/ServicePackMajor

sp: to-string copy/part at third OS-VERSION-INFO-EX 21 128
sp: copy/part sp find sp #"^@"

Win-Vista?: Win-2003?: Win-XP?: Win-2000?: false
OS?: switch/default os-ver [
"6.0" [ Win-Vista?: true "Win-Vista"]
"5.2" [ Win-2003?: true "Win-2003" ]
"5.1" [ Win-XP?: true "Win-XP" ]
"5.0" [ Win-2000?: true "Win-2000" ]
] [ none ]

SP0?: SP1?: SP2?: SP3?: SP4?: false

sp?: switch sp [
"" [ SP0?: true 0 ]
"Service Pack 1" [ SP1?: true 1 ]
"Service Pack 2" [ SP2?: true 2 ]
"Service Pack 3" [ SP3?: true 3 ]
]

HOME-Ed?: not zero? (OS-VERSION-INFO-EX/SuiteMask and 512)
EMBEDDED-Ed?: not zero? (OS-VERSION-INFO-EX/SuiteMask and 64)

zh-TW?: zh-CN?: zh-HK?: zh-MO?: zh-SG?: ja-JP?: ko-KR?: vi-VN?: th-TH?: false

lang?: switch/default skip to-hex GetSystemDefaultLCID 4 [
#0404 [
zh-TW?: true "zh-TW"
]
#0804 [
zh-CN?: true "zh-CN"
]
#0c04 [
zh-HK?: true "zh-HK"
]
#1404 [
zh-MO?: true "zh-MO"
]
#1004 [
zh-SG?: true "zh-SG"
]
#0411 [
ja-JP?: true "ja-JP"
]
#0412 [
ko-KR?: true "ko-KR"
]
#042a [
vi-VN?: true "vi-VN"
]
#041e [
th-TH?: true "th-TH"
]
] [ none ]

2008年12月25日

利用Delect設計方言

REBOL具有豐富的Literal,除了可以提高程式的「可讀性」和「可寫性」之外,也可以方便設計方言(dialect,也就是所謂的DSL)。這一點是其他語言遠比不上REBOL的。

以往,設計REBOL方言的方式,是利用Parse函數。在REBOL 3.0之後,又多了一個方式,就是利用Delect。Delect是DEcode diaLECT的意思。REBOL內部的方言,許多都是利用Delect做出來的。

大多數的方言,都會定義一些關鍵字(keyword)。每個關鍵字後面可以有0到多個參數。有些時候,這些參數出現的次序無所謂,因為透過型別就可以推測出參數的意義。例如:「Circle 100x100 50」與「Circle 50 100x100」,雖然100x100和50的出現次序不同,但是都可以讓我們認為這是要表達一個「圓心在100x100」,半徑是50的圓。所以處理這類方言的時候,值的次序不重要,重要的是值的型別,這種方言用Delect設計會相當容易。

良好的方言,應該要方便學習與使用。放寬參數的次序,可以幫助使用者學習與使用方言。以上面的Circle例子來說,使用者就不需要牢記,到底是先寫半徑再寫圓心,還是先寫圓心再寫半徑。

關於Delect的用法,請見DocBase的這篇說明。我以後也會寫文章說明Delect的詳細用法。

2008年12月2日

REBOL 3.0 alpha 18

這個版本新加入三名核心測試成員。未來在修正完畢他們所找到的Bug之後,將會更廣泛地釋出,讓更多人試用。關於2008/11與2008/12的進展與規劃,請見這篇文章。

2008年12月1日

文法剖析器


昨天晚上整理磁碟機,找到一個很久以前用REBOL寫的一個程式:「文法剖析器」。我希望在REBOL 3.0釋出之後,能有時間重寫這個程式,並完善它。

RebTalk

REBOL語言設計者Carl今天對GUI Test World釋出新版的REBOL 3.0,並開發了一個名為RebTalk的Reblet程式。這是REBOL 3.0的第一個Reblet。

2008年11月28日

Jerry Tsai-Certified REBOL Programmers

I've been training my programmers for REBOL programming. After they finish my REBOL course, I would like to give them certifications like this.




I hope that some day I will have a REBOL Technologies certification, signed by Carl personally. :-)

2008年11月20日

ReBrowse

近日在REBOL.NET的Wiki上,REBOL語言設計者Carl寫了一份文件說明REBOL 3.0的新操作介面ReBroswe,用來取代REBOL 2.x的Desktop。

文件中提到,ReBrowse將採用類似Web瀏覽器的介面設計,讓大家容易上手。但ReBrowse不打算取代Web瀏覽器,它只是用來連接到Web Server,以瀏覽與執行Reblet。

2008年11月4日

關於REBOL 3.0的進展

我長期關注REBOL語言的發展,至今已經有八年了。看到REBOL語言設計者Carl Sassenrath幾乎完全以一己之力,漸漸打造出一個他理想中的語言,我感到相當佩服。小巧、簡單、效率,這些特質已經漸漸不再被泛泛大眾所重視,取而代之的是「Time-To-Market」等急就章的論調。儘管如此,小巧、簡單、效率,依然是Carl Sassenrath在發展REBOL時的信條。

理想和現實是有落差的,為了做出一個理想的語言,我看到REBOL的發展腳步相當緩慢。就以REBOL 3.0來說,已經開發了三年,至今依然在Alpha版。為了設計出Carl理想中的GUI方言,他甚至將一套完成約80%的GUI方言次系統整個重做。像這樣的事情,往往使得我們這些REBOL用戶相當著急。

但這畢竟是Carl的語言,我們還是尊重他的開發理念。而且他確實有相當多先進的看法,我們也不希望他急就章弄出一個不好的語言,我們這些持續等待REBOL的人,相當程度都認同Carl的理想,佩服Carl的眼光和技術能力。但還是有一些人無法久候,紛紛離去。
最近Carl在開發GUI次系統上似乎已經有了一定的進展。我想或許再過幾個月就可以有Beta版出現。我想明年應該一定會推出REBOL 3.0。我希望到時候能有IT廠商或媒體願意和我一起邀請Carl到北京,親自向大家介紹這個「不顧現實的時間壓力」而精心打造出來的好語言。

2008年9月29日

Multi-tasking in REBOL

REBOL 3會支援Multi-tasking,儘管目前DocBase尚未有相關的說明,但已經完成了初步的實做,可以使用了。例如:

>> do make task! [ [ ] loop 10 [ print now wait 1] ]

2008年8月27日

2008年7月18日

REBOL in Olympics

I've been in Beijing for 7 days. It's a busy week. I've been working very hard everyday, including weekend, from morning to midnight. I'm re-doing our product in REBOL, and I made a good progress, thanks to REBOL.

A small component I developed in REBOL is now included in a software package, which will be shipped to a famous IT company for the Olympics-related affairs.

2008年7月4日

REBOL與RIA

在「RIA說文解字」一文中,我提到了RIA的幾個技術,雖然當時沒提到REBOL,但是我心理其實很清楚,REBOL相當適合做RIA。在這篇「REBOL讓你讚嘆WOW」一文章,我特別說明REBOL具有RIA領域的潛力,因為:

(1) 執行環境超小
(2) 寫出來的程式小
(3) 跨越許多平台
(4) 支援許多標準協定

我認為,在RIA領域,REBOL會比Flash/AIR、Silverlight/WPF、Curl更適合。

根據內部的消息,REBOL 3.0會開始著墨在RIA領域。REBOL/IOS、REBOL/Desktop…等應用,以後會朝RIA的方向邁進。我想,這應該就是之前Carl在「A Busy Spring 2008」一文中所提到的祕密計畫。

2008年6月28日

Moving to Beijing in Two Weeks

Not for the Olympic Games, I am leaving Taiwan for China because I've just accepted a job there. The CTO is an old friend of mine. He has been hearing me talking about REBOL for many years. Since REBOL Technologies will support Unicode in REBOL/View soon, there is a good chance that we'll use REBOL in our next product.

2008年6月20日

相等

欲判斷兩個值是否相等,利用「=」op!或者「equal?」action!,例如:

>> 1 = 1
== true
>> equal? 1 1
== true
>> "ABC" = "abc"
== true

即使型別不同,值也可能相等,例如:

>> 1 = 1.0
== true

上面的1是integer!,而1.0是decimal!,兩者型別不同。

如果要求嚴格一點,必須值和型別都一致,才能算相等,這時候使用「==」op!或「strict-equal?」action!。例如:

>> 1 == 1.0
== false
>> strict-equal? 1 1.0
== false

判斷兩個值是否不相等,利用「<>」op!或「!=」op!,或者「not-equal?」action!,例如:

>> 1 <> 2
== true
>> 1 != 2
== true
>> not-equal? 1 2
== true

判斷兩個值不相等「且」型別也不一致,利用「strict-not-equal?」action!(沒有對應的op!可用)。目前「strict-not-equal?」有bug。

=?用來判斷兩個參考(reference)是否一樣(identical),只有用在function, Series, object等參考型別,才有意義。用在其他型別時,相當於「=」。「=?」如果一邊是參考型別,另一邊是其他型別(非參考),則一定為false。

2008年6月19日

閱讀REBOL源碼時的干擾

我發現在閱讀REBOL源碼時,如果參數或修飾字(refinement)所使用的名稱和REBOL常用函數同名時,會造成我相當大的閱讀困擾。例如:

replace: make function! [[
target [series!]
search
replace
/all
/case
/tail
/local save-target len pos value do-break
][
....
]

這裡出現的replace,除了是函數名稱,也是參數;all、case、tail除了是修飾字,更是很常用的REBOL函數。我只要忘了它們是修飾字和參數,一直以函數的方式對待它們,就會造成閱讀源碼時理解力受到干擾。例如:

do-break: unless all [:break]

我一直覺得這裡的程式有錯,一直到很久以後才發現這裡的all不是函數,而是修飾字。

寫這篇blog文章,用來提醒自己和本blog的讀者(if any),閱讀REBOL源碼時,一定要注意這種狀況。

In REBOL 3.0, op! is infix only.

剛剛發現,在REBOL 3.0中,op!居然「只能」進行中序(indix)運算,這和REBOL 2.x的作法不同。
R2 >> 1 + 2
== 3
R2 >> + 1 2
== 3

R3 >> 1 + 2
== 3
R3 >> + 1 2
** Script error: + operator is missing an argument

我認為R3的作法比較好,可以簡化解譯器。以後op!應該不會再支援prefix。

My 8 REBOL Articles Published in China, 2004

我曾經在中國大陸的雜誌發表過一系列REBOL文章(約8篇),本來考慮要在這個Blog重新發表這系列文章,但是最近重新看了一下,許多內容其實已經過時,和REBOL 3.0的現況以及REBOL未來的走向不符合。所以這些文章只好石沈大海了!

如果你手邊還有我的這些文章,其實你不需要花時間閱讀它們。把時間花在REBOL 3.0版,這才是未來,這才重要。

Bug: change/part

軟體開發就是這樣,有時候消除掉十個Bug,卻會因此導致一個原本不存在的Bug。剛剛發現,原本沒問題的函數Change,現在卻有了一個Bug。

>> data: "C# Rules"
== "C# Rules"

>> change/part data "REBOL" 2
== "les"

>> data
== "REBOLles"

由於Change是Action!,所以我無法fix。我已經將它呈遞到Bug Database了。

Bugfix: script?

修改後的結果如下:

script?: make function! [[
{檢查是否具有有效的劇本標頭(暫時)。}
source [file! url! binary! string!]
][
switch type?/word source [
file! [ source: read source ]
url! [ source: read source ]
string! [ source: to-binary source]
]
find-script source
]]

A Secret Project Is Going On

根據Carl部落格文章「A Busy String 2008」的說明,今年夏天較大規模釋出REBOL 3.0的目標不變。除此之外,他還提到REBOL 3.0會有一個神秘的內建功能,至於是什麼,目前他先賣個關子。

我一向認為「謠言始於智者」,我開始散播謠言。我猜想應該是「軟體線上市集」或「AltME傳訊軟體」或「Peer-to-Peer軟體」之類的。

2008年6月18日

小心TO-BLOCK的陷阱

許多REBOL函數的參數都同時接受單一值,或多重值。當然多重值會放在block中。我們經常會需要在函數內,先將某參數化成block,然後才進行處理,這時候程式會這麼寫。

if not block? :args [args: reduce [:args]]

透過to-block(或者to block!),可以簡化上面的作法,改寫成這樣:

args: to-block :args

to-block會判斷,如果後面的參數是any-block!,則不處理,直接傳出其block形式;如果參數不是any-block!,則傳出以該參數為「唯一」元素的block。如下所示:

>> to-block 1
== [ 1 ]
>> to-block [ 1 ]
== [ 1 ]

To-block確實相當方便,但是要注意一個特殊狀況,那就是any-block!除了block!之外,還包括其他:

>> ? any-block!
ANY-BLOCK! is a typeset of value: make typeset! [block! paren! map! path! set-pa
th! get-path! lit-path! ]

因此下面的狀況固然可能非你所願,但卻是正確的:

>> to-block 'system/version
== [ system version ]

這個時候,只好回去改用一開始的寫法:

if not block? :args [args: reduce [:args]]

Bugfix: ??


??: make function! [[
{印出變數名稱,後面跟著它的鑄模值(幫助除錯用)。}
'name
][
print either any [word? :name path? name] [
repend form name [": " mold name: get :name]
] [
mold :name
]
:name
]]

上面是??函數的源碼,有個小小的bug。必須把其中的form改成mold,否則當參數是path的時候,「/」會消失,例如:

>> ?? system/version
system version: 2.100.13.3.1
== 2.100.13.3.1

另外「any [word? :name path? name]」也應該要改成「any [word? name path? name]」。

R2 vs. R3 Contexts

Brian Hawley是頂尖的REBOL專家之一,他對於程式語言的理論於實務,以及REBOL內部的運作,都相當熟悉。我經常在與他交談的過程中,學到相當多知識。他有一篇關於REBOL Context的說明,相當精采。文章請見 http://www.rebol.net/wiki/R2_vs._R3_Contexts

2008年6月17日

Cheyenne Web Server

看到Cheyenne(http://cheyenne-server.org/),你一定會感到很驚訝…一個具有實用功能的Web Server系統,居然如此小巧!Windows版本的exe檔,不到500 KB,這主要是因為,Cheyenne是用REBOL開發出來的。

Cheyenne作者Dockimbel表示,在一般雙核心的伺服器上,可以同時服務500個請求,Cheyenne適合用來架設中小型網站,也相當適合用來架設個人網站。目前Cheyenne雖然仍在Beta階段,但是已經相當穩定,也在某些網站中實際運作一段時日。

Cheyenne對於Web標準的支援已經算很完備,且持續在進步中。你可以在Cheyenne網站的首頁看到它目前所支援的標準,也可以在此下載它的REBOL源碼(BSD授權方式)或執行檔(Windows、Linux、Mac OS)。請注意,如果你要透過源碼執行Cheyenne,請使用REBOL 2.x版,而非REBOL 3.0版。

2008年6月16日

PICK is not picky

PICK可能是最不挑剔的(picky)REBOL函數,它接受的第一個參數可以是35種資料型別:

series! gob! pair! event! money! date! time! object! port! tuple ! bitset! any-function!

只有12個?別忘了,其中series!和any-function!是typeset!,將它們兩個展開之後變成

string! binary! file! email! url! tag! issue! bitset! vector! image! block! paren! map! path! set-path! get-path! lit-path! gob! pair! event! money! date! time! object! port! tuple ! bitset! native! action! routine! rebcode! op! closure! function!

共有35個資料型別。其中any-function!和object應該已經可以利用reflect(以及相關函數)取代,只是目前REBOL 3.0尚未禁止使用REBOL 2.x的pick、first等函數進行reflect。

另外有兩個可疑的資料型別,分別是event!與money!,我認為PICK不應該用來處理它們。我已經報告到Bug資料庫了。

閱讀REBOL源碼

我記得十多年前學習Borland OWL時,由於參考資料不多,我必須閱讀源碼才能獲得更進一步的資訊。當時意外的收穫是,我的C++功力大增。

當參考資料不足時,我們就必須閱讀源碼。最近我開始感覺到有閱讀REBOL源碼的必要,因為REBOL 3.0正在開發階段,大家不是很有空去寫DocBase的文件,而DocBase的文件我已經看得差不多了。

除了action和native之外,其實REBOL將所有的源碼都公開。你可以利用source函數或probe函數取得源碼。或者用下面的方式,將全部的源碼都輸出到一個檔案(例如C:\R-Source.txt):

write %/c/R-Source.txt to-binary mold system

請在「完全乾淨」的REBOL console在做上面的動作,也就是說,開始一個新的REBOL console,然後馬上做這個動作。如果你先執行過一些敘述,可能會造成你的系統環境受到污損(contaminated)。

這個檔案雖然不小,但是其中有相當多重複的地方。例如system/contexts/system、system/contexts/export、system/contexts/current、system/words是完全一樣的內容,將重複的內容刪除之後,你會發現檔案變小許多。

然後你就可以開始閱讀REBOL的源碼了。閱讀大師寫的源碼,有助於自己未來成為大師。

2008年6月15日

China Developing Interest in REBOL

According to Axlea, for the past week, China seems very interested in REBOL.

  • REBOL.COM ( 6.0 % -> 7.5 % )
  • REBOL.NET ( ? -> 16.8 % )
  • REBOL.ORG (10.2 % -> 13.6 % )

Wow, not bad.

不使用VID,也可以寫出GUI程式

REBOL有意打造VID(Visual Interface Dialect),成為適合大多數人使用的GUI程式設計方式。從許多Carl的Blog文章中,以及Gabriele(負責實踐與設計VID的人)目前的設計成果,我深切相信這樣的目標確實可以達成。

一方面基於想要「自我挑戰」,另一方面當作學習REBOL 3.0過程的歷練,今年一、二月的時候,我不使用VID,而直接使用GOB和View,寫過數十KB的GUI程式。這樣的經驗讓我體會到:「如果不使用VID,設計GUI會很辛苦」,所以對大多數的人來說,我並不建議這麼做。

但我還是準備自己從頭打造自己的GUI,主要是因為,我希望我的GUI能很動態(dynamic),且支援雙向文字(bi-directional text)。我已經花了相當多時間進行相關的Layout Manager程式編寫與除錯,也獲得一些基本的成果,但是最近因為工作的關係,這件任務可能必須拖延到今年第四季,我才有時間繼續。但是在這之前,我會持續加強我自己的REBOL 3.0程式能力,Prepare for prime time。

2008年6月14日

map、remove-each、foreach

許多Functional Language(例如Erlang與F#)都具有很方便的map、filter、iter函數,可以將一個List,對應到另一個List。在REBOL 3.0中,也有類似的函數,分別是map、remove-each、foreach。比較大的差異是,其他語言是利用Higher-Order Function做到的,但REBOL是用Block做到的。

Event Handler

Event Handler是一種物件,具有三個word,分別是name、priority、handler,如下面的範例所示:
make object! [
name: 'view-default
priority: 50
handler: func [event] [
print ["view-event:" event/type event/offset]
switch event/type [
close [quit]
key [if event/key = escape [quit]]
]
show event/window
none ; we handled it
]
]

一般我們會在View函數中,利用下面的方式指定Event Handler:
view/options window [
handler: [
name: 'my-handler
priority: 100
handler: func [event] [
if event/type = 'down [quit]
]
]
]

注意,在上面的例子中,我們並未建立物件(make object!),而是以區塊(block!)的方式指定此Event Handler的內容。

在REBOL/View系統中,可以同時具有多個Event Handler,將它們串接起來。你可以透過handle-events與unhandle-events來分別增加與刪除Event Handler。注意,handle-events需要的參數是block!,而unhandle-events需要的參數則是物件。

每個Event Handler都具有一個name(名稱),是用來識別Event Handler之用的。想知道目前有沒有具備某名稱的Event Handler,可以透過handled-events?函數。此函數需要傳入一個名稱,它會傳出此名稱的Event Handler(如果有的話)。

利用handle-events函數加入一個Event Handler時,會根據其priority(優先權),決定其位置,優先權越高者放在越前面。例如,現在已經有兩個Event Handler,分別為優先權500的A與優先權200的B,再新增一個優先權為300的C時,最後的結果會是A、C、B,因為C的優先權介於A、B之間。

每個Event Handler都具有一個handler,這是真正處理事件的函數,所以需要事件當參數。特別注意的是,handler的傳出值如果為event,則處理完目前的Event-Handler之後,會繼續處理下一個Event Handler;handler的傳出值如果為none,則表示此事件已經處理完畢(被消耗掉了),不用再往後面的Event Handler遞送。例如,目前依序有A、C、B這三個Event Handler,其中A的handler函數傳出值為none,則C與B都不會發生作用。

2008年6月13日

MAP-INNER and MAP-OUTER are killing me

我一直搞不懂MAP-INNER與MAP-OUTER要如何使用,也沒有文件說明可以閱讀。我在動手寫程式做了一些實驗之後,終於放棄了。我於是定義了下面的函數(取得某個gob的絕對座標):
abs-offset?: func [ gob [gob!] ] [
negate second map-inner gob 0x0
]

以後我打算只使用MAP-EVENT和abs-offset?,不再碰觸令人痛苦不已的MAP-INNER與MAP-OUTER。

MAP-EVENT的用法


REBOL 3.0最近新增一個函數MAP-EVENT,相當好用,可以將滑鼠相關的event,從「最底層的視窗gob」對應到「最上層的gob」。新產生出來的event,其offset與gob屬性都會被修改。通常用法會是這樣:

event: map-event event

假設滑鼠是圖中的黑點,則對應前event/gob會得到紅色的gob,且event/offset會得到黑點和紅色gob左上角的相對位置。對應後,event/gob會得到藍色的gob,且event/offset會得到黑點和藍色gob左上角的相對位置。

Bugfix: parse-url

REBOL 3.0目前剖析URL的時候有Bug,當Path出現「@」、「!」或中文、日文、韓文…的時候,Path的解析出錯。這是相當嚴重的問題,因為檔案名稱中很常出現各國語言,如果用URL表示檔案,就會出問題,例如「file:///C:/音樂/love.mp3」就無法被順利剖析。

剖析URL的函數是decode-url,事實上,此函數會去使用到system/intrinsic/parse-url物件的定義。在parse-url物件定義中,為了要讓path納入「@」與「!」,必須修改path-char的定義修改如下:

path-char: make bitset!
#{00000000EFFFFFF5FFFFFFF57FFFFFEA}

為了要納入各國語言(#"^(100)" - #"^(FFFF)"),更必須將它改成:

path-char: union
make bitset!
#{00000000EFFFFFF5FFFFFFF57FFFFFEA}
charset [#"^(100)" - #"^(FFFF)"]

我改天再把它呈遞給DevBase。

FORMAT與PRINTF的用法

REBOL 3.0提供format與printf函數,這兩個函數的參數完全一樣。Format函數會傳出格式化的字串,而printf則會印出格式化的字串。事實上,printf內部直接呼叫format。

>> source printf
printf: make function! [[
"Formatted print."
fmt "Format"
val "Value or block of values"
][
print format :fmt :val
]]
>>

下面是它們的使用範例:

>> format [ 20 #"/" -10 ] [ %my-rebol-file.r 31023 ]
== "my-rebol-file.r / 31023"

>> printf [ 20 #"/" -10 ] [ %my-rebol-file.r 31023 ]
my-rebol-file.r / 31023
>>

printf與format都需要兩個參數,第一個是格式規則,第二個是代換內容的區塊(block)。格式規則區塊內可以出現四種元素:
  • 正整數:指定內容長度,如果內容超過指定長度,則自動放寬。如果內容不滿指定長度,則「補上空白」,且內容「向左對齊」。
  • 負整數:指定內容長度,如果內容超過指定長度,則自動放寬。如果內容不滿指定長度,則「補上空白」,且內容「向右對齊」。
  • 字元:直接放置一個字元
  • 字串:直接放置此字串

使用format時,還可以透過/pad,指定補上某字元(或字串),而不是補上空白。例如:



>> format/pad [ 20 #"/" -10 ] [ %my-rebol-file.r 31023 ] #"."
== "my-rebol-file.r...../.....31023"

Bugfix: ls與format

我已經修正完畢LS函數和FORMAT函數的Bug,下面列出的是FORMAT的定義。至於LS的定義修改方式很簡單,把LENGTH?用LENGTH+?取代即可。當FORMAT遇到中文時,終於不會出問題了!

LENGTH+?不會計算出字串的字元長度,而是會計算出「相當於多少個half-width字元」的長度。記得把前一篇文章的Full-Wide-Width-charset定義也加進來,因為LENGTH+?會用到這個定義。

我會找一天把修正後的結果透過DevBase遞交給REBOL公司。

length+?: func [str [string!] /local ans ] [
ans: 0
foreach ch str [
either find Full-Wide-Width-charset ch [
ans: ans + 2
] [
ans: ans + 1
]
]
]

format: make function! [[
"Format a string according to the format dialect."
rules {A block in the format dialect. E.g. [10 -10 #"-" 4]}
values
/pad p
/local out val
][
p: any [p #" "]
unless block? :rules [rules: reduce [:rules]]
unless block? :values [values: reduce [:values]]
out: make string! ""
foreach rule rules [
if word? :rule [rule: get rule]
switch type?/word :rule [
integer! [
val: first+ values
val: form :val
pad: (abs rule) - length+? val
if negative? pad [ pad: 0 ]
either positive? rule [
append out :val
append/dup out p pad
] [
append/dup out p pad
append out :val
]
]
string! [append out rule]
char! [append out rule]
]
]
if not tail? values [append out values]
head out
]]

Format函數與寬字元

REBOL 3.0有提供一個函數名為format,用來進行輸出文字的格式化。許多函數都會用到format,例如what、ls。

format一遇到中文就會失靈,輸出格式亂掉,這是因為中文字是寬字元(Wide Character)。為了要修正format的此錯誤,我剛剛把Unicode Standard Annex #11 (East Asian Width) 所附的檔案整理過,找出其中屬於Full-Width與Wide的字元。接下來,就等我(或任何人)有空去修正format函數的定義了。
full-wide-width-charset: charset [
#"^(1100)" - #"^(1159)"
#"^(115F)"
#"^(2329)" - #"^(232A)"
#"^(2E80)" - #"^(2E99)"
#"^(2E9B)" - #"^(2EF3)"
#"^(2F00)" - #"^(2FD5)"
#"^(2FF0)" - #"^(2FFB)"
#"^(3000)" - #"^(303E)"
#"^(3041)" - #"^(3096)"
#"^(3099)" - #"^(30FF)"
#"^(3105)" - #"^(312D)"
#"^(3131)" - #"^(318E)"
#"^(3190)" - #"^(31B7)"
#"^(31C0)" - #"^(31E3)"
#"^(31F0)" - #"^(321E)"
#"^(3220)" - #"^(3243)"
#"^(3250)" - #"^(32FE)"
#"^(3300)" - #"^(33FF)"
#"^(3400)" - #"^(4DB5)"
#"^(4E00)" - #"^(9FC3)"
#"^(A000)" - #"^(A48C)"
#"^(A490)" - #"^(A4C6)"
#"^(AC00)" - #"^(D7A3)"
#"^(F900)" - #"^(FA2D)"
#"^(FA30)" - #"^(FA6A)"
#"^(FA70)" - #"^(FAD9)"
#"^(FE10)" - #"^(FE19)"
#"^(FE30)" - #"^(FE52)"
#"^(FE54)" - #"^(FE66)"
#"^(FE68)"
#"^(FF01)" - #"^(FF60)"
#"^(FFE0)" - #"^(FFE6)"
]

2008年6月12日

對Object!進行Append與Length?

REBOL 3.0現在允許擴充(extend)物件(object!),如下所示:
>> obj: context [ a: 1 ]
== make object! [
a: 1
]
>> append obj [ b 2 c 4]
== make object! [
a: 1
b: 2
c: 4
]

上面的例子中,[ b 2 c 4] 也可以寫成 [ b: 2 c: 4],效果一樣。

如果Block內的word和Object內的word相同時,會造成修改的效果。例如:
>> append obj [c 3]
== make object! [
a: 1
b: 2
c: 3
]

上面的例子,由於obj本來就有c,所以append之後,c的值會被改變。

特別注意,這些處理series的函數中,只有append可以用在物件上,remove、insert並不適用於物件。我猜想可能是因為remove、insert會讓context內部binding對應的方式亂掉。

Block不會先被reduce,如果有reduce的必要,必須自己來。這個時候,通常會使用compose或reduce/no-set,例如:
>> append obj reduce/no-set [ d: 365 + now ]
== make object! [
a: 1
b: 2
c: 3
d: 12-Jun-2009/22:56:29+8:00
]

想知道某物件有多少Word,可以利用Length?,例如:

>> length? obj
== 4

效果相當於「length? words-of obj」。

2008年6月11日

My REBOL 3.0 Wishlist

  1. More text encoders/decoders (Big5, GB2312, ... )
  2. Unicode text rendering
  3. Unicode text input from keyboard
  4. Plug-in ( for calling native C functions )

Because I am developing:

  1. A spider program, which needs item 1
  2. A Chinese text editor (Transma), which needs items 1, 2, and 3
  3. An user interface for an utility, which needs items 1, 2, 3, and 4

2008年6月9日

Who is visiting REBOL.COM?

According to Alexa, REBOL.COM users come from these countries:

  • Germany 23.1%
  • United States 21.6%
  • India 14.3%
  • Canada 8.1%
  • China 6.0%
  • France 1.9%

Also, REBOL.NET users come from these countries:

  • United States 37.9%
  • Germany 19.2%
  • Malaysia 1.7%

And, REBOL.ORG users come from these countries:

  • United States 33.1%
  • Germany 16.7%
  • Iran 11.9%
  • China 10.2%
  • Canada 6.1%
  • France 1.7%
  • Philippines 0.7%

OOP in REBOL 3.0: An Example

REBOL[]


Inherit: func [super [object!] sub [object!]] [
append sub reduce/no-set [parent: super]
]


Invoke: func [ obj member /local ans] [
either ans: select obj member [
ans
] [
attempt [Invoke obj/parent member]
]
]


Super-Class: context [
Member-A: does [ print "Member-A of Super" ]
Member-B: does [ print "Member-B of Super" ]
]


Sub-Class: context [
Inherit Super-Class Self


Member-B: does [ print "Member-B of Sub" ]
Member-C: does [ print "Member-C of Sub" ]
]


Invoke sub-class 'Member-A
Invoke sub-class 'Member-B
Invoke sub-class 'Member-C

2008年6月8日

TCP/IP Client/Server的互動方式



透過「如何用REBOL寫TCP/IP的Client程式」與「如何用REBOL寫TCP/IP的Server程式」,你應該已經可以使用REBOL 3.0寫出TCP/IP的Client/Server系統。這裡是一個綜合的示意圖,展示出DNS、Client Port、Server Listen Port、Server Service Port之間的互動關係。

The Long Tail ?

我在BlogSpot開了兩個Blog,一個是「JerryLovesRebol.BlogSpot.com」,另一個是「RebolLovesJerry.BlogSpot.com」;前者是「言程序」Blog,內容是主流技術與前瞻技術的心得文章;後者是「Rebol This Rebol That」Blog,內容是和REBOL相關的消息與技術心得。我預期,REBOL是比較冷門的技術,所以雖然成立REBOL文章專屬的Blog,我並不預期會有太多人來看。沒人來閱讀我的REBOL文章是無所謂的,就當成是自己的學習筆記。

但結果令我意外,「言程序」的點擊率居然還沒有「Rebol This Rebol That」高。我分析一下原因,可能是「長尾效應」(Long Tail)。

「言程序」Blog大多數的文章其實已經先發表於台灣的iThome週刊與網站,所以大家不見得要到我的Blog來閱讀,而「言程序」的讀者只限於華人,所以算是區域性的Blog。

但「Rebol This Rebol That」Blog就不同了。這裡的文章並未發表於他處,且對REBOL技術感興趣的人「比例」雖然不高,但是全球累積起來的「數量」仍不少。這就是Long Tail!

除非懂中文,否則大多數的人閱讀「Rebol This Rebol That」Blog的時候,會透過「Google Translate」,先翻譯成英文。看他們這麼克難地閱讀,我有一點想用英文寫Blog。但我終究還是沒這麼做,因為顧慮到「Rebol This Rebol That」有一部份的任務是向華人介紹這個優秀的技術,既然如此,還是用中文寫比較恰當。

希望更多人用各種語言(捷克文、法文…)開闢REBOL blog,讓我們一起為REBOL的Promotion努力。

2008年6月7日

如何用REBOL寫TCP/IP的Server程式


在「如何用REBOL寫TCP/IP的Client程式」一文中,我說明了Client程式的寫法,現在這篇文章說明Server程式的寫法,程式碼請參考這篇DocBase文章的Pong Server。重點整理如下:

  1. Server會先Open自己電腦上的一個Listen Port,並開始聆聽(Listen)連線請求。對於REBOL來說,只要呼叫Open函數來開啟伺服器的Port,就會同時進行Listen(你不需要呼叫Listen,也沒有這樣的函數可用)。
  2. 當Client呼叫Open,並指定連到Server的IP和Port時,Server就會收到Accept的事件。處理Accept事件的方式是呼叫First,會得到一個Service Port。Server的Awake函數必須傳出false(表示任務尚未完成),否則會結束Listen,只能服務一人。
  3. Listen Port繼續接受來自其他Client的新連線請求,而Service Port開始和連線上的Client溝通。也就是說,Listen Port和Service Port兩個是不同的Port,且Listen Port只有一個,且可能產生出多個Service Port。
  4. Service Port會進行Read、Write、Close動作(函數),會分別收到Read、Wrote、Close事件。這部分和「如何用REBOL寫TCP/IP的Client程式」一文類似,不再贅述。
  5. 通常Service Port會先呼叫Read(而非Write),以讀取來自客戶的請求內容。

A New VID Prototype – VID 3.4

目前REBOL 3.0的開發焦點在VID。REBOL語言設計者Carl最近在AltME r3-alpha社群討論區表示,為了讓VID具有相當大的客製化(customization)彈性,將對既有的VID雛形系統(Prototype)做出較多深入的改變,然後推出第四個雛形系統(VID 3.4)。

Functional Programming

I've just published a series of FP-related articles in my non-REBOL blog.

Functional Programming (1) Language is functional again.
Functional Programming (2) Why Functional Programming.
Functional Programming (3) FP is as FP does

2008年6月6日

DevBase Is Around the Corner

剛剛釋出REBOL 3.0 Alpha 2.100.13,這個版本所做的改變,大多是為了VID和REBOL/Services的需求,而VID和REBOL/Services是DevBase的基石。DevBase是一個Client-Server的系統,Client的使用者介面會用到VID,Client和Server的溝通會用到REBOL/Services。

為了要擴大社群對REBOL開發的貢獻,在REBOL 3.0中,DevBase會擔負起這方面的角色,讓社群可以檢視REBOL的程式碼,修改,呈遞,最後由關鍵的兩三個人決定採用與否。目前DevBase已經在運行中,但是是透過REBOL 2.x。最新釋出的REBOL 3.0 Aplah上頭,也開發了一套DevBase,將會試圖取代REBOL 2.x的DevBase。

REBOL大將Gabriele在AltME上面的r3-alpha社群中提到,經他測試,REBOL 3.0的DevBase已經可以順利運作。我相信,這個月(說不定下週)如果他們發現DevBase一切都穩定的話,就會釋出這個DevBase版本。

GUI Made Easy

許多學術領域的專家或教授,會使用編程語言,將自己的研究結果,寫成特殊領域的程式,例如氣象、水利、天文、自然語處理...等。但是這些程式往往只有文字的輸入和輸出。他們很希望讓程式具有美觀的GUI介面,但是這部分他們做不來,因為「大多數的GUI程式設計都很複雜」。不信的話,你可以看看MFC、WinForms、WPF、Tk、Java Swing...,沒有一個是簡單的。對這些專家來說,「沒有GUI」是他們程式的一大缺失。

REBOL 3.0試圖利用VID(Visual Interface Dialect)做出相當容易寫的GUI程式,讓任何人都可以輕易地寫出GUI程式。如果你用過REBOL 2.x的VID,你應該已經體會到VID的方便。而在REBOL 3.0中,VID會更美觀、更有彈性、且支援Unicode。你可以閱讀Carl的三篇Blog文章:

2008年6月5日

Pathologic Obesity in Software

許多人第一次看到REBOL,都會為它小巧的體積感到相當驚訝,畢竟在軟體產業,肥胖已經成了一種通病。幾個月前,我發表了一篇「Pathologic Obesity in Software」(軟體的病態性肥胖),得到不少讀者的共鳴。你可以到這裡閱讀這篇文章。

OOP in REBOL ?

根據REBOL語言設計者Carl在部落格文章中的說法,他以前曾經相當著迷於OOP (Object-Oriented Programming),但是現在已經不再是如此了。雖然Carl不是特別喜歡OOP,但是在REBOL 3.0的設計中,我們依然可以找到少數OOP的影子。最明顯的例子是Scheme與Port。

從OOP的角度來看,Scheme就像是Class,Port就像是Instance;Scheme只有一份,被所有的Port共用。另外,Scheme也像是Super-class;而Port像是Sub-class,Port所提供的action方法可以覆蓋(shadow)掉Scheme的action;而Port如果沒有實做(implement)自己的action,就會使用Scheme的版本。

如果你想在REBOL中寫OOP的程式,那麼你可以參考Scheme與Port的源碼。關於OOP,你可以參考我的這篇文章

如何用REBOL寫TCP/IP的Client程式


我還記得約14年前讀大學時寫過的第一個網路程式WinBrick(Windows 3.1的網路對打俄羅斯方塊Tetris),當時使用WinSock API。後來讀研究所時用Java Networking API寫VOD(Video-On-Demand)系統,Java的API比C的WinSock稍微簡單一些,但程式還是不好寫。REBOL的TCP/IP程式設計方式,比起上述兩者,都更簡單。你可以閱讀Carl的這篇部落格文章
經過自己手動實驗之後,我簡單地整理一下重點:
  1. DECODE-URL用來將URL(例如:http://www.rebol.com/index.htm)拆解成區塊(例如[scheme: 'http host: "www.rebol.com" port-id: 80 path: "/index.htm"]),但是DECODE-URL有時候會拆解錯誤,如果與到拆解錯誤的狀況,就必須自己處理。如這個例子所示。
  2. 上述的區塊可以當作OPEN的參數,就可以連接到伺服器。
  3. 如果host是host-name,則程式會先連到DNS,查詢(lookup)伺服器的ip,所以會收到「lookup」事件,這個時候,必須再次呼叫OPEN。
  4. 如果host本來就是host-ip,則不會有上述的步驟(不會收到lookup事件)。
  5. 當程式收到「connect」事件時,表示程式對伺服器的連線請求,已經被伺服器接受(accept),可以開始進行資料通訊,這個時候,通常是由Client先傳送資料(服務請求)給伺服器,而伺服器先聆聽(listen)。因此,在收到connect事件時,Client通常會呼叫WRITE。
  6. 當資料寫入完畢,Client會收到「wrote」事件。這個時候,通常會呼叫READ,進行資料的讀取。當然,你也可以會直接呼叫CLOSE,主動關閉網路連線,這由你的通訊協定(protocol)決定。
  7. 呼叫READ,就會從緩衝區(buffer)讀取資料,一旦讀取完目前緩衝區的資料,就會收到read事件(這個read是「過去分詞」,表示「已經讀取了」)。
  8. 通常你需要解析剛剛讀到的資料,以瞭解資料是否已經讀完。如果尚未讀完,你必須繼續呼叫READ來繼續讀取資料。如果已經讀完,你可能會呼叫WRITE傳資料給伺服器,或者呼叫CLOSE來關閉連線。
  9. 當連線的兩端中,任何一端先呼叫CLOSE來關閉連線時,Client都會收到「close」事件。當client收到這個事件的時候,要做一些收尾的動作。
  10. awake的傳出值如果為false,表示port尚未處理完畢;如果為true,表示port處理完畢,會立刻結束這個port。通常我們會在遇到通訊協定格式錯誤、事件無法識別、或收到close事件之後,傳出true,其他狀況則傳出false。
  11. 為了避免任何動作做太久,我們通常會在外部呼叫WAIT,等候一段Timeout的時間。

2008年6月4日

我對REBOL 3.0的體會

我注意REBOL 3.0的開發過程,算是種「敏捷開發」(Agile Process),隨時都在進行動態的調整。如果開發者很多的話,這樣的敏捷開發恐怕不可行,一定要按部就班進行。最後的結果可能就是得到一個功能普通、體積肥大、且效能不佳的軟體。

由於REBOL 3.0的核心開發人力是Carl本人,所以他可以隨時動態調整開發項目的優先次序。例如原本Unicode要在3.1才加入,後來提前。這樣的例子屢見不鮮。

Carl在開發的過程,有一些理念上的堅持,那就是REBOL必須「簡單」、「體積小」、「效能」。任何作法只要違反這幾點,都會被排除,不予採納。所以即使REBOL已經開發這麼久了,且圖形和網路的功能都加進來了,還是只有約600K。且執行效率相當好,語法也維持一貫的簡單。

在這個年頭,軟體要保持「簡單」、「體積小」、「效能」,談何容易,但REBOL 3.0做到了。加上強大與創新的功能,這真的是一個好語言。

2008年6月3日

REBOL 3.0的進度


如果你關心REBOL 3.0現在的開發進度,你可以看一下這張示意圖。這是我個人認知的進度狀況,並非官方的資料,所以不保證精確。

2008年6月2日

暫時擱置Transma計畫

想用REBOL 3.0,繼續動手完成我之前的計畫(Transma),無奈REBOL 3.0尚未支援下面的功能:

  1. 從鍵盤取得Unicode輸入
  2. 將Unicode文字輸出到GUI
  3. 呼叫外部DLL

看樣子我只好持續把這個計畫擱置下來。

2008年5月30日

使用Console(二)


REBOL預計會有兩個Console,目前已完成的只有文字模式的DevStdIO,而圖形模式的DevConsole短時間之內是不會出現的,看樣子我們得使用DevStdIO好一段時間。「使用Console」 這篇文章有介紹一些用法。除此之外,如果你要使用「複製 / 貼上」功能的話,你必須啟用「快速編輯模式」和「插入模式」。作法如下:

  1. 用滑鼠右鍵點選REBOL Console視窗的標題列,叫出系統選單。
  2. 從選單中選取「內容」。你會看到附圖的視窗。
  3. 將兩個編輯選項(「快速編輯模式」和「插入模式」)都選取。然後按下「確定」。

只要有上面的設定,你就可以用滑鼠選取文字,用滑鼠右鍵複製反白區的文字到剪貼簿;在沒有反白區的情況下,按下滑鼠右鍵可以將剪貼簿的文字貼到Console上。


2008年5月28日

REBOL剖析URL時的問題

昨天用REBOL寫了一個Spider程式,要下載某些網站的內容。這些網站所使用到的URL中,有出現「!」與「@」等特殊字元,REBOL 2.x在剖析URL的時候,會因此判斷錯誤。因此。我必須自己剖析URL,然後利用下面的方式讀取資料:

read [
scheme: 'http
host: "www.some-host.net"
port-id: 80
path: "database/!data.html"
]

2008年5月26日

還是要進化

我一直樂於推薦REBOL,許多人因此接觸REBOL之後,反應卻都不太妙。其中有相當大比例的人,對於REBOL的語法感到相當不適應。因此,他們的結論往往是「Jerry的判斷有問題,推銷一個不是很好的東西」,更嚴重的說法是「REBOL是邪魔歪道」。

我只能說,「一個東西的理念領先人家太多,也不見得是好事」,因為大家的程度跟不上,當然也就不識貨。太先進的東西會被原始人當作垃圾。我只好讓他們繼續留在原地當原始人,我先一步進化去了。

2008年5月25日

Torn between two lovers

I am an F# lecturer in the Taiwan MSDN Forum. Call me a traitor, but I got bills to pay. I like F#, but I love REBOL from the bottom of my heart. I wish I could have a REBOL-related full-time job, so I wouldn't have been switching between REBOL and the others. Seriously.

2008年5月23日

REBOL最新動態:VID、Desktop、REBOL/Services

在Unicode核心開發告一段落,且網路介面與圖形介面也整合進來之後。REBOL科技公司CTO Carl Sassenrath表示,近期的目標是將REBOL桌面和REBOL/Services整合進來,應該不久之後就可以對Alpha小組釋出這個版本,開始測試。

2008年5月20日

從OOP到FP

如果你只習慣OOP(Object-Oriented Programming)的編程方式,現在想改用FP(Functional Programming)的方式,但不知道要從何下手,你可以參考我的這篇文章。

利用Delect設計方言

REBOL具有豐富的Literal,除了可以提高程式的「可讀性」和「可寫性」之外,也可以方便設計方言(dialect,也就是所謂的DSL)。這一點是其他語言遠比不上REBOL的。

以往,設計REBOL方言的方式,是利用Parse函數。在REBOL 3.0之後,又多了一個方式,就是利用Delect。Delect是DEcode diaLECT的意思。REBOL內部的方言,許多都是利用Delect做出來的。

大多數的方言,都會定義一些關鍵字(keyword)。每個關鍵字後面可以有0到多個參數。有些時候,這些參數出現的次序無所謂,因為透過型別就可以推測出參數的意義。例如:「Circle 100x100 50」與「Circle 50 100x100」,雖然100x100和50的出現次序不同,但是都可以讓我們認為這是要表達一個「圓心在100x100,半徑是50」的圓。所以處理這類方言的時候,值的次序不重要,重要的是值的型別,這種方言用Delect設計會相當容易。

良好的方言,應該要方便學習與使用。放寬參數的次序,可以幫助使用者學習與使用方言。以上面的Circle例子來說,使用者就不需要牢記,到底是先寫半徑再寫圓心,還是先寫圓心再寫半徑。

關於Delect的用法,請見DocBase的這篇說明。我以後也會寫文章說明Delect的詳細用法。

2008年5月18日

REBOL, Erlang, F#

我在這篇文章中,比較了REBOL、Erlang、F#這三個語言的能力,你可以參考閱讀。為什麼我要不斷地把REBOL拿來和其他語言做比較?因為當別人用Google搜尋「Erlang vs ...」或「F# vs ...」的時候,很可能就會看到這一篇文章,然後會基於好奇心而去研究一下REBOL,然後喜歡上REBOL。

2008年5月16日

好事多磨

我最近忙工作的事,連幫忙REBOL測試的時間都沒了。本來想要透過DevBase幫忙改寫一些REBOL函數,也因此遲遲未動手。

近年來,REBOL面臨一個嚴重的問題:核心開發人力不足。這使得REBOL具有相當先進的理念,美好的願景,但是卻進展步調緩慢。每次我們看到其他軟體又推出新版本了,裡面有一些不錯的想法,我們不免緊張心急,因為這些想法都是REBOL很早已前都早就想到的,但是人力不足,讓我們眼睜睜看著別人捷足先登。其他公司的產品使用的人力可能是REBOL的數十倍。

真正「有能力」且「有意願」協助REBOL的人已經不多,加上符合條件的人還是得為現實生活的帳單奮鬥,只能提供相當有限的時間協助REBOL開發。大多數的開發還是集中在Carl等少數幾個人身上。這使得他們的Load相當重。背負著大家的期望,壓力也相當大。

如果REBOL能夠Open Source,或許就可以解決開發人力的問題。但是Carl為了打造一個理想中的語言,並不願意這麼做,他認為這會造成語言的分歧。因此他試圖透過一個「半開放源碼」的作法,來維持核心不被更動,但是可以讓其他人幫忙將REBOL輕易地移植到各個平台,並為REBOL加進各種擴充能力。這也是REBOL 3.0的一部份設計理念。

但是在REBOL 3.0正式釋出之前,我們外人能做的不多。我們只能希望,有人能將資金投入REBOL,來增聘一些寫程式高手,早一點將REBOL 3.0做出來。

… 總是好事多磨。

2008年5月7日

REBOL 3.0 - 2.100.11

REBOL 3.0今天釋出2.100.11。這個版本整合了Unicode的核心和圖形系統,並修正了一些先前提出的錯誤。接下來,Carl會和Gabriele(VID設計者)進行VID的檢討,如果順利的話,很快就可以有VID(視覺設計方言)可用了。

2008年5月2日

RHAE & RRTE


REBOL 3會推出一個適合嵌入(embed)到其他應用的版本,此版本分成兩部份:
  1. RRTE(REBOL Runtime Environment)
  2. RHAE(REBOL Host Application Environment)

RRTE是封閉源碼的DLL,RHAE則是將RRTE包裝(wrap)起來的薄薄一層程式,RHAE開放源碼(應該是使用C語言)。你的程式如果想嵌入REBOL環境,成為REBOL Host,就必須實踐RHAE所宣告的函數。

2008年4月29日

整合Unicode核心和圖形介面的版本

REBOL 3今天釋出內部版本2.100.8,將圖形次系統加入Unicode核心中。體積從221 KB變成523 KB。目前這個版本尚欠缺一些常用的網路通訊協定,欠缺一些編碼解碼器,圖形介面的部分也尚未支援Unicode文字的展示。詳細的說明,請見http://www.rebol.net/wiki/R3_Releases

2008年4月26日

關於meta-programming

我在這篇文章中提到,對於DSL的支援良窳,我的三個基本的判斷原則是: (1) 能否輕易地設計DSL (2) 能否設計出有彈性的DSL (3) 能否讓DSL和GPL使用上合為一體。我覺得REBOL這三方面都做得相當好,是最棒的meta-programming語言。用REBOL設計出來的DSL,稱為Dialect。

2008年4月20日

Web顏色名稱

REBOL沒有支援標準的Web顏色名稱(共140個)。如果你想使用這些標準顏色名稱,你可以使用下面的定義。從這個顏色名稱的定義中,可以看出文化差異。譬如鮭魚、石板、綠松石、北美印第安人穿的鹿皮軟鞋都可當作顏色名稱,和我們東方社會的習慣差異很大。

詳細的顏色,請參考這個網頁
do web-colors: [ {愛麗絲藍} AliceBlue: 240.248.255 {古董白} AntiqueWhite: 250.235.215 {水} Aqua: 0.255.255 {海藍寶石} Aquamarine: 127.255.212 {蔚藍} Azure: 240.255.255 {米色} Beige: 245.245.220 {陶器} Bisque: 255.228.196 {黑} Black: 0.0.0 {白杏仁} BlanchedAlmond: 255.235.205 {藍} Blue: 0.0.255 {藍紫} BlueViolet: 138.43.226 {褐} Brown: 165.42.42 {木材} BurlyWood: 222.184.135 {軍官藍} CadetBlue: 95.158.160 {淡黃綠} Chartreuse: 127.255.0 {巧克力} Chocolate: 210.105.30 {珊瑚} Coral: 255.127.80 {矢車菊藍} CornflowerBlue: 100.149.237 {玉米穗絲} Cornsilk: 255.248.220 {緋紅} Crimson: 220.20.60 {青綠色} Cyan: 0.255.255 {暗藍} DarkBlue: 0.0.139 {暗青} DarkCyan: 0.139.139 {暗金黃桿} DarkGoldenRod: 184.134.11 {暗灰} DarkGray: 169.169.169 {暗灰} DarkGrey: 169.169.169 {暗綠} DarkGreen: 0.100.0 {暗卡其} DarkKhaki: 189.183.107 {暗洋紅} DarkMagenta: 139.0.139 {暗橄欖綠} DarkOliveGreen: 85.107.47 {暗橙} DarkOrange: 255.140.0 {暗蘭花} DarkOrchid: 153.50.204 {暗紅} DarkRed: 139.0.0 {暗鮭魚} DarkSalmon: 233.150.122 {暗海綠} DarkSeaGreen: 143.188.143 {暗石板藍} DarkSlateBlue: 72.61.139 {暗石板灰} DarkSlateGray: 47.79.79 {暗石板灰} DarkSlateGrey: 47.79.79 {綠松石} DarkTurquoise: 0.206.209 {暗紫色} DarkViolet: 148.0.211 DeepPink: 255.20.147 DeepSkyBlue: 0.191.255 DimGray: 105.105.105 DimGrey: 105.105.105 DodgerBlue: 30.144.255 FireBrick: 178.34.34 FloralWhite: 255.250.240 ForestGreen: 34.139.34 Fuchsia: 255.0.255 Gainsboro: 220.220.220 GhostWhite: 248.248.255 Gold: 255.215.0 GoldenRod: 218.165.32 Gray: 128.128.128 Grey: 128.128.128 Green: 0.128.0 GreenYellow: 173.255.47 HoneyDew: 240.255.240 HotPink: 255.105.180 IndianRed: 205.92.92 Indigo: 75.0.130 Ivory: 255.255.240 Khaki: 240.230.140 Lavender: 230.230.250 LavenderBlush: 255.240.245 LawnGreen: 124.252.0 LemonChiffon: 255.250.205 LightBlue: 173.216.230 LightCoral: 240.128.128 LightCyan: 224.255.255 LightGoldenRodYellow: 250.250.210 LightGray: 211.211.211 LightGrey: 211.211.211 LightGreen: 144.238.144 LightPink: 255.182.193 LightSalmon: 255.160.122 LightSeaGreen: 32.178.170 LightSkyBlue: 135.206.250 LightSlateGray: 119.136.153 LightSlateGrey: 119.136.153 LightSteelBlue: 176.196.222 LightYellow: 255.255.224 Lime: 0.255.0 LimeGreen: 50.205.50 Linen: 250.240.230 Magenta: 255.0.255 Maroon: 128.0.0 MediumAquaMarine: 102.205.170 MediumBlue: 0.0.205 MediumOrchid: 186.85.211 MediumPurple: 147.112.216 MediumSeaGreen: 60.179.113 MediumSlateBlue: 123.104.238 MediumSpringGreen: 0.250.154 MediumTurquoise: 72.209.204 MediumVioletRed: 199.21.133 MidnightBlue: 25.25.112 MintCream: 245.255.250 MistyRose: 255.228.225 Moccasin: 255.228.181 NavajoWhite: 255.222.173 Navy: 0.0.128 OldLace: 253.245.230 Olive: 128.128.0 OliveDrab: 107.142.35 Orange: 255.165.0 OrangeRed: 255.69.0 Orchid: 218.112.214 PaleGoldenRod: 238.232.170 PaleGreen: 152.251.152 PaleTurquoise: 175.238.238 PaleVioletRed: 216.112.147 PapayaWhip: 255.239.213 PeachPuff: 255.218.185 Peru: 205.133.63 Pink: 255.192.203 Plum: 221.160.221 PowderBlue: 176.224.230 Purple: 128.0.128 Red: 255.0.0 RosyBrown: 188.143.143 RoyalBlue: 65.105.225 SaddleBrown: 139.69.19 Salmon: 250.128.114 SandyBrown: 244.164.96 SeaGreen: 46.139.87 SeaShell: 255.245.238 Sienna: 160.82.45 Silver: 192.192.192 SkyBlue: 135.206.235 SlateBlue: 106.90.205 SlateGray: 112.128.144 SlateGrey: 112.128.144 Snow: 255.250.250 SpringGreen: 0.255.127 SteelBlue: 70.130.180 Tan: 210.180.140 Teal: 0.128.128 Thistle: 216.191.216 Tomato: 255.99.71 Turquoise: 64.224.208 Violet: 238.130.238 Wheat: 245.222.179 White: 255.255.255 WhiteSmoke: 245.245.245 Yellow: 255.255.0 YellowGreen: 154.205.50]

2008年4月18日

DevCon 2008

據說原本預估9月要在布拉格舉辦的DevCon已經取消。要等到REBOL 3.0完成之後,才會舉辦。

2008年4月17日

About the Erlang VS. REBOL Series

I know this series is a mess. I promise to write an organized version in English when I have time. It's a rain check.

2008年4月16日

Erlang VS. REBOL, Part VI

* if 與 case
Erlang的if 相當於REBOL的case;Erlang的case相當於REBOL的switch。Erlang的if與case都可以搭配guard使用,但REBOL沒有guard的觀念。

* 將資料放入List
在Erlang中,將資料插入List頭部,是最有效率的作法。但REBOL則是插入尾部才有效率。

* Exception
在Erlang程式中主動發出例外,有三種方式
exit(Why)
throw(Why) 類似Java的可處理例外
erlang:error(Why) 類似Java的Error(嚴重的狀況)

REBOL的例外只有一種,類似上面第二種。

* try/catch
Erlang的try/catch語法比REBOL豐富,類似Java的try/catch/finally。
try FuncOrExpressionSequence of
Pattern1 [when Guard1] -> Expressions1;
Pattern2 [when Guard2] -> Expressions2;
...
catch
ExceptionType: ExPattern1 [when ExGuard1] -> ExExpressions1;
ExceptionType: ExPattern2 [when ExGuard2] -> ExExpressions2;
...
after
AfterExpressions
end


* Stack Trace
erlang:get_stack_trace()可以取得目前的堆疊。REBOL具有類似功能的stack函數。

* Binary
Erlang的binary寫法類似List,但[…]改用<< >>,且每個值都必須介於0到255之間,例如:
<<5,10,20>>

* Bit Syntax
Erlang具有位元語法,可以方便二元檔案或網路封包處理。例如:

> Red = 2
> Green = 61
> Blue = 20
> Mem = << Red:5,Green:6,Blue:5 >>
<<23,180>>

Bit Syntax甚至可以讓你指定big-endian或little-endian或native-endian、signed或unsigned、integer或float或binary。

* 使用者自訂的模組屬性
REBOL和Erlang都允許使用者自行定義的模組屬性。
Erlang:
-my-info(taiwan).

REBOL:
REBOL[

My-info: 'Taiwan
]


* Apply
REBOL和Erlang都支援apply函數。

* Preprocessor
Erlang具有前處理器,用來將檔案含括(include)進來。例如:
-include(FileName)

* Macro
Erlang具有Macro,例如:

-define(macro1(X, Y), {a, X, Y}).
foo(A) ->
?macro1(A+10, b)


會變成

foo(A) ->
{a,A+10,b}.



Erlang VS. REBOL, Part V

* List Comprehension
Erlang支援List Comprehension;REBOL雖然沒有List Comprehension,但是要設計一個方言來達到List Comprehension,應該不難。

Erlang:

> L = [1,2,3,4,5]
> [2*X || X <- L]
[2,4,6,8,10]

下面是另一個例子,利用List Comprehension寫出簡短的Quick Sort:
lib_misc.erl
lib_misc.erl
qsort([]) -> [];
qsort([Pivot|T]) ->
qsort([X || X <- T, X < Pivot])
++ [Pivot] ++
qsort([X || X <- T, X >= Pivot]).

1> L=[23,6,2,9,27,400,78,45,61,82,14].
[23,6,2,9,27,400,78,45,61,82,14]
2> lib_misc:qsort(L).
[2,6,9,14,23,27,45,61,78,82,400]


再來一個例子:

lib_misc.erl
perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
> lib_misc:perms("123").
["123","132","213","231","312","321"]
> lib_misc:perms("cats").
["cats", "cast", "ctas", "ctsa", "csat", "csta", "acts", "acst", "atcs", "atsc", "asct", "astc", "tcas", "tcsa", "tacs", "tasc", "tsca", "tsac", "scat", "scta", "sact", "satc", "stca", "stac"]


List Comprehension的語法之簡潔,威力之強大,實在有一點恐怖。

* 等於
Erlang的等於符號是「=:=」與「==」;REBOL的等於符號是「=」與「==」。

* Bitwise運算子
Erlang具有下列的Bitwise運算子bnot、band、bor、bxor、bsl、bsr;REBOL不具有bitwise運算子,但有某些型別和函數具有類似功能,例如logic!型別、shift函數。

* Short-Circuit運算子
Erlang具有orelse和andalso這兩個short-circuit運算子。REBOL的AND和OR則「不是」Short-Circuit,但ANY和ALL可以達到Short-Circuit的效果。

* Guard
Erlang某些地方可以用到Guard,以補模式比對的不足。Guard的運算結果為true或false。

REBOL沒有模式比對的概念,所以沒有Guard。

*Record
Erlang利用Record來模擬物件,Record其實就是Tuple,只是每個元素都有對應的名稱。REBOL的Object和Erlang的Record很類似。

定義record時利用「-record」命令。存放Record定義的檔案,副檔名為.hrl。下面是一個範例:
Records.hrl
-record(todo, {status=reminder,who=jerry,text}).

上面的檔案定義了一個todo記錄結構,使用rr函數(read record)可以將它載入:
> rr("records.hrl").

建立的方式:
> X=#todo{}.
#todo{status = reminder,who = joe,text = undefined}
> X1 = #todo{status=urgent, text="Fix errata in book"}.
#todo{status = urgent,who = joe,text = "Fix errata in book"}
> X2 = X1#todo{status=done}.
#todo{status = done,who = joe,text = "Fix errata in book"}
> X2#todo.text.
"Fix errata in book"

Erlang VS. REBOL, Part IV

* 匯出全部

Erlang允許個模組將自己的函數全部匯出:
-compile(export_all)

REBOL雖然沒有這樣的作法。但是REBOL模組中沒有被匯出者,依然可以透過explicit的方式,被外部使用,例如:
my-module/non-expoted-func

* Arity
對Erlang來說,Arity(引數的個數)是函數的一部份,函數area/1表示函數名為area,且具有一個引數。而area/1可能又有許多個clause。同一個Erlang模組內,即使函數名稱一樣,但Arity不同,那麼就是完全不同的函數。Erlang常常會使用相同名稱但不同Arity的函數,當作輔助函數(helper)。

REBOL函數不具有clause,也不強調Arity。

* 匿名函數
Erlang和REBOL都支援匿名函數。

Erlang:
fun(x) -> x * 2 end.

REBOL:
func[x] [x * 2]

但是Erlang的匿名函數可以有多個clause,REBOL則不行。

* 較高次方函數
Erlang和REBOL也都支援「較高次方」函數(higher-order function),也就是「以函數為引數或傳出值」的函數。較高次方函數是Function Programming很重要的特色。

Erlang大量使用「較高次方」函數,但REBOL卻很少使用「較高次方」函數。

* 把函數當引數用
Erlang有相當多list-at-a-time函數,可以將對整個list的操作,當作單一個概念。簡化我們的思考,也簡化我們的程式。REBOL也可以做到這一點,設計出block-at-a-time函數,只是目前好像這樣的函數並不多(但MAP是個例外)。而且許多時候,REBOL都是利用block來當作函數(不像函數那麼有彈性)。這一點,我比較喜歡Erlang。

Erlang:
> Double = fun(x) -> x * 2 end.
> L = [1,2,3,4]
> list:map(Double, L).
[2,4,6,8]

* 把函數當傳出值用
Erlang可以將函數當傳出值,例如:
> Fruit = [apple,pear,orange].
> MakeTest = fun(L) -> (fun(X) -> lists:member(X, L) end) end.
> IsFruit = MakeTest(Fruit).
> IsFruit(apple).
true
> IsFruit(beef).
false

REBOL必須透過closure,才能做到這件事:
>> Fruit: [apple pear orange]
>> Make-Test: closure [L] [ func[X] [found? Find L X] ]
>> Is-Fruit: Make-Test Fruit
>> Is-Fruit 'apple
== true
>> Is-Fruit 'beef
== false

不管是Erlang或REBOL,「把函數當傳出值用」都很少見。但是利用這種方式建立自己的流程控制,可以相當有效地減少程式體積,有時候甚至會讓程式更清晰易讀。

Erlang VS. REBOL, Part III

* String
Erlang String其實就是List,所以下面三者雖然寫法不同,但意義完全相同。
[65, 66, 67]
[$A, $B, $C]
"ABC"

Erlang的字串使用Latin-1(ISO-8859-1),能表示的字元相當有限。REBOL的字串使用Unicode,可以表示任何國家的文字。

* Un-bind
Erlang有一個函數f(),可以用來將所有的binding解除。REBOL不具有這樣的函數。

Erlang:
> X = 10.
10
> X.
10
> f().
> X.
* 1: veriable 'X' is unbound

*副檔名(file extension)
Erlang源碼使用的副檔名為.erl;REBOL則是.r。Erlang編譯後的模組檔名為.beam;REBOL不需要編譯。你可以使用c()函數來編譯Erlang模組。

*模組定義
Erlang和REBOL都可以定義模組。

Erlang在源碼檔案前這麼寫:
-module(geometry).
-export([area/1]).
-vsn(1234).

REBOL在源碼檔案前這麼寫:


REBOL [
Name: geometry
Version: 1.0.0
Type: module
Export: [area-rect area-circle]
]
對Erlang來說,沒有被匯出者,無法在別的地方使用。但是對REBOL來說,即時沒有被匯出,依然可以在其他地方被使用,只要註明模組為何即可,例如:
my-module/non-exported-word

* 函數與Clause
Erlang的函數可以包含許多Clause。Clause之間用英文分號隔開,最後一個Clause用英文句號結束。例如:

area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R}) -> 3.14159 * R * R.

呼叫函數的時候會先進行模式比對(Pattern Matching),找出適合的Clause呼叫。

REBOL不具有Clause的概念。上面的程式要改寫成:
Area-Rect: func [ Width Ht ] [ Width * Ht ]
Area-Circle: func [ R ] [ 3.14159 * R * R ]

* 編譯與呼叫模組
Erlang程式要編譯成模組才能執行,你可以使用c()函數來編譯Erlang模組。呼叫某Erlang模組的某函數時,使用英文冒號當作模組和函數之間的分隔符號。

Erlang:
1> c(geometry).
{ok,geometry}
2> geometry:area({rectangle, 10, 5}).
50
3> geometry:area({circle, 1.4}).
6.15752

你也可以利用下面的方式,將模組匯入(import),就可不用在呼叫函數時前面冠上模組名稱和冒號:
-import(geometry, [area/1])
area({rectangle, 10, 5})


REBOL不需要編譯。可以利用import將模組匯入。呼叫函數時,可以用implicit的方式(不註明模組),也可以用explicit的方式(註明模組,用斜線當分隔符號)

mod: import ' geometry
area-rect 10 20
mod/area-rect 10 20

Erlang VS. REBOL, Part II

* Word與Atom
Erlang所謂的Atom,其實就是REBOL所謂的Word。Erlang規定Atom只能使用小寫字母、底線(_)、阿拉伯數字。REBOL對於Word沒有這樣的規範,REBOL的Word甚至可以使用各國文字(因為REBOL有支援Unicode)。Erlang的Atom沒有繫結值(也可以視為繫結到自己),而變數有繫結值;REBOL則不管有沒有繫結值,一律都是Word。

* Erlang Tuple – REBOL Block
Erlang所謂的Tuple,其實就是REBOL的Block。Erlang Tuple使用{},且元素之間要用逗號(,)隔開;REBOL使用[]。

Erlang:
{"Jerry", "Tsai", 1.77 }

REBOL:
["Jerry" "Tsai" 1.77 ]

* Object
許多Erlang使用Tuple的場合,在REBOL中不見得要用Block,可以改用Object,但Erlang不支援Object。

* Unification
使用模式比對(Pattern Matching)來取出值,稱為Unification。Erlang有支援Unification。REBOL則必須透過Set函數。

Erlang:
P = {"Jerry", "Tsai", 1.77 }
{F, L, H} = P
現在F被繫結到"Jerry";L被繫結到"Tsai";H被繫結到1.77。

REBOL的寫法:
P: ["Jerry" "Tsai" 1.77 ]
Set [F L H] P

* Erlang List – REBOL Block
除了Tuple,Erlang還有List(清單)。你可以把Tuple(值組)想成是C語言的Struct,List想成是動態陣列。Erlang所謂的Tuple和List,對REBOL來說都是Block。

Erlang:
T = [1, 2, 3]
L = [4, 5|T]

相當於REBOL:
T: [1 2 3]
L: Join [ 4 5 ] T

* 取出List元素
Erlang List的Head可以非常有效率地被取出。幾乎所有處理List的函數,都是先取出Head,做處理,再遞迴地處理Tail。

Erlang:
[X|Y] = L

或者利用BIF(Build-in Function)
X = hd(L)
Y = tl(L)

REBOL的作法:
X: First L
Y: Next L

Erlang VS. REBOL, Part I

* 語言種類
Erlang屬於Pure Functional Language。REBOL屬於Impure Functional Language。Erlang的許多設計考量,是為了方便進行concurrency。REBOL的設計則最適合進行Meta-Programming。

* 註解(Comment)
Erlang使用「%」,REBOL使用「;」,當作單行註解記號。

* 算術表示式 2 + 3 * 4
Erlang: 2 + 3 * 4 (寫法比較直覺)
REBOL: 2 + (3 * 4) (一定要寫括號,不然一律由左至右計算)

* Integer Overflow?
Erlang使用任意長度的整數,來進行算術運算,不會發生overflow。
REBOL使用64位元長度記錄整數,有可能發生overflow。

Erlang:
> 12345678 * 987654321 * 112233445566778899 * 998877665544332211.
(A very huge number. No problem here)

REBOL:
>> 12345678 * 987654321 * 112233445566778899 * 998877665544332211
** Math error: Math or number overflow

* 不同基底的整數
Erlang可以使用任何基底,例如:
16#cafebabe (表示16進位)
17#cafebabe (表示17進位)

REBOL做不到這一點。

* 變數名稱
Erlang的變數必須頭文字大寫,且只能使用英文和數字。REBOL不區分大小寫,可以使用各國文字。

* 變數範疇
Erlang的變數範疇(scope)只有在clause內。不同的clause之間不可能共享變數。REBOL沒有此限制。

* 變數繫結
Erlang的變數只能被繫結(bound)一次(可以讓除錯變得很容易)。REBOL沒有此限制。

2008年4月13日

Keep IT Simple 歌詞

我喜歡的歌手之一Van Morrison,最近(四月)推出的專輯,就名為「Keep it Simple」,你可到Van Morrison的官方網站聽這首歌的Audio Clip。

「Keep IT Simple」碰巧也是REBOL的Slogan,只不過這裡的IT要大寫,表示「Information Technologies」(資訊科技)的意思。IT往往搞得過度複雜,而REBOL正致力於破除這一點。

我把這首歌的歌詞改編如下,希望能在下次REBOL DevCon時登台演唱這首歌(I am just kidding):

Keep IT Simple

原創:Van Morrison
改編:Jerry Tsai

They mocked me when I was singing the songs
Trying to go forward to something more simple than we have
They mocked me ‘cos I told it like it was

Wrote about slowness and monolith
Wrote about what we really didn’t need in our code
Make us work like a horse

Dream software systems on the one hand
And straight reality is always cold
Blame it on the programming language
It’s not your fault at all

Mocked me when it got out of hand
Nobody tried to understand
Now we got to keep it simple and that’s that

Dream software systems on the one hand
And straight reality is always cold
Blame it on the programming language
It’s not your fault at all

Can’t find the bugs while I am writing code
It happens many times before
Hay, we got to keep it simple to save ourselves

Mocked me when I tried to go forward
Said the train was completely off the track
And we got to go forward to something simple to save ourselves

Whoa we got to leave .NET for REBOL just to save yourselves
Well got to leave Java for REBOL just to save yourselves
Well you got to keep it simple, keep it simple just to - and that’s that

Whoa you got to keep it simple nowadays and that’s just that
Whoa you got to keep it simple nowadays and that’s just the way it is
And you got to keep it simple these days ‘cos that’s the way it is

Well you got to keep it, keep it simple and that’s that

REBOL投影片


我開始利用閒暇時間,製作「認識REBOL」投影片。我喜歡用圖解說,不喜歡用Bullet Point,所以這份投影片會繪製許多示意圖。 此Blog的許多圖,就是來自這份教材。
我希望這份教材至少能完整地涵蓋REBOL User's Guide,加上VID、CGI、聲音。其他的部分會不會加進來,就不一定了。

GUI架構


REBOL的GUI是以GOB為核心,GOB的設計考慮到效率和彈性。GOB的下層目前是AGG的2D繪圖引擎,以後還有可能加上OpenGL/DirectX等3D引擎。

你可以只利用GOB和View設計出REBOL的GUI程式(我的舊版本Transma程式,就只有使用到GOB和View,沒用到VID),但是這麼做的複雜度比較高,大多數的人會使用VID。VID是一種高階的視覺化介面方言,將下面的細節隱匿起來。VID也具有Skin(轉換不同視覺外觀)的能力。

2008年4月12日

CJK地區,誰在注意REBOL?


你可以到這個網址,看到世界各地的都市中,有哪些人在造訪REBOL.com。在CJK(中日韓)地區,以北京、台北、澳門的造訪者最多。詳細的中國地圖,請見這裡
註:Macao也可以寫成Macau。

用Draw方言繪製發光文字


REBOL Draw底層使用AGG 2.4當繪圖引擎,繪圖能力很強。這裡是一個範例展示:

REBOL[]

my-font: make system/standard/font [
name: "Times New Roman" size: 64
]

draw-block: [ ]

for i 0 9 1 [
j: to-integer (255 / 10.0 * (i + 1))
append draw-block compose/deep [
pen (to-tuple reduce [ j j 255 ] )
line-width (10 - i)
line-join round
fill-pen none
text 10x0 vectorial [
font my-font bold "REBOL"
]
]
]

append draw-block compose [
fill-pen black
text 10x0 vectorial [
font my-font bold "REBOL"
]
Fill-pen Red
text 142x0 vectorial [
font my-font bold "O"
]
]

win: make gob! [
size: 360x130
color: black
]

append win g: make gob! compose [
size: ( win/size )
offset: 0x0
]

g/draw: draw-block

view win

2008年4月9日

REBOL的註解

REBOL的註解,是從英文分號「;」開始,到該行結束為止。例如:

name: "John Doe" ; 預定使用者名稱

你也可以使用comment函數,此函數後面需要一個參數,此參數會被視為註解,不予執行。例如:

comment 100

利用{}字串或 []區塊當參數,comment可以延續許多行,例如:

comment [
多行的註解
]

comment {
多行的註解
}

我們盡量使用{}字串當comment參數,而不要用[]區塊當其參數,因為[]區塊內還是要遵守REBOL型別的規範,不能隨意寫。例如:

comment [
OK :)
]
**Syntax Error

而{}字串就沒有這樣的問題。

comment {
OK :)
}

其實,許多出現comment { … }的地方,都可以將comment省略,直接寫{ … },例如:

pi: 3.14 {圓周率}

這類多餘的值,會被REBOL解譯器丟棄,不會有作用,所以可以當成註解使用。

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函數)

在Block內安插換行記號

如果你想在console將block列印出來,你可能會希望block的內容不要擠在一起。其實,你可以在block內插入一些換行的記號,你可以每個元素後面插入換行記號,或者每隔n個元素後面插入換行記號。請看下面的範例:

>> data: [ 1 "A" 2 "B" 3 "C" ]
== [ 1 "A" 2 "B" 3 "C" ]
>> new-line/skip data true 2

new-line是一個函數(不要和newline混淆了,newline是一個字元)。上面的指令意思是:在data內每隔2個元素插入一個換行記號。true表示「要加入換行記號」。執行完後,你會發現現在每兩個元素就會換行:

>> data
== [
1 "A"
2 "B"
3 "C"
]

你可以利用new-line?來判斷某block是否具有換行記號:

>> new-line? data
== true

你可以利用new-line將換行記號移除:

>> new-line/all data false
== [ 1 "A" 2 "B" 3 "C" ]

Series函數總整理

本來想要將所有和Series相關的函數全都繪製成圖,但是過去三篇貼文所呈現的效果並不好,所以做罷。現在決定將這些函數整理分類列出來就好。

【傳出位置】(指標不變動)
next:傳出下一個位置
back:傳出前一個位置
head:傳出頭的位置
tail:傳出尾的位置
at:傳出某位置(目前的位置為1)
skip:傳出某位置(目前的位置為0)

【移動位置】(指標會變動)
++:傳出目前位置,再移動到下一個
--:移動到前一個,再傳出目前的位置

【取得資訊】
index?:目前位置索引值
offset?:兩個索引值的差距
length?:剩下的個數
empty?:是否剩下的個數為0
head?:是否為頭
tail?:是否為尾
past?:是否超過尾端

【取得元素】
first:第一個元素
last:最後一個元素
pick:注意0和負的時候,R3.0和R2.x的傳出值不同。
second ... tenth:第n個元素
也可以使用path來取得元素,效果同pick

【取得元素+移動位置】(指標會變動)
first+:取得目前位置的元素,並移動到下一個位置

【取得元素+移動位置】
generate-cycle:製作循環的函數
generate-series:製作不循環的函數(cf. first+)

【取得元素+移除元素】(內容會變動)
take: first + remove

【插入元素】
insert
append(repend)

【移除元素】
remove(效果同take,但傳出值不同)
clear
remove-each

【改變】
poke
change

【複製】
copy

【尋找】
find
maximum-of
minimum-of
replace

【排列】
reverse:顛倒
random:亂排
sort:排序

【交換】
swap:交換兩個序列位置內的first元素

【建立】
array

【其他】
alter可能在3.0版中會移除。

2008年4月5日

REBOL Application Marketplace?

我記得Carl似乎曾有一個類似REBOL 3.0 Application Online Marketplace的提議,當然如果此計畫要成形,最快也會是在2009。我觀察到今年(2008)Marketplace概念有一點小熱門,包括Apple iPhone、U3、VMware、Windows、Adobe AIR、SalesForce AppExchange都有意這麼搞,或者已經在這麼搞了。連最近積極和我聯繫的一家北京軟體公司,也在做類似U3 + VMware的平台。我最近在iThome【言程序】發表了一篇Marketplace相關的文章,你可以參考。

2008年4月3日

Series函數:取得資訊

index?用來取得目前位置索引值。

offset?計算兩個索引值的差距。其實相當於「第二個引數的索引值」減掉「第一個引數的索引值」。

想知道是否在頭的位置,使用head?。

想知道是否在尾的位置,使用tail?。

length?計算剩下的個數(目前的位置算在內,但tail不算)。

想知道是否為空,可以判斷是否為尾端,或者length?是否為零,或者直接使用empty?。雖然tail?和empty?的作用一樣(事實上,empty?和tail?是完全相同的函數,所以「same? :empty? :tail?」會得到「true」),但是意義不同,寫程式時最好選擇正確意義者,以方便他人閱讀理解。

想判斷是否超過尾端,用past?。補充說明:為何會超出尾端?

(待解問題:用empty?判斷指向past的位置,會如何?)

Series函數:遞增與遞減



Series可以使用遞增(++)和遞減(--)函數,這兩個函數都會改變指標。++會傳出目前位置,再移動到下一個,新位置和傳出的位置是不一樣的。--是移動到前一個,再傳出目前的位置,新位置和傳出的位置是一樣的。為何兩者的作法不一致(++先傳出再移動,--先移動再傳出),這個問題留待迴圈的文章中解答。

2008年4月2日

Series函數:傳出新位置,指標不變動

Next用來傳出下一個位置,參數指標的位置不變動。

Back用來傳出前一個位置,參數指標的位置不變動。

At傳出某位置,目前的位置視為「位置1」。

Skip傳出「略過多少格位置」之後的位置。 Skip和At會有一格的差距。

At和Skip的引數都可以為負的值,這個時候,這兩個函數的結果完全一樣。


Head會傳出頭部的位置。

Tail會傳出「尾部」的位置。所謂的「尾部」,是指「最後一個元素的下一個」位置,而不是最後一個元素的位置。

不管用什麼函數,當意圖往前移動到超出邊界,會停留在頭部的位置;意圖往後移動超出邊界,則會停留在尾部的位置。

2008年4月1日

關於Unicode的一些判斷函數

utf?函數需要一個binary!當參數。根據此binary的BOM(byte order mark)來做判斷,如果傳出值的絕對值是8,則表示UTF-8;如果傳出值的絕對值為16,則表示UTF-16;如果是Big-Endian,則傳出值為正;如果為Little-Endian,則傳出值為負。 例如:

>> utf? #{EF BB BF}
== 8
>> utf? #{FE FF}
== 16
>> utf? #{FF FE}
== -16
>> utf? #{00 00 FE FF}
== 32
>> utf? #{FF FE 00 00}
== -32

如果binary不是上述的開頭,會得到0的傳出值,表示無法識別。

latin1?需要一個引數,可以是泛字串或字元或整數(表示Code Point)。如果code point都小於256,則傳出true;否則傳出false。latin1?未來有可能改成latin-1?。

ascii?函數和latin1?完全一樣,但用來判斷code point是否小於128。

關於Unicode的BOM,可以參考這篇FAQ的解釋以及這篇Wiki。REBOL的UTF?只用來判斷8, 16, 32的LE和BE的BOM,不支援其他BOM(因為其他BOM很少有人用)。

什麼是action?

action是所有資料型別都必備的基本原生函數。你可以透過下面的方式取得完整的action列表:

>> probe system/catalog/actions

每個資料型別都有一個表格(類似C++的Virtual Table),記錄此型別專屬的所有action函數。目前每個資料型別的action表格內都有68個函數。

>> length? system/catalog/actions

== 68

當你使用action函數時,此action函數會根據參數的資料型別,來決定呼叫那個表格內的對應函數,其實這是一種Polymorphism。例如,當你呼叫subtract時,它會根據參數是整數或日期或其他,來決定使用哪一個資料型別action表格內的subtract函數。

如果你使用下面的方式觀察,你會發現實際上列出來的action!不只68個:

>> ? action!

那是因為許多action都可以輕易處理掉,不需要在action表格中準備特殊的函數。這類的action函數以查詢型別的函數為大宗,例如integer?、string?。

關於集合的基本運算



REBOL有一個unique函數,可以將一個集合內重複出現的元素移除,例如:

>> unique [1 2 1 2 3]
== [1 2 3]

REBOL有四個函數,可以進行兩個集合的計算,這四個函數分別是union、intersect、exclude、difference,其作用如圖所示。 使用union、intersect、exclude、difference所得到的結果,一定不會有重複的元素,感覺就像是使用unique處理過一樣。這是因為,一旦使用這四個函數,就等於是將引數視為「集合」,而數學上對集合的定義,本來就是不可以有重複的元素。例如:

>> union [1 2 1] [1 3 1]
== [1 2 3]

上述五個函數都具有/skip和/case兩個修飾(refinement)。/case表示區分大小寫(不加上此refinement則不會區分大小寫)。/skip需要一個整數引數n,表示每n個元素被視為一筆記錄。

這裡所謂的集合,包括了string!、block!、以及bitset!。

bitset本來就是不會重複的字元集合,所以對bitset使用unique函數並不會產生任何作用。這五個集合函數作用在bitset上的時候,不可以搭配/case或/skip。這五個集合函數用在string時,不可以搭配/skip。
請注意,unique會造成原本集合的改變,但union、intersect、exclude、difference則不會破壞原來的集合。
另外,complement函數可以用來為bitset進行反相,但是目前alpha版尚未定義實際的作法。

REBOL 3.0進度:Linux and MacOS X

幾個小時前,REBOL公司推出了Linux和MacOS X平台上的core alpha測試版(螢幕截圖請見Henrik的貼文)。加上原本的Winodws版,現在已經有三個OS平台的版本了。

另外,REBOL公司預計在四月釋出REBOL 3.0 beta核心,供大眾測試。

2008年3月31日

logic!資料型別的位元欄位

大多數的語言都會有一種只能表示真(true)或假(false)的資料型別,有的語言稱此型別為boolean,有的稱為bool,REBOL稱它為logic!。

由於電腦資料處理時,最小的單位是位元組(8個位元),而8個位元能夠表達的資料有2的8次方(256)種,所以只用來表達logic!實在有一點浪費,但這也是無可奈何的事。

REBOL 3.0充分運用原本浪費的空間,來做更豐富的表達。logic!可以被視為有許多位元欄位(bit field),每個欄位都可以有自己的名稱:

>> paradigm: make logic! [ imperative object-oriented functional logical ]
== false
>> paradigm/imperative: true
== true
>> paradigm/functional: true
== true
>> mold/all paragigm
{#[logic! 5 [imperative object-oriented functional logical]]}

為何內部的值會是5?因為imperative(最低的位元)和object-oriented(第三低的位元)為true,所以值為#{00000101},也就是5。

邏輯值的位元欄位可以超過8個,例如:

>> paradigm: make logic! [ b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 ]
== false

由於只要有任何一個位元欄位為true,整體邏輯值就為true,所以位元欄位可以用來取代OR運算子或ANY函數,例如:

if any [b1 b2 b3 b4 b5] [
do-something
]

改寫成:

flags: make logic! [b1 b2 b3 b4 b5]

...

if flags [
do-something
]

2008年3月30日

Reflection

最新版的REBOL有一個新的reflect函數,利用它可以得知某值(通常是物件、模組、或函數)的內部資訊,此函數用來取代舊版REBOL的first、second、third。reflect函數用法如下:

>> reflect system 'words
== […]
>> reflect system 'values
== […]

上面的兩個例子,意思是列出system物件的words,以及列出system物件的values。

除了words和values,reflect還接收哪些單字當作第二個引數?這些單字全都列在system/catalog/reflectors中:

>> system/catalog/reflectors
== [ spec body words values types title ]

除了使用reflect函數之外,也可以使用速寫的版本,包括了spec-of、body-of、words-of、values-of、types-of、title-of。也就是說「reflect system 'values」和「values-of system」作用是一樣的。

這些函數適合用在IDE和自動測試程式中,除此之外,平常應該是不會用到。

REBOL 3.0未來將可以支援UCS-4

REBOL 3.0支援Unicode,內部使用UCS-2(也就是16位元)的方式記錄資料,已經相當夠我們使用。在REBOL 3.0的一切都穩定之後,REBOL公司不排除特別為中文的REBOL使用者編譯一套UCS-4(也就是32位元)的版本(只需要改變一下編譯時的設定即可)。如此一來,蒙古文,藏文,以及相當罕用的中文特殊字,都可以支援。

另外,新版的REBOL提供了原生函數,可以幫助我們判斷字元或字串是否為ASCII(0~127),或者是否為Latin-1(0~255)。

2008年3月29日

千變萬化的REBOL Host

REBOL將與系統相關的部分,獨立出來,放在Host。Host的部分是開放源碼的,任何人都可以幫助REBOL將Host移植到不同的平台,這並不難做到。

你可以寫出各種作業系統的Host,這麼一來,REBOL程式就可以在該作業系統上執行。REBOL公司預計會提供Windows、MacOS、Linux、BSD等主流作業系統的Host。(目前2.x版就支援上述的平台。)

REBOL公司應該也會推出瀏覽器插件(Plug-in)版本的Host,如此一來,REBOL程式就可以像Sliverlight或Flash一樣,被嵌入網頁內。(2.x版已經有支援瀏覽器插件,請見這裡。)

Host也可以寫成Web伺服器的模組,例如Apache MOD,讓REBOL程式和伺服器之間的整合更好,適合當作Server-side的編劇環境。

可以預期會有人幫REBOL做出Windows Services,使得REBOL可以寫出「常駐程式」(有人開發出2.x版的Windows Services,請見NT-Services Support Library)。

最棒的是,你可以讓你自己的原生程式當作REBOL的Host,等於是把REBOL嵌入你的程式中。

2008年3月28日

REBOL 3.0進度


在REBOL公司的網站改版之前,REBOL 3.0有一個暫時性的網址。官方最新的消息都會公布在這裡。


根據該網站的說明,最近2.x版的維護已經告一個段落,重點再度回到REBOL 3.0。AltME社群內部的記錄顯示,REBOL公司已經修正我之前提出的某些Bug,也將Unicode核心版本釋出給所有的Alpha成員。另外REBOL公司也製作一套自動Test Case產生器,來幫忙找出Bug。

2008年3月27日

REBOL教學影片

剛剛在REBOL部落客Henrik的網站發現一篇貼文,提到Nick Antonaccio有製作許多REBOL教學影片,放在YouTube上,你可以去瞧瞧。完整的影片清單在這裡。影片中,Nick打字的時候,鍵盤發出的聲音都被錄進去,好大聲呀 ...

預測3.0正式版的推出日期

今年預計9月舉辦DevCon,我之前據此推測此時會推出REBOL 3.0正式版,但是我現在要修正我的看法。根據現在尚缺的功能,以及RT公司目前的工作量,還有「其他權威人士」的預測,我認為REBOL 3.0正式版有可能拖到2009年才會出現。唉!

2008年3月21日

REBOL 3.0的開放源碼模型


REBOL 3.0採用混合式的開放源碼模式,共分成四個部分:
  1. Host的部分開放,這部分是和OS相關的抽象層,源碼是C語言。
  2. 核心(core)的部分不開放,由REBOL公司負責維護。如果擔心因此無法獲得保障,可以向REBOL公司取得核心源碼的授權。
  3. 內部元件(internal components)是標準的元件,這部分的源碼開放,而源碼可能是C也可能是REBOL。
  4. 外部插件(external plug-in)是REBOL開發者可以自行擴充REBOL的部分。至於源碼是開放或封閉,C或REBOL,加密或不加密,免費或收費,則由插件開發者自行決定。

2008年3月19日

Jerry Tsai’s “Size Does Matter” Chart, 2008 Edition, Service Pack 1

我其實之前也在懷疑,.NET 1.1「才」23MB,怎麼.NET 3.5會躍升到197MB,裡面是裝鉛塊了嗎?不過我自從這幾年人變老之後,已經沒有以往追根究底的精神,我懶得去分析這驚人的197MB成分為何。

有讀者告訴我,其實197MB裡面同時包含了64位元和32位元的.NET執行環境,儘管如此,我依然無法想像為何會包成這麼大一包。我印象中,不管32位元的CLR或64位元的CLR,都是使用32位元的Managed PE檔。這就好像不管32位元的JVM或64位元的JVM,都是使用相同的.class檔(Java Bytecode)一樣。

.NET體積雖然大,但是有將Enterprise的程式庫放進去。我列出的JRE體積雖小,但是只有J2SE,並非J2EE。Adobe AIR雖然一出生就11MB,感覺很大,但它內建資料庫、和網頁展現引擎,這是其他執行環境(Java和.NET)所沒有的。所以本來體積的比較,就不會太公平。

所以體積的比較,只能讓你瞭解大概的狀況,軟體是不是真的「虛胖」,你必須分析它的功能才知道,這工程浩大,我就無法代勞了。

2008年3月18日

Size Does Matter, 2008 Edition


我承認在「Size Dos Matter」一文中,資料有些舊了,所以剛剛花了一點時間重繪這張圖,移除大家不熟悉的LISP,新增剛推出的Adobe AIR。

方塊內所標示的數字,是各種語言執行環境的體積MB數。為了要有一致的比較基準,這裡的軟體都一律是Windows平台上最新版,且是普遍使用的版本(例如Java使用的是Sun的版本,Perl使用的是ActivePerl的版本)。

這只是單純的體積比較。各種語言執行環境的能力(例如GUI、資料庫、Web框架、網頁展現引擎)不在這裡考量之列。

2008年3月17日

推薦一本自然語處理的好書


如果你對自然語處理感興趣,想瞭解如何寫出相關的程式。我推薦這本書,由James Allen教授著作的「Natural Language Understanding, 2nd Ed.」。雖然這是13年前的舊書,但內容還是很實用,就怕這本書絕版不容易找到了。
REBOL相當適合用來寫自然語處理的程式。

馬賽克與柔邊

剛剛寫了一篇短文,內容請見 mosaic and anti-aliasing

REBOL的命名慣例

  • 型別名稱後面加上驚嘆號,例如「integer!」、「char!」、「block!」。
  • 只詢問資訊,沒有命令動作,也不會更動任何資料的函數,名稱後面加上問號「?」,例如「length?」、「type?」、「exists?」。
  • 一個單字如果由多個字組成,則字和字之間使用-當連接符號,不使用底線當連接符號,例如「to-local-file」、「split-path」、「make-dir」。
  • 目的與作用一樣,只是多了一點小變化,則名稱也多了一點小變化,通常是在名稱後面加上「*」或「+」或「~」,例如「first」與「first+」、「system/options/locale」與「system/options/locale*」。

2008年3月15日

Size Does Matter


這是我好幾年前整理出來的一張圖,其中列出來的許多Script語言可能都沒有包含GUI套件,如果把GUI套件也加上去的話(它們使用的GUI套件體積都很大),原本不小的體積就會更龐大了。

經過這些年,這些執行環境本身都變大許多,例如.NET到了2.0版,已經膨脹到約50 MB,至於.NET 3.5有多大,我已經懶得去查了。但是最近推出alpha的REBOL 3.0,卻依然維持相當小的體積,只有數百KB,正式版推出時,應該也不會超過1MB。在這1MB之內,所有的功能(包含GUI),一應俱全。

我非常討厭肥大的軟體,你可以去閱讀我的這一篇文章「軟體的病態性肥胖」。

為各種資料型別分類


分門別類,是一種整理知識的表現。對REBOL程式員來說,為各種資料型別(datatype)分類相當重要,因為相同種類的資料型別,往往會有相當類似的操作方式,甚至可以使用完全一樣的函數來進行操作。如果你知道每個型別屬於哪一類,那麼在寫REBOL程式時,很容易就可以發揮聯想,猜測有哪些函數可以用。

我們可以利用兩種方式,看看REBOL內部是如何為這些資料型別分類的:

  1. typeset!
  2. spec-of

【typeset】

typeset!(資料集)本身就是一種資料型別,用來將某些資料型別結合起來,成為一種「類似」資料型別的新組合。你可以利用下面的方式,看看REBOL預先定義哪些資料集:

>> ? typeset!

你會看到上面列出8種資料集,分別是any-block!、any-function!、any-string!、any-type!、any-word!、number!、scalar!、series!。你可以再次利用?函數,來看它們各自的詳細說明,例如:

>> ? number!
NUMBER! is a datatype of value: make typeset! [integer! decimal! [percent!]

意思是,NUMBER!是由integer!、decimal!、percent!這三種型別所組成的資料集。

下面就這八種資料集做整理:

Number! 有三種

  • Integer! (整數)
  • Decimal! (小數)
  • Percent! (百分比)

Scalar!

  • Integer! Decimal! Percent! (同Number!)
  • Money! (精準數)
  • Char! (字元)
  • Pair! (值對)
  • Tuple! (值組)
  • Time! (時間)
  • (註:我認為應該也要包含Date!)

Any-Word! (泛單字)

  • Word! (單字)
  • Set-word! (設字)
  • Get-word! (取字)
  • Lit-word! (原字)
  • Refinement! (修飾)

Any-string! (泛字串)

  • String! (字串)
  • Binary! (二元)
  • File! (檔案)
  • Email! (電郵)
  • Url! (網址)
  • Tag! (標籤)
  • Issue! (期號)
  • (註:我認為應該也要包含Bitset!)

Any-block! (泛區塊)

  • Block! (區塊)
  • Paren! (括號區塊)
  • Map! (對應表)
  • Path! (路徑)
  • Set-path! (設徑)
  • Get-path! (取徑)
  • Lit-path! (原徑)

Series! (系列)

  • 包含「泛字串」與「泛區塊」的全部類別
  • Bitset! (位元集)
  • Vector! (向量)
  • Image! (影像)

Any-function! (泛函數)

  • Native! (原生函數)
  • Action! (動作函數)
  • Routine! (常式)
  • Rebcode! (彙編碼)
  • Op! (運算子)
  • Closure!
  • Function! (普通函數)

Any-type!

  • 全部(50+個)資料型別

【spec-of】

除了上述的方式,利用spec-of,搭配probe,也可以看出各種類別的分類,而且更全面。 作法:

>> probe spec-of block!

下面列出用這種方式觀察出來的分類。

Block

  • Block!
  • Get-path!
  • Lit-path!
  • Map!
  • Paren!
  • Path!
  • Rebcode! (註:這有點怪)
  • Set-path!
  • Struct! (註:這有點特別)

Function

  • Action!
  • Closure!
  • Function!
  • Native!
  • Op!
  • Routine!

Internal (內部使用)

  • End!
  • Frame!
  • Handle!
  • Library!
  • Unset!

Object

  • Error!
  • Module!
  • Object!
  • Port!
  • Task!
  • Utype!

Opt-Object

  • Event!
  • Gob!
  • Typeset!

Scalar

  • Char!
  • Date!
  • Decimal!
  • Integer!
  • Logic!
  • Money!
  • None!
  • Pair!
  • Path!
  • Percent!
  • Time!
  • Tuple!

String

  • Binary!
  • Bitset!
  • Email!
  • File!
  • Issue!
  • String!
  • Tag!
  • Url!

Symbol

  • Datatype!

Vector

  • Image!
  • Vector!

Word

  • Get-word!
  • Lit-word!
  • Refinement!
  • Set-word!
  • Word!

String和Binary之間的轉換



REBOL 3.0支援Unicode,字串和字元一律是Unicode。任何來自外部的資料,被視為二元,如果是文字資料,必須被解碼(decode)之後,成為Unicode字串,才能使用。字串必須被編碼(encode)轉成二元之後,被送到外部。

REBOL 2.7.6釋出

REBOL 2.x推出最新版本2.7.6,你可以到這裡下載取得最新版的SDK,或到這裡取得更多平台的Core,或找這裡下載View。建議下載View版本。完整的變動報告請見這裡

2008年3月14日

REBOL解譯器的執行方式

文 / 蔡學鏞

這篇文章相當重要,只要你懂這篇文章,REBOL剩下的主題,都不會太難。本文章中提到的計算、執行、估算、求值,都是指一樣的概念。

【ANY-WORD】
有的word有綁著(bind)一個值,計算這類的word,就相當於計算它所綁的值。例如:
>> pi
== 3.141592653589793

有的word沒有綁著一個值,計算這類的word,就會得到錯誤訊息。例如:
>> vista
** Script error: vista has no value

將一個word前面加上一個單引號('),就變成lit-wird。如果執行一個lit-word,就會得到它對應的word。例如:
>> 'pi
== pi
>> 'vista
== vista

將一個word後面加上一個冒號(:),就變成set-word。如果計算一個set-word,就會先計算後面的值,然後把這個值綁到word。例如:
>> pi: 3.14
== 3.14
>> pi
== 3.14

你可以把冒號想像成「等於」,所以「pi: 3.14」的意思就是「pi = 3.14」。原本pi被綁到一個很精確的值(3.141592653589793),現在被改成綁到3.14。

執行set-word之後,新的值會再度傳出。如果想一次將多個word設定成相同的值,可以利用這一點,例如:
>> a: b: c: 0

現在a b c都被設定為(綁到)0。

將一個word前面加上一個冒號(:),就變成get-word。如果計算一個get-word,就會得到它所綁的值。例如:
>> :pi
== 3.14

你可能覺得很奇怪,執行get-word和執行word得到的結果不是一樣嗎?
>> pi
== 3.14

雖然這個例子的結果一樣,但是意義不一樣。計算get-word,會取出所綁的值(3.14),直接傳出來;計算word,則多了一個步驟:先取出所綁的值(3.14),然後再計算這個值。只不過3.14計算之後剛好還是3.14。

當word被綁到函數的時候(REBOL有七種函數,請見「各種函數的區別」一文),就不一樣了:
>> now
== 14-Mar-2008/0:06:21+8:00
>> :now
== native!

now被綁到一個native函數,計算「:now」的值,只會得到這個函數,所以你會看到上面的例子寫「== native!」;計算now的值就會造成計算此native函數,因而得到現在的時刻。

將一個word前面加上一個斜線(/),就變成refinement。如果計算一個refinement,就會得到原封不動一樣的refinement。例如:

>> /year
== /year

refinement主要用在函數中,等一下就會看到。

【ANY-PATH】
有一種和word很像的東西,名為path。它由多個word串接起來的,word之間利用「/」當分隔符號。例如:

>> system/version
== 2.99.4.3.1

執行此path,得到的值是「2.99.4.3.1」(整個是一個值)。

path一樣有對應的lit-word、set-word、get-word。例如:
>> 'system/version
== system/version
>> system/user/name
== none
>> system/user/name: "Jerry Tsai"
== "Jerry Tsai"
>> system/user/name
== "Jerry Tsai"
>> :system/user/name
== "Jerry Tsai"

【函數】

前面提到過,如果某word或path被綁到一個函數,那麼計算此word或path就等於計算此函數:
>> now
== 14-Mar-2008/0:06:21+8:00

某些函數word可以加上refinement。例如:你只想取得現在的年份,你可以在now函數的後面,緊接著「/year」refinement:
>> now/year
== 2008

某些函數需要引數(argument),例如「length?」後面可以接著一個字串,傳出字串長度:

>> length? "Jerry"
== 5

大多數的函數,都會先計算出後續的引數,然後才執行函數。以上面的例子來說,"Jerry"計算的結果還是"Jerry",然後傳進length?當引數。下面是另一種寫法:

>> name: "Jerry"
== "Jerry"
>> length? name
== 5

name會先被執行,得到"Jerry"的值,然後把"Jerry"傳進length?當引數。

少部份的函數(例如help、source),不會先估算後續的引數,而是直接將後面的值傳入函數中。例如:

>> help 3.14
3.14 is a decimal
>> help pi
PI is a decimal of value: 3.14

為何第二個訊息會出現PI?因為傳進help函數的引數,是一個word,其值為pi。再看另一個範例:

>> now
== 14-Mar-2008/0:06:21+8:00
>> help 14-Mar-2008/0:06:21+8:00
14-Mar-2008/0:06:21+8:00 is a date
>> help now
USAGE …

「help now」列出了now的使用方式,而不是「… is a date」,這是因為help不會先去估算now的值,而是直接把now這個單字當作值,傳入help中。

那些函數所需要的引數是不會先估算的?這類的引數相當少,而且幾乎都是用在shell中做一些輔助查詢。

常常會一個函數執行到一半,就要跳去執行另一個函數。例如:

>> length? to-local-file what-dir

這個意思是取出目前的目錄(what-dir),將它轉成作業系統的檔案字串(to-local-file),然後計算長度(length?)。感覺好像運算次序變成由右到左,其實還是由左到右。先計算length?,由於length?是一個函數,它需要一個引數,所以從後面找來一個引數,此引數為to-local-file。剛好to-local-file也是一個函數,也需要一個引數,所以從後面取得what-dir的估算結果。有了what-dir的估算結果當引數,to-local-file就可以順利被估算,得到一個結果;有了to-local-file的估算結果當引數,就可以計算length?。

【op】

你可以用下面的方式表達「一加二」:
>> add 1 2
== 3

add是一個函數,需要兩個引數,分別是被加數與加數。最後add將計算的結果傳出來。

你可以用下面的方式表達「一加二減三」:
>> subtract add 1 2 3
== 0

subtract是「減」,後面需要兩個引數,分別是被減數與減數;其中被減數是add,而add又需要兩個引數,分別是1、2。所以「add 1 2」的結果會被當作subtract的第一個引數,最後的3就變成subtract的第二個引數。

你一定很不能接受,「一加二減三」居然要寫成「subtract add 1 2 3」,尤其是subtract居然還出現在add的前面。一個簡單的數學式子用REBOL來表達,竟會如此麻煩。所以在表達數學式子的時候,我們不喜歡用這樣的方式,我們會寫成:

>> 1 + 2 - 3
== 0

其中+與-也都是函數,這種函數稱為op(運算子),出現在兩個引數的中間。也就是說「add 1 2」相當於「1 + 2」

出了出現的位置和一般函數不一樣之外,op還有一點很特殊:一律由左到右的次序計算。例如:

>> 1 - 2 + 3
== 2

先計算「1 - 2」,得到「-1」;再計算「-1 + 3」,得到「2」。

op一律由左到右的次序計算,沒有所謂的「先乘除後加減」,例如:

>> 1 - 2 * 3
== -3

你可以利用括號 ( ) 改變次序,例如:

>> 1 - (2 * 3)
== -5

想知道REBOL有哪些op,在shell輸入:
>> help op!

來看最後一個例子:

>> divide 5 + 4 * 3 2
== 13.5

divide(除法)需要兩個引數,第一個引數是「5 + 4 * 3」,第二個引數是2;「5 + 4 * 3」的值是27,而「divide 27 2」得到13.5。

為何第一個引數是「5 + 4 * 3」,而不是「5」,因為REBOL解譯器注意到,5的後面跟著一個op,而op是一定位於兩個值的中間,所以第一個引數擴大為「5 + 4」;接下來REBOL解譯器又注意到後面又出現一個op,於是再把第一個引數擴大為「5 + 4 * 3」。