Rebol 程式設計/語言特性/控制
REBOL 提供了多種不同的控制結構,其中許多與其他計算機語言中的結構相似,也有一些是 REBOL 獨有的。
all: native [
{Shortcut AND. Evaluates and returns at the first FALSE or NONE.}
block [block!] "Block of expressions"
]
all 是一個有用的函式,允許檢查塊中的所有表示式是否都計算為真。它消除了級聯 either 結構的需要。
>> all [true true true] == true
>> all [true true false] == none
現在,有用的地方在於,儘管我們可以使用它來測試多個條件,如上所示,但只要每個表示式返回的值不同於 false 或 none,我們就可以在塊中執行多個任務。
all [ create-pdf-of-newsletter ftp-newsletter-to-website check-newsletter-not-corrupt update-webpage-last-change-date spam-our-customers ]
有趣的是,all 結構確保在我們完成任務列表和垃圾郵件之前,所有前面的表示式都必須為真。如果我們的網站更新沒有完成,我們不希望這樣做!
上面的示例利用了 all 的一個重要特性,即當它遇到 false 或 none 時不會進一步計算。這樣,前面的條件可以“保護”後面的條件,例如
>> x: 0 == 0 >> all [x <> 0 1 / x < 2] == none
當使用 and 運算子時,情況就不一樣了
>> (x <> 0) and (1 / x < 2) ** Math Error: Attempt to divide by zero ** Near: 1 / x < 2
注意:雖然 all 的幫助資訊說它是 and 的快捷方式,但實際上並非如此。所以,請注意以下情況
>> all [ true ] == true >> all [ true false ] == none
其中返回值是 none 而不是邏輯!false。
any: native [
{Shortcut OR. Evaluates and returns the first value that is not FALSE or NONE.}
block [block!] "Block of expressions"
]
any 如果塊中的任何表示式返回 true,則返回 true。
>> any [false false false true] == true
與 all 相似,any “保護”後面的條件,如果它遇到一個被認為是 true 的值(即任何不同於 false 和 none 的值)。同樣,此屬性不適用於 or 運算子。
if: native [
"If condition is TRUE, evaluates the block."
condition
then-block [block!]
/else "If not true, evaluate this block"
else-block [block!]
]
if 函式允許在 condition 引數為真(這意味著,不同於 false 或 none)時有條件地計算一個塊。否則 if 返回 none。如果使用 /else 修飾,則如果 condition 為假,則計算第二個塊。
條件可以是簡單表示式的結果,例如
>> if/else 5 < 10 [print "less than 10"] [print "greater than or equal to 10"] less than 10
警告:/else 修飾在未來的 Rebol 版本中可能不受支援,被認為是過時的!使用 either 原生函式更快且更具未來性。
條件可以是任何返回值的合法函式的結果。因此,如果您有一個名為 web-update? 的函式,用於檢查某個特定頁面是否在過去幾天內更新過,您可以這樣寫
>> if web-update? http://www.rebol.com [print "Rebol Tech has updated their website!"]
either 原生函式有條件地計算它的第二個引數(true-block),如果它的第一個引數(condition)為真(即不同於 false 和 none),否則計算它的第三個引數(false-block)。
>> either now/time < 12:00 [print "Good Morning"] [print "Good Afternoon"]
Rebol 中的真值與英語中使用相同的術語有所不同。任何不是 false 或 none 的東西都被視為真。這意味著所有整數,包括 0,都被視為真,以及所有塊等。
>> if [] [print "even an empty block is true"] even an empty block is treated as true
初學者陷阱:你應該特別注意
>> either [false] [true] [false] == true
, 因為該塊被視為真。0 也一樣
>> either 0 [true] [false] == true
我們可以使用 unless 函式代替 if not 組合。例如
>> if not 2 > 3 ["OK"] == "OK"
>> unless 2 > 3 ["OK"] == "OK"
一般 case 表示式形式可以是
case [
condition1 block1
condition2 block2
etc.
]
條件逐個計算和檢查。如果為真,則計算條件後面的塊並將其作為 case 的結果返回。
Case 增強了可讀性,取代了巢狀的 either。例如
wife-birthday: 1-March-1960
Valentines-day: 14-Feb-1900
wedding-anniversary: 1-April-1980
anniversary?: func [
date [date!]
{check to see if today's date is an anniversary of the argument}
][
all [date/day = now/day date/month = now/month]
]
greeting: case [
anniversary? wife-birthday ["Happy birthday dear"]
anniversary? Valentines-day ["Happy Valentines dear"]
anniversary? wedding-anniversary ["Happy anniversary dear"]
true ["Hello dear"]
]
上面的示例使用 either 編寫時幾乎不可讀。請注意 true 條件,如果前面的條件都不為真,則它將返回“Hello dear”。
USAGE:
SWITCH value cases /default case
DESCRIPTION:
Selects a choice and evaluates what follows it.
SWITCH is a function value.
ARGUMENTS:
value -- Value to search for. (Type: any)
cases -- Block of cases to search. (Type: block)
REFINEMENTS:
/default
case -- Default case if no others are found. (Type: any)
(SPECIAL ATTRIBUTES)
throw
switch 與 case 函式類似。但是,它接受一個值,然後將該值與一個值塊對塊進行比較。如果值匹配,則計算該塊並返回其值,並在該點離開函式。如果找不到匹配項,則返回 none。如果指定了 default 修飾,則計算可選的最後一個塊。
假設我們想實現一個 POP3 伺服器。我們知道 POP3 伺服器是狀態驅動的,有兩種狀態:授權狀態和事務狀態。在每種狀態下,POP3 伺服器只接受某些命令。
state: 'transaction
command: 'RETR
..
..
switch/default state [
transaction [
... contains another switch to check for valid transaction commands ...
]
authorisation [
... contains another switch to check for valid authorisation commands ...
]
] [print "Unknown state error"]
授權 switch 可能如下所示,其中塊中的單詞是虛擬碼。
switch/default command [ user [check to see if valid user syntax] pass [check to see if valid username and password combination] apop [change authentication method] quit [close connection and wait for next user] ] [print "Unknown command in authorisation mode"]
在以上兩個示例中,我們都使用 word 值作為 switch,但可以使用任何 Rebol 值。
switch 3 [
3 [print "3 received"]
4 [print "4 received"]
]
USAGE:
FOR 'word start end bump body
DESCRIPTION:
Repeats a block over a range of values.
FOR is a function value.
ARGUMENTS:
word -- Variable to hold current value (Type: word)
start -- Starting value (Type: number series money time date char)
end -- Ending value (Type: number series money time date char)
bump -- Amount to skip each time (Type: number money time char)
body -- Block to evaluate (Type: block)
(SPECIAL ATTRIBUTES)
catch
throw
for 與其他語言中常見的傳統 for 迴圈結構非常相似,只有一個小的變化 - 增量或增加量可以是除整數以外的資料型別!通常是必需的。
因此,如果您想編寫一個迴圈,其中計數器值以 money! 型別遞增,我們可以這樣做
for opinion $0 $.10 $0.02 [
print ["here is my 2c again totalling " opinion]
]
得到
here is my 2c again totalling $0.00 here is my 2c again totalling $0.02 here is my 2c again totalling $0.04 here is my 2c again totalling $0.06 here is my 2c again totalling $0.08 here is my 2c again totalling $0.10
如果 repeat 控制函式適合該工作,請優先使用 repeat,它更快。
USAGE:
FORALL 'word body
DESCRIPTION:
Evaluates a block for every value in a series.
FORALL is a function value.
ARGUMENTS:
word -- Word set to each position in series and changed as a result (Type: word)
body -- Block to evaluate each time (Type: block)
(SPECIAL ATTRIBUTES)
catch
Rebol 中最常用的迴圈是 **foreach**。但 **foreach** 無法修改當前值,因為它沒有提供系列位置。**forall** 為這種情況而建立,因為它提供位置而不是當前值。
在遍歷系列時,當前值成為系列的第一個元素。
我們可以像這樣更改第一個元素
my-series: [1 2 3 4]
forall my-series [
change my-series (first my-series) * first my-series
]
>> my-series == [1 4 9 16]
在上面的示例中,my-series 的所有成員都被平方。
另一種訪問系列的方法是使用路徑表示法,例如 my-series/1。然後它看起來像
my-series: [1 2 3 4]
forall my-series [
my-series/1: my-series/1 * my-series/1
]
這有時更方便。
**forall** 相對於 **foreach** 的另一個優勢是,您可以訪問系列中正在遍歷的當前元素的索引。
forall my-series [
print index? my-series
]
請注意,'my-series 在迴圈中被修改。在某些情況下,即使迴圈終止,這種變化也會保留。這種情況發生在中斷或錯誤時,這是故意的。
my-series: [1 2 3 4]
forall my-series [
if 3 = first my-series [break]
]
>> my-series == [3 4]
在 Rebol 的 pre view1.3/core 2.6 版本中,系列在迴圈完成時保持在尾部,這對許多人來說是一個混淆源。
這就是為什麼你發現
forall series [..] series: head series
在一些舊程式碼中。
Forskip
[edit | edit source]USAGE:
FORSKIP 'word skip-num body
DESCRIPTION:
Evaluates a block for periodic values in a series.
FORSKIP is a function value.
ARGUMENTS:
word -- Word set to each position in series and changed as a result (Type: word)
skip-num -- Number of values to skip each time (Type: integer)
body -- Block to evaluate each time (Type: block)
(SPECIAL ATTRIBUTES)
throw
catch
**forskip** 與 **forall** 類似,因為它在遍歷系列時將當前元素設定為系列的第一個元素。它與 **forskip** 不同的是,**forskip** 允許您按指定整數量在系列中移動。從 1.3 版本開始,它還會在完成時重置系列。**forall** 相對於 **foreach** 的所有優點也適用於 **forskip**。示例
areacodes: [
"Ukiah" 707
"San Francisco" 415
"Sacramento" 916
]
forskip areacodes 2 [
print [first areacodes "area code is" second areacodes]
]
產生以下輸出
Ukiah area code is 707 San Francisco area code is 415 Sacramento area code is 916
假設我們現在要將 1000 新增到區號。這裡有一種使用 **forskip** 完成此操作的方法
reverse areacodes ; so that the area code is the first element
forskip areacodes 2 [
change areacodes add 1000 first areacodes
]
reverse areacodes ; and set it back to the original format
得到
[ "Ukiah" 1707 "San Francisco" 1415 "Sacramento" 1916 ]
這裡還有另一種使用路徑表示法完成此操作的方法,它消除了反轉系列的必要性
forskip areacodes 2 [
areacodes/2: add 1000 areacodes/2
]
Foreach
[edit | edit source]USAGE:
FOREACH 'word data body
DESCRIPTION:
Evaluates a block for each value(s) in a series.
FOREACH is a native value.
ARGUMENTS:
word -- Word or block of words to set each time (will be local) (Type: get-word word block)
data -- The series to traverse (Type: series)
body -- Block to evaluate
**foreach** 可以說是 Rebol 中最常見的重複結構。與大多數重複結構一樣,它不影響它遍歷的系列。
files: read %./
foreach file files [
print file
]
在本示例中,當前目錄被讀取並作為系列儲存在詞 **files** 中。在 **foreach** 結構中,臨時變數 **file** 被分配系列中當前元素的值,然後列印到控制檯。臨時變數 **file** 僅在評估塊中是區域性的。
這也可以寫成
foreach file read %./ [
print file
]
這樣就無需定義詞 **files** 了。如果你真的想要詞 **files**,你也可以這樣做
foreach file files: read %./ [
print file
]
這樣可以節省你原始碼中的一行。
如果你的系列成對出現,你可以建立一個臨時變數對
names: ["Joe" "Bloggs" "Jane" "Doe"]
foreach [first-name surname] names [
print [first-name " " surname]
]
它會列印姓氏,一個空格,然後是名字,最後開始一個新行。
Forever
[edit | edit source]**Forever** 不斷地評估一個塊,除非在控制檯中使用 ESCAPE 鍵或塊內的函式終止。
它有什麼用呢?它可以用來增加一個人的業力!在西藏,人們使用機械轉經筒來旋轉祈禱文,例如 "嗡嘛呢唄美吽"。達賴喇嘛尊者曾說過,計算機可以起到同樣的作用(因為硬碟也在旋轉)。
因此,這裡有一個正在執行的 Rebol 轉經筒
forever [
print "Om Mani Padme Hum"
]
然而,**forever** 更常見的用法是建立一個伺服器迴圈
listen: open tcp://:12345
waitports: [listen]
forever [
data: wait waitports
either same? data listen [
active-port: first listen
append waitports active-port
][
incoming-from-remote: first data
print incoming-from-remote
]
]
Loop
[edit | edit source]USAGE:
LOOP count block
DESCRIPTION:
Evaluates a block a specified number of times.
LOOP is a native value.
ARGUMENTS:
count -- Number of repetitions (Type: integer)
block -- Block to evaluate (Type: block)
**loop** 允許您評估一個塊指定的次數,除非在塊內退出。但是,沒有可訪問的本地計數器,因此,如果您希望跟蹤重複次數,您將不得不為其建立自己的變數。或者,您可以使用 **for** 函式。
result: loop 3 [print "hail Mary"] "Hail Mary" "Hail Mary" "Hail Mary"
**loop** 返回塊最後一次執行的值,並在此處分配給 **result**。如果我們使用 **print**,我們會遇到錯誤,因為 **print** 不返回值。
Repeat
[edit | edit source]USAGE:
REPEAT 'word value body
DESCRIPTION:
Evaluates a block a number of times or over a series.
REPEAT is a native value.
ARGUMENTS:
word -- Word to set each time (Type: word)
value -- Maximum number or series to traverse (Type: integer series)
body -- Block to evaluate each time (Type: block)
**repeat** 評估一個塊,並提供一個本地計數器。如果重複 **value** 是一個整數,那麼計數器從一開始,並達到重複 **value**。
repeat value 3 [
print value
]
1
2
3
如果它是一個系列,那麼 "計數器" 將採用系列中的值
repeat value ["a" "b" "c"] [
print value
]
a
b
c
返回最終值。
Until
[edit | edit source]**Until** 是一個控制流函式,它在評估其 **block** 引數後檢查條件。
USAGE: UNTIL block
DESCRIPTION:
Evaluates a block until it is TRUE.
UNTIL is a native value.
ARGUMENTS:
block -- (Type: block)
**until** 不斷地評估一個塊,直到 **block** 評估為真。
until [ prin "Enter password: " pass: input/hide pass == "Uncle" ]
這裡,我們提示使用者輸入密碼。如果它與秘密值匹配,則獲得一個真值,我們退出塊。我們透過使用 **==** 而不是不區分大小寫的 **=** 來確保測試區分大小寫。
While
[edit | edit source]USAGE:
WHILE cond-block body-block
DESCRIPTION:
While a condition block is TRUE, evaluates another block.
WHILE is a native value.
ARGUMENTS:
cond-block -- (Type: block)
body-block -- (Type: block)
**while** 與 **until** 的不同之處在於,**body-block** 可能不會被評估,而在 **until** 中,**body** 塊總是至少被評估一次。
**While** 與其他程式語言中類似的控制流構造的不同之處在於,它實際上是在 "任何地方" 評估條件。它通常可以按以下方式使用
while [
do some pre-check stuff
need-more-work? ; the check
] [
do some post-check stuff
]
,其中預檢查內容和後檢查內容都是不必要的。
此示例等效於 **forever**
while [true] [
print "S.O.S."
]
,這是條件在 "一次重複的開始" 處評估的情況,因為在 **cond-block** 中的 **true** 表示式之前沒有評估。
另一個使用 **while** 寫無限迴圈的示例
while [print "Om Mani Padme Hum" true] []
,在這種情況下,條件實際上是在 "一次重複的結束" 處評估的,因為在 **cond-block**(產生 **true** 值)被評估後和另一次重複開始之前什麼也沒有做。
使用 **while** 跳過系列很常見
while [not tail? my-series: next my-series] [ do something here ]
Break
[edit | edit source]有幾種方法可以用來停止任何重複控制函式的重複。具體來說,我們可以使用 **break** 函式。將以下程式碼與 **while** 部分中使用的通用示例進行比較
while [true] [
do some pre-check stuff
unless need-more-work? [break]
do some post-check stuff
]
**break** 函式有一個 **/return** 細化,使我們能夠讓重複控制函式返回特定值。
還有兩種其他方法可以用來停止重複。如果重複是在函式中完成的,我們可以使用 **return** 或 **exit** 函式,如果重複是在 **catch** 塊中完成的,我們可以使用 **throw** 函式。