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** 函数。