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月26日
Get Windows Information in REBOL 2.x
2008年12月25日
利用Delect設計方言
以往,設計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月12日
2008年12月2日
REBOL 3.0 alpha 18
2008年12月1日
2008年11月28日
Jerry Tsai-Certified REBOL Programmers
2008年11月20日
2008年11月4日
關於REBOL 3.0的進展
理想和現實是有落差的,為了做出一個理想的語言,我看到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
>> do make task! [ [ ] loop 10 [ print now wait 1] ]
2008年8月27日
2008年7月18日
REBOL in Olympics
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
(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
2008年6月20日
相等
>> 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源碼時的干擾
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.
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 3.0版,這才是未來,這才重要。
Bug: change/part
>> 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
我一向認為「謠言始於智者」,我開始散播謠言。我猜想應該是「軟體線上市集」或「AltME傳訊軟體」或「Peer-to-Peer軟體」之類的。
2008年6月18日
小心TO-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
2008年6月17日
Cheyenne Web Server
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
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源碼
當參考資料不足時,我們就必須閱讀源碼。最近我開始感覺到有閱讀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 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
Event 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
abs-offset?: func [ gob [gob!] ] [
negate second map-inner gob 0x0
]
以後我打算只使用MAP-EVENT和abs-offset?,不再碰觸令人痛苦不已的MAP-INNER與MAP-OUTER。
MAP-EVENT的用法
Bugfix: parse-url
剖析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的用法
>> 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
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函數與寬字元
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?
>> 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
- More text encoders/decoders (Big5, GB2312, ... )
- Unicode text rendering
- Unicode text input from keyboard
- Plug-in ( for calling native C functions )
Because I am developing:
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 ?
但結果令我意外,「言程序」的點擊率居然還沒有「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。重點整理如下:
- Server會先Open自己電腦上的一個Listen Port,並開始聆聽(Listen)連線請求。對於REBOL來說,只要呼叫Open函數來開啟伺服器的Port,就會同時進行Listen(你不需要呼叫Listen,也沒有這樣的函數可用)。
- 當Client呼叫Open,並指定連到Server的IP和Port時,Server就會收到Accept的事件。處理Accept事件的方式是呼叫First,會得到一個Service Port。Server的Awake函數必須傳出false(表示任務尚未完成),否則會結束Listen,只能服務一人。
- Listen Port繼續接受來自其他Client的新連線請求,而Service Port開始和連線上的Client溝通。也就是說,Listen Port和Service Port兩個是不同的Port,且Listen Port只有一個,且可能產生出多個Service Port。
- Service Port會進行Read、Write、Close動作(函數),會分別收到Read、Wrote、Close事件。這部分和「如何用REBOL寫TCP/IP的Client程式」一文類似,不再贅述。
- 通常Service Port會先呼叫Read(而非Write),以讀取來自客戶的請求內容。
A New VID Prototype – VID 3.4
Functional Programming
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開發的貢獻,在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
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
OOP in REBOL ?
從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程式
- DECODE-URL用來將URL(例如:http://www.rebol.com/index.htm)拆解成區塊(例如[scheme: 'http host: "www.rebol.com" port-id: 80 path: "/index.htm"]),但是DECODE-URL有時候會拆解錯誤,如果與到拆解錯誤的狀況,就必須自己處理。如這個例子所示。
- 上述的區塊可以當作OPEN的參數,就可以連接到伺服器。
- 如果host是host-name,則程式會先連到DNS,查詢(lookup)伺服器的ip,所以會收到「lookup」事件,這個時候,必須再次呼叫OPEN。
- 如果host本來就是host-ip,則不會有上述的步驟(不會收到lookup事件)。
- 當程式收到「connect」事件時,表示程式對伺服器的連線請求,已經被伺服器接受(accept),可以開始進行資料通訊,這個時候,通常是由Client先傳送資料(服務請求)給伺服器,而伺服器先聆聽(listen)。因此,在收到connect事件時,Client通常會呼叫WRITE。
- 當資料寫入完畢,Client會收到「wrote」事件。這個時候,通常會呼叫READ,進行資料的讀取。當然,你也可以會直接呼叫CLOSE,主動關閉網路連線,這由你的通訊協定(protocol)決定。
- 呼叫READ,就會從緩衝區(buffer)讀取資料,一旦讀取完目前緩衝區的資料,就會收到read事件(這個read是「過去分詞」,表示「已經讀取了」)。
- 通常你需要解析剛剛讀到的資料,以瞭解資料是否已經讀完。如果尚未讀完,你必須繼續呼叫READ來繼續讀取資料。如果已經讀完,你可能會呼叫WRITE傳資料給伺服器,或者呼叫CLOSE來關閉連線。
- 當連線的兩端中,任何一端先呼叫CLOSE來關閉連線時,Client都會收到「close」事件。當client收到這個事件的時候,要做一些收尾的動作。
- awake的傳出值如果為false,表示port尚未處理完畢;如果為true,表示port處理完畢,會立刻結束這個port。通常我們會在遇到通訊協定格式錯誤、事件無法識別、或收到close事件之後,傳出true,其他狀況則傳出false。
- 為了避免任何動作做太久,我們通常會在外部呼叫WAIT,等候一段Timeout的時間。
2008年6月4日
我對REBOL 3.0的體會
由於REBOL 3.0的核心開發人力是Carl本人,所以他可以隨時動態調整開發項目的優先次序。例如原本Unicode要在3.1才加入,後來提前。這樣的例子屢見不鮮。
Carl在開發的過程,有一些理念上的堅持,那就是REBOL必須「簡單」、「體積小」、「效能」。任何作法只要違反這幾點,都會被排除,不予採納。所以即使REBOL已經開發這麼久了,且圖形和網路的功能都加進來了,還是只有約600K。且執行效率相當好,語法也維持一貫的簡單。
在這個年頭,軟體要保持「簡單」、「體積小」、「效能」,談何容易,但REBOL 3.0做到了。加上強大與創新的功能,這真的是一個好語言。
2008年6月3日
2008年6月2日
暫時擱置Transma計畫
想用REBOL 3.0,繼續動手完成我之前的計畫(Transma),無奈REBOL 3.0尚未支援下面的功能:
- 從鍵盤取得Unicode輸入
- 將Unicode文字輸出到GUI
- 呼叫外部DLL
看樣子我只好持續把這個計畫擱置下來。
2008年5月30日
使用Console(二)
REBOL預計會有兩個Console,目前已完成的只有文字模式的DevStdIO,而圖形模式的DevConsole短時間之內是不會出現的,看樣子我們得使用DevStdIO好一段時間。「使用Console」 這篇文章有介紹一些用法。除此之外,如果你要使用「複製 / 貼上」功能的話,你必須啟用「快速編輯模式」和「插入模式」。作法如下:
- 用滑鼠右鍵點選REBOL Console視窗的標題列,叫出系統選單。
- 從選單中選取「內容」。你會看到附圖的視窗。
- 將兩個編輯選項(「快速編輯模式」和「插入模式」)都選取。然後按下「確定」。
只要有上面的設定,你就可以用滑鼠選取文字,用滑鼠右鍵複製反白區的文字到剪貼簿;在沒有反白區的情況下,按下滑鼠右鍵可以將剪貼簿的文字貼到Console上。
2008年5月28日
REBOL剖析URL時的問題
read [
scheme: 'http
host: "www.some-host.net"
port-id: 80
path: "database/!data.html"
]
2008年5月26日
還是要進化
我只能說,「一個東西的理念領先人家太多,也不見得是好事」,因為大家的程度跟不上,當然也就不識貨。太先進的東西會被原始人當作垃圾。我只好讓他們繼續留在原地當原始人,我先一步進化去了。
2008年5月25日
Torn between two lovers
2008年5月23日
REBOL最新動態:VID、Desktop、REBOL/Services
2008年5月20日
利用Delect設計方言
以往,設計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#
2008年5月16日
好事多磨
近年來,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
2008年5月2日
RHAE & RRTE
2008年4月29日
整合Unicode核心和圖形介面的版本
2008年4月26日
關於meta-programming
2008年4月20日
Web顏色名稱
詳細的顏色,請參考這個網頁:
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日
2008年4月17日
About the Erlang VS. REBOL Series
2008年4月16日
Erlang VS. REBOL, Part VI
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
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
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在源碼檔案前這麼寫:
對Erlang來說,沒有被匯出者,無法在別的地方使用。但是對REBOL來說,即時沒有被匯出,依然可以在其他地方被使用,只要註明模組為何即可,例如:
REBOL [
Name: geometry
Version: 1.0.0
Type: module
Export: [area-rect area-circle]
]
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
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 歌詞
「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投影片
2008年4月12日
用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的註解
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,可以將多個值聚在一起。如下所示:
[ 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內安插換行記號
>> 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函數總整理
【傳出位置】(指標不變動)
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?
2008年4月3日
Series函數:取得資訊
offset?計算兩個索引值的差距。其實相當於「第二個引數的索引值」減掉「第一個引數的索引值」。
想知道是否在頭的位置,使用head?。
想知道是否在尾的位置,使用tail?。
length?計算剩下的個數(目前的位置算在內,但tail不算)。
想知道是否為空,可以判斷是否為尾端,或者length?是否為零,或者直接使用empty?。雖然tail?和empty?的作用一樣(事實上,empty?和tail?是完全相同的函數,所以「same? :empty? :tail?」會得到「true」),但是意義不同,寫程式時最好選擇正確意義者,以方便他人閱讀理解。
想判斷是否超過尾端,用past?。補充說明:為何會超出尾端?
(待解問題:用empty?判斷指向past的位置,會如何?)
2008年4月2日
2008年4月1日
關於Unicode的一些判斷函數
>> 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。
REBOL 3.0進度:Linux and MacOS X
另外,REBOL公司預計在四月釋出REBOL 3.0 beta核心,供大眾測試。
2008年3月31日
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
>> 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提供了原生函數,可以幫助我們判斷字元或字串是否為ASCII(0~127),或者是否為Latin-1(0~255)。
2008年3月29日
千變萬化的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日
預測3.0正式版的推出日期
2008年3月21日
REBOL 3.0的開放源碼模型
2008年3月19日
Jerry Tsai’s “Size Does Matter” Chart, 2008 Edition, Service Pack 1
有讀者告訴我,其實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
2008年3月17日
推薦一本自然語處理的好書
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內部是如何為這些資料型別分類的:
- typeset!
- 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之間的轉換
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」。