Chapter 4 - Expressions
REBOL/Core Users Guide Main Table of Contents Send Us Feedback
Contents:
1. Overview
2. Blocks
3. Values
3.1 Direct and Indirect Values
3.2 Datatypes of Values
4. Evaluating Expressions
4.1 Evaluating Console Input
4.2 Evaluating Simple Values
4.3 Evaluating Blocks
4.4 Reducing Blocks
4.5 Evaluating Scripts
4.6 Evaluating Strings
4.7 Evaluation Errors
5. Words
5.1 Word Names
5.2 Word Usage
5.3 Setting Words
5.4 Getting Words
5.5 Literal Words
5.6 Unset Words
5.7 Protecting Words
6. Conditional Evaluation
6.1 Conditional Blocks
6.2 Any and All
6.3 Conditional Loops
6.4 Common Mistakes
7. Repeated Evaluation
7.1 Loop
7.2 Repeat
7.3 For
7.4 Foreach
7.5 Forall and Forskip
7.6 Forever
7.7 Break
8. Selective Evaluation
8.1 Select
8.2 Switch
9. Stopping Evaluation
10. Trying Blocks
1. Overview
The foremost goal of REBOL is to establish a standard method of
communication that spans all computer systems. REBOL provides a
simple, direct means of expressing any kind of information with
optimal flexibility and minimal syntax. For example, examine the
following line:
Sell 100 shares of "Acme" at $47.97 per share
The line looks a lot like English making it easy to compose if
you are sending it and easy to understand if you are receiving
it. However, this line is actually a valid expression in REBOL,
so your computer could also understand and act on it. (Note that
the line is a "dialect" of REBOL. It can be in-directly evaluated.
More on this concept below.)
REBOL provides a common language between you and your computer. In
addition, if your computer sends this expression to your stock
broker's computer, which is also running REBOL, your stock
broker's computer can understand the expression and act on it.
So, REBOL provides a common language between computers. And, the line
could be sent to millions of other computer systems that could
also act on it.
The following line is another example of a REBOL expression:
Reschedule exam for 2-January-1999 at 10:30
The expression shown in the above example (written in another dialect) may have come from
your doctor typing it, or perhaps it originated from an
application that was run by your doctor. It does not matter.
What is important is that the expression can be acted upon
regardless of the type of computer, hand-held device, kiosk, or
television console you are using.
The data values (numbers, strings, prices, dates, and times) in
all of the expressions shown in the previous examples are
standardized valid REBOL formats. The words, however, depend on
a specific context of interpretation to convey their meaning.
Words such as sell, at, and read have different
meanings in different contexts. The words are relative
expressions -- their meaning is context dependent.
Expressions can be processed in one of two ways: directly by
the REBOL interpreter, or indirectly by a REBOL script. An
expression processed indirectly is called a dialect. The
previous examples are dialects, so they are processed by
a script. The following example is not a dialect and is
processed directly by the REBOL interpreter:
send master@rebol.com read http://www.rebol.com
In this example the words send and read are functions
that are processed by the REBOL interpreter.
The distinction REBOL makes is that information is either
directly or indirectly interpreted. The distinction is not
whether information is code or data, but how it is processed. In
REBOL code is often handled as data and data is frequently
processed as code, so the traditional division between code and
data blurs. How information is processed determines whether it
is code or data.
2. Blocks
REBOL Expressions are based on this concept: you combine
values and words into blocks.
In scripts, a block is normally enclosed with square brackets [ ]. Everything
within the square brackets is part of the block. The block contents can span any
number of lines, and its format is completely freeform. The following examples
show various ways of formatting block content:
[white red green blue yellow orange black]
["Spielberg" "Back to the Future" 1:56:20 MCA]
[
"Bill" billg@ms.dom #315-555-1234
"Steve" jobs@apl.dom #408-555-4321
"Ted" ted@gw2.dom #213-555-1010
]
sites: [
http://www.rebol.com [save %reb.html data]
http://www.cnn.com [print data]
ftp://www.amiga.com [send cs@org.foo data]
]
Some blocks do not require square brackets, because they are implied. For
example, in a REBOL script, there are no brackets around the entire script,
however, the script content is a block. The square brackets of an
outer-block of the script are implied. The same is true for
expressions typed at the command prompt or for REBOL messages sent between
computers--each is an implied block.
Another important aspect of blocks is that they imply additional information.
Blocks group a set of values in a particular
order. That is, a block can be used as a data set as well as a
sequence. This will be described in more detail in the Series Chapter.
3. Values
REBOL provides a built-in set of values that can be expressed
and exchanged between all systems. Values are the primary elements for composing
all REBOL expressions.
3.1 Direct and Indirect Values
Values can be directly or indirectly expressed.
A directly expressed value is known as it is lexically, or
literally, written. For instance, the number 10 or the time
10:30 are directly expressed values.
An indirectly expressed value is unknown until it is
evaluated. The values none, true, and false all require
words to represent them. These values are indirectly expressed
because they must be evaluated for their values to be known.
This is also true of other values, such as lists, hashes,
functions, objects.
3.2 Datatypes of Values
Every REBOL value is of a particular datatype. The datatype
of a value defines:
- The range of possible values for the datatype. For example,
the logic datatype can only be true or false.
- The operations that can be performed. For example, you can add
two integers, but you cannot add two logical values.
- The way in which the values are stored in memory. Some
datatypes can be stored directly (such as numbers), while others
are stored indirectly (such as strings).
By convention, REBOL datatype words are followed by an exclamation point
(!) to help make them stand out. For example:
integer!
char!
word!
string!
Datatype Words are Just Words
The words used for datatypes are just like any other words in REBOL.
There is nothing magic about the ! used to represent them.
See the Values
Appendix for a description of all the REBOL datatypes.
4. Evaluating Expressions
To evaluate an expression is to compute its value. REBOL
operates by evaluating the series of expressions constituting a
script and then returning the result. Evaluating is also called
running, processing, or executing a script.
Evaluation is performed on blocks. Blocks can be typed at the
console or loaded from a script file. Either way, the process of
evaluation is the same.
4.1 Evaluating Console Input
Any expression that can be evaluated in a script, can also be
evaluated from the REBOL prompt, providing a simple means of
testing individual expressions in a script.
For example, if you type the following expression at the console
prompt:
>> 1 + 2
the expression is evaluated and the following result is returned:
== 3
About The Code Examples...
In the example above, the console prompt (>>) and result
indicator (==) are shown to give you an idea of how they appear
in the console. For the examples that follow, the prompt and
result strings are not shown. However, you can assume that these
examples can be typed into the console to verify their results.
4.2 Evaluating Simple Values
Since the value of directly expressed values is known, they
simply return their values. For example, if you type the
following line:
10:30
the value 10:30 is returned. This is the behavior of all directly expressed
values. It includes:
integer 1234
decimal 12.34
string "REBOL world!"
time 13:47:02
date 30-June-1957
tuple 199.4.80.1
money $12.49
pair 100x200
char #"A"
binary #{ab82408b}
email info@rebol.com
issue #707-467-8000
tag <IMG SRC="xray.jpg">
file %xray.jpg
url http://www.rebol.com/
block [milk bread butter]
4.3 Evaluating Blocks
Normally, blocks are not evaluated. For example, typing the
following block:
[1 + 2]
returns the same block:
[1 + 2]
The block is not evaluated; it is simply treated as data.
To evaluate a block, use the do function, as shown in the
following example:
do [1 + 2]
3
The do function returns the result of the evaluation. In the
previous example, the number 3 is returned.
If a block contains multiple expressions, only the result of the last
expression is returned:
do [
1 + 2
3 + 4
]
7
In this example, both expressions are evaluated, but only the result of the
3 + 4 expression is returned.
There are a number of functions such as if, loop,
while, and foreach that evaluate a block as part of their
function. These functions are discussed in detail later in this
chapter, but here are a few examples:
if time > 12:30 [print "past noon"]
past noon
loop 4 [print "looping"]
looping
looping
looping
looping
This is important to remember: blocks are treated as data until they are
explicitly evaluated by a function. Only a function can cause them to be
evaluated.
4.4 Reducing Blocks
When you evaluate a block with do, only the value of its last
expression is returned as a result. However, there are times
when you want the values of all the expressions in a block to be
returned. To return the results of all of the expressions in a
block, use the reduce function. In the following example,
reduce is used to return the results of both expressions in
the block:
reduce [
1 + 2
3 + 4
]
[3 7]
In the above example, the block was reduced to its evaluation
results. The reduce function returns results in a block.
The reduce function is important because it enables you to
create blocks of expressions that are evaluated and passed to other functions.
Reduce evaluates each expression in a block and puts the result
of that expression into a new block. That new block is returned as the result of
reduce.
Some functions, like print, use reduce as
part of their operation, as shown in the following example:
print [1 + 2 3 + 4]
3 7
The rejoin, reform, and
remold functions also use reduce as part of
their operation, as shown in the following examples:
print rejoin [1 + 2 3 + 4]
37
print reform [1 + 2 3 + 4]
3 7
print remold [1 + 2 3 + 4]
[3 7]
The rejoin, reform, and
remold functions are based on the join,
form, and mold functions, but reduce their
blocks first.
4.5 Evaluating Scripts
The do function can be used to evaluate entire scripts.
Normally, do evaluates a block, as shown in the following
example:
do [print "Hello!"]
Hello!
But, when do evaluates a file name instead of a block, the
file will be loaded into the interpreter as a block, then evaluated as shown in
the following example:
do %script.r
A REBOL Header is Required
For a script file to be evaluated, it must include a valid REBOL
header, which is described in the Scripts Chapter. The
header identifies that the file contains a script and not just
random text.
4.6 Evaluating Strings
The do function can be used to evaluate expressions that are
found within text strings. For example, the following expression:
do "1 + 2"
3
returns the result 3. First the string is converted to a block,
then the block is evaluated.
Evaluating strings can be handy at times, but it should be done only when
necessary. For example, to create a REBOL console line processor, type the
following expression:
forever [probe do ask "=> "]
The above expression would prompt you with => and wait for you to
type a line of text. The text would then be evaluated, and its result would be
printed. (Of course, it's not really quite this simple, because the script could
have produced an error.)
Unless it is necessary, evaluating strings is not generally a good practice.
Evaluating strings is less efficient than evaluating blocks, and the context of
words in a string is not known. For example, the following expression:
do form ["1" "+" "2"]
is much less efficient than typing:
do [1 + 2]
REBOL blocks can be constructed just as easily as strings, and blocks are
better for expressions that need to be evaluated.
4.7 Evaluation Errors
Errors may occur for many different reasons during evaluation. For example,
if you divide a number by zero, evaluation is stopped and an error is
displayed
100 / 0
** Math Error: Attempt to divide by zero.
** Where: 100 / 0
A common error is using a word before it has been defined:
size + 10
** Script Error: size has no value.
** Where: size + 10
Another common error is not providing the proper values to a function in an
expression:
10 + [size]
** Script Error: Cannot use add on block! value.
** Where: 10 + [size]
Sometimes errors are not so obvious, and you will need to experiment to
determine what is causing the error.
5. Words
Expressions are built from values and words. Words are used to represent
meaning. A word can represent an idea or it can represent a specific value.
In the previous examples in this chapter, a number of words were used within
expressions without explanation. For instance, the do,
reduce, and try words are used, but not
explained.
Words are evaluated somewhat differently than directly expressed values. When
a word is evaluated, its value is looked up, evaluated, and returned as a
result. For example, if you type the following word:
zero
0
the value 0 is returned. The word zero is predefined to be
the number zero. When the word is looked up, a zero is found and
is returned.
When words like do and print are looked up, their values
are found to be functions, rather than simple values. In such
cases, the function is evaluated, and the result of the function
is returned.
5.1 Word Names
Words are composed of alphabetic characters, numbers, and any of the
following characters:
? ! . ' + - * & | = _ ~
A word cannot begin with a number, and there are also some restrictions on
words that could be interpreted as numbers. For example, -1 and +1 are numbers,
not words.
The end of a word is marked by a space, a new line, or one of the following
characters:
[ ] ( ) { } " : ; /
Thus, the brackets of a block are not part of a word. For example, the
following block contains the word test :
[test]
The following characters are not allowed in words as they cause words to be
misinterpreted or to generate an error:
@ # $ % ^ ,
Words can be of any length, but words cannot extend past the end
of a line:
this-is-a-very-long-word-used-as-an-example
The following lines provide examples of valid words:
Copy print test
number? time? date!
image-files l'image
++ -- == +-
***** *new-line*
left&right left|right
REBOL is not case sensitive. The following words all refer to the same
word:
blue
Blue
BLUE
The case of a word is preserved when it is printed.
Words can be reused. The meaning of a word is dependent on its context, so
words can be reused in different contexts. There are no keywords in REBOL. You
can reuse any word, even those that are predefined in REBOL. For instance, you
can use the word if in your code differently than the REBOL
interpreter uses this word.
Pick Good Words
Pick the words you use carefully. Words are used to associate
meaning. If you pick your words well, it will be easier for you
and others to understand your scripts.
5.2 Word Usage
Words are used in two ways: as symbols or as
variables. In the following block, words are used as symbols
for colors.
[red green blue]
In the following line:
print second [red green blue]
green
the words have no meaning other than their use as names for colors. All words
used within blocks serve as symbols until they are evaluated.
When a word is evaluated, it is used as a variable. In the previous example,
the words print and second are variables that
hold native functions which perform the required processing.
A word can be written in four ways to indicate how it is to be
treated, as shown in Word
Formats.
Format
|
What It Does
|
---|
word
|
Evaluates the word. This is the most natural and common way to
write words. If the word holds a function, it will be evaluated.
Otherwise, the value of the word will be returned.
|
word:
|
Defines or sets the value of a word. It is given a new value.
The value can be anything, including a function. See Setting Words below.
|
:word
|
Gets the word's value, but doesn't evaluate it. This is useful
for referring to functions and other data without evaluating
them. See Getting
Words below.
|
'word
|
Treats the word as a symbol, but does not evaluate it. The word
itself is the value.
|
5.3 Setting Words
A word followed by a colon (:) is used to define or set its value:
age: 42
lunch-time: 12:32
birthday: 20-March-1990
town: "Dodge City"
test: %stuff.r
You can set a word to be any type of value. In the previous examples, words
are defined to be integer, time, date, string, and file values. You can also set
words to be more complex types of values. For example, the following words are
set to block and function values:
towns: ["Ukiah" "Willits" "Mendocino"]
code: [if age > 32 [print town]]
say: func [item] [print item]
Why Words Are Set This Way
In many langages words are set with an equal sign, such as:
age = 42
In REBOL words are set with a colon. The reason for this is
important. It makes the set operation on words into a single
lexical value. The representation for the set operation is
atomic.
The difference between the two approaches can be seen in this
example.
print length? [age: 42]
2
print length? [age = 42]
3
REBOL is a reflective language, it is able to manipulate its
own code. This method of setting values allows you to write
code that easily manipulates set-word operations as a single
unit.
Of couse, the other reason is that the equal sign (=) is
used as a comparision operator.
Multiple words can be set at one time by cascading the word definitions. For
example, each of the following words are set to 42:
age: number: size: 42
Words can also be set with the set function:
set 'time 10:30
In this example, the line sets the word time to 10:30. The word
time is written as a literal (using a single quote) so that it will not
be evaluated.
The set function can also set multiple words:
set [number num ten] 10
print [number num ten]
10 10 10
In the above example, notice that the words do not need to be quoted because
they are within a block, which is not evaluated. The print
function shows that each word is set to the integer 10.
If set is provided a block of values, each of the individual
values are set to the words. In this example, one, two, and three are set to 1,
2, and 3:
set [one two three] [1 2 3]
print three
3
print [one two three]
1 2 3
See the Words
Section in the
Values Appendix for more about setting words.
5.4 Getting Words
To get the value of a word that was previously defined, place a colon (:) at
the front of the word. A word prefixed with a colon obtains the value of the
word, but does not evaluate it further if it is a function. For example, the
following line:
drucken: :print
defines a new word, drucken (which is German for print), to refer to
the same function print does. This is possible because
:print returns the function for print, but
does not evaluate it.
Now, drucken performs the same function as print :
drucken "test"
test
Both print and drucken are set to the same value,
which is the function that does printing.
This can also be accomplished with the get function. When
given a literal word, get returns its value, but does not
evaluate it:
stampa: get 'print
stampa "test"
test
The ability to get the value of a word is also important if you want to
determine what the value is without evaluating it. For example, you can
determine if a word is a native function using the following line:
print native? :if
true
Here the get returns the function for if. The
if function is not evaluated, but rather it is passed to the
native? function which checks if it is a native datatype.
Without the colon, the if function would be evaluated, and,
because it has no arguments, an error would occur.
5.5 Literal Words
The ability to deal with a word as a literal is useful. Both
set and get, as well as other functions like
value?, unset, protect,
and unprotect, expect a literal value.
Literal words can be written in one of two ways: by prefixing the word with a
single quotation mark, also known as a tick, (`) or by placing the word in a
block.
You can use a tick in front of a word that is evaluated:
word: 'this
In the above example, the word variable is set to the literal word
this, not to the value of this. The word variable
just uses the name symbolically. The example below shows that if you print the
value of the word, you will see the this word:
print word
this
You can also obtain literal words from an unevaluated block. In the following
example, the first function fetches the first word from the
block. This word is then set to the word variable.
word: first [this and that]
Any word can be used as a literal. It may or may not refer to a value. For
example, in the example below the word here has no value. The word
print does have a value, but it can still be used as a literal
because literal words are not evaluated.
word: 'here
print word
here
word: 'print
print word
print
The next example illustrates the importance of literal values:
video: [
title "Independence Day"
length 2:25:24
date 4/july/1996
]
print select video 'title
Independence Day
In this example, the word title is searched for in a block. If the
tick was missing from title, then its natural value would be used. If
title has no natural value, an error is displayed.
See the Words Section
in the Values Appendix
for more information about word literals.
5.6 Unset Words
A word that has no value is unset. If an unset word is
evaluated, an error will occur:
>> outlook
** Script Error: outlook has no value.
** Where: outlook
The error message in the previous example indicates that the word has not
been set to a value. The word is unset. Do not confuse this with a word that has
been set to none, which is a valid value.
A previously defined word can be unset at any time using
unset :
unset 'word
When a word is unset, its value is lost.
To determine if a word has been set, use the value?
function, which takes a literal word as its argument:
if not value? 'word [print "word is not set"]
word is not set
Determining whether a word is set can be useful in scripts that call other
scripts. For instance, a script may set a default parameter that was not
previously set:
if not value? 'test-mode [test-mode: on]
5.7 Protecting Words
You can prevent a word from being set with the protect
function:
protect 'word
An attempt to redefine a protected word causes an error:
word: "here"
** Script Error: Word word is protected, cannot modify.
** Where: word: "here"
A word can be unprotected as well using unprotect :
unprotect 'word
word: "here"
The protect and unprotect functions also
accept a block of words:
protect [this that other]
Important function and system words can be protected using the
protect-system function. Protecting function and system words
is especially useful for beginners who might accidentally set important words.
If protect-system is placed in your user.r file, then
all predefined words are protected.
6. Conditional Evaluation
As previously mentioned, blocks are not normally evaluated. A
do function is required to force a block to be evaluated. There
are times when you may need to conditionally evaluate a block. The following
section describes several ways to do this.
6.1 Conditional Blocks
The if function takes two arguments. The first argument is a
condition and the second argument is a block. If the condition is true
, the block is evaluated, otherwise it is not evaluated.
if now/time > 12:00 [print "past noon"]
past noon
The condition is normally an expression that evaluates to true or
false ; however, other values can also be supplied. Only a
false or a none value prevents the block from being evaluated.
All other values (including zero) are treated as true, and cause the
block to be evaluated. This can be useful for checking the results of
find, select, next, and
other functions that return none :
string: "let's talk about REBOL"
if find string "talk" [print "found"]
found
The either function extends if to include a
third argument, which is the block to evaluate if the condition is false:
either now/time > 12:00 [
print "after lunch"
][
print "before lunch"
]
after lunch
The either function also interprets a none value as
false.
Both the if and either functions return the
result of evaluating their blocks. In the case of an if, the
block value is only returned if the block is evaluated; otherwise, a
none is returned. The if function is useful for
conditional initialization of variables:
flag: if time > 13:00 ["lunch eaten"]
print flag
lunch eaten
Making use of the result of the either function, the
previous example could be rewritten as follows:
print either now/time > 12:00 [
"after lunch"
][
"before lunch"
]
after lunch
Since both if and either are functions,
their block arguments can be any expression that results in a block when
evaluated. In the following examples, words are used to represent the block
argument for if and either.
notice: [print "Wake up!"]
if now/time > 7:00 notice
Wake up!
notices: [
[print "It's past sunrise!"]
[print "It's past noon!"]
[print "It's past sunset!"]
]
if now/time > 12:00 second notices
It's past noon!
sleep: [print "Keep sleeping"]
either now/time > 7:00 notice sleep
Wake up!
The conditional expressions used for the first argument of both
if and either can be composed from a wide
variety of comparison and logic functions. Refer to the Math
Chapter for more information.
Avoid This Common Mistake
The most commonly made mistake in REBOL is to forget the second
block on either or add a second block to if. These examples
both creator hard-to-find errors:
either age > 10 [print "Older"]
if age > 10 [print "Older"] [print "Younger"]
These types of errors may be difficult to detect, so keep this
in mind if these functions do not seem to be doing what you
expect.
6.2 Any and All
The any and all functions offer a shortcut
to evaluating some types of conditional expressions. These functions can be used
in a number of ways:either in conjunction with if,
either, and other conditional functions, or separately.
Both any and all accept a block of
expressions, which is evaluated one expression at a time. The
any function returns on the first true expression, and the
all function returns on the first false expression. Keep in
mind that a false expression can also be none, and that a true
expression is any value other than false or
none.
The any function returns the first value that is not
false, otherwise it returns none. The all
function returns the last value if all the expressions are not false,
otherwise it returns none.
Both the any and all functions only
evaluate as much as they need. For example, once any has found
a true expression, none of the remaining expressions are evaluated. Here is an
example of using any :
size: 50
if any [size < 10 size > 90] [
print "Size is out of range."
]
The behavior of any is also useful for setting default
values. For example, the following lines set a number to 100, but only
when its value is none :
number: none
print number: any [number 100]
100
Similarly, if you have various potential values, you can use the first one
that actually has a value (is not none ):
num1: num2: none
num3: 80
print number: any [num1 num2 num3]
80
You can use any with functions like find to
always return a valid result:
data: [123 456 789]
print any [find data 432 999]
999
Similarly, all can be used for conditions that require all
expressions to be true :
if all [size > 10 size < 90] [print "Size is in range"]
Size is in range
You can verify that values have been set up before evaluating a function:
a: "REBOL/"
b: none
probe all [string? a string? b append a b]
none
b: "Core"
probe all [string? a string? b append a b]
REBOL/Core
6.3 Conditional Loops
The until and while functions repeat the
evaluation of a block until a condition is met.
The until function repeats a block until the evaluation of
the block returns true (that is, not false or none ).
The evaluation block is always evaluated at least once. The
until function returns the value of its block.
The example below will print each word in the color block. The block begins
by printing the first word of the block. It then moves to the next color for
each color in the block. The tail? function checks for the end
of the block, and will return true, which will cause the
until function to exit.
color: [red green blue]
until [
print first color
tail? color: next color
]
red
green
blue
The break function can be used to escape from the until
loop at any time.
The while function repeats the evaluation of its two block
arguments while the first block returns true. The first block is the
condition block, the second block is the evaluation block. When the condition
block returns false or none, the expression block will no
longer be evaluated and the loop terminates.
Here is a similar example to that show above. The while loop
will continue to print a color while there are still colors to print.
color: [red green blue]
while [not tail? color] [
print first color
color: next color
]
red
green
blue
The condition block can contain any number of expressions, so long as the
last expression returns the condition. To illustrate this, the next example adds
a print to the condition block. This will print the index value of the color. It
will then check for the tail of the color block, which is the condition used for
the loop.
color: [red green blue]
while [
print index? color
not tail? color
][
print first color
color: next color
]
1
red
2
green
3
blue
4
The last value of the block is returned from the while
function.
A break can be used to escape from the loop at any time.
6.4 Common Mistakes
Conditional expressions are only false when they return false or
none, and they are true when they return any
other value. All of the conditional expressions in the following examples return
true, even the zero and empty block values:
if true [print "yep"]
yep
if 1 [print "yep"]
yep
if 0 [print "yep"]
yep
if [] [print "yep"]
yep
The following conditional expressions return false :
if false [print "yep"]
if none [print "yep"]
Do not enclose conditional expressions in a block. Conditional expressions
enclosed in blocks, always return a true result:
if [false] [print "yep"]
yep
Do not confuse either with if. For example, if you
intend to write:
either some-condition [a: 1] [b: 2]
but write this instead:
if some-condition [a: 1] [b: 2]
the if function would ignore the second block. This would
not cause an error, but the second block would never get evaluated.
The opposite is also true. If you write the following line,
omitting a second block:
either some-condition [a: 1]
the either function will not evaluate the correct code and
may produce an erroneous result.
7. Repeated Evaluation
The while and until functions above where used to loop
until a condition was met. There are also several functions that
let you loop for a specified a number of times.
7.1 Loop
The loop function evaluates a block a specified number of
times. The following example prints a line of 40 dashes:
loop 40 [prin "-"]
----------------------------------------
Note that the prin function is similar to the
print function, but prints its argument without a line
termination.
The loop function returns the value of the final evaluation
of the block:
i: 0
print loop 40 [i: i + 10]
400
7.2 Repeat
The repeat function extends loop by
allowing you to monitor the loop counter. The repeat function's
first argument is a word that will be used to hold the count value:
repeat count 3 [print ["count:" count]]
count: 1
count: 2
count: 3
The final block value is also returned:
i: 0
print repeat count 10 [i: i + count]
55
In the previous examples, the count word only has its value within
the repeat block. In other words, the value of count
is local to the block. After repeat finishes, count
returns to any previous set value.
7.3 For
The for function extends repeat by allowing
the starting value, the ending value, and the increment to the value to be
specified. Any of the values can be positive or negative.
The example below begins at zero and counts to 50 by incrementing 10 each
time through the loop.
for count 0 50 10 [print count]
0
10
20
30
40
50
The for function cycles through the loop up to and including
the ending value. However, if the count exceeds the ending value, the loop is
still terminated. The example below specifies an ending value of 55. That value
will never be hit because the loop increments by 10 each time. The loop stops at
50.
for count 0 55 10 [prin [count " "]]
0 10 20 30 40 50
The next example shows how to count down. It begins at four and counts down
to zero one at a time.
for count 4 0 -1 [print count]
4
3
2
1
0
The for function also works for decimal numbers, money,
times, dates, series, and characters. Be sure that both the starting and ending
values are of the same datatype. Here are several examples of using the
for loop with other datatypes.
for count 10.5 0.0 -1 [prin [count " "]]
10.5 9.5 8.5 7.5 6.5 5.5 4.5 3.5 2.5 1.5 0.5
for money $0.00 $1.00 $0.25 [prin [money " "]]
$0.00 $0.25 $0.50 $0.75 $1.00
for time 10:00 12:00 0:20 [prin [time " "]]
10:00 10:20 10:40 11:00 11:20 11:40 12:00
for date 1-jan-2000 4-jan-2000 1 [prin [date " "]]
1-Jan-2000 2-Jan-2000 3-Jan-2000 4-Jan-2000
for char #"a" #"z" 1 [prin char]
abcdefghijklmnopqrstuvwxyz
The for function also works on series. The following example
uses for on a string value. The word end is defined as
the string with its current index at the d character. The
for function moves through the string series one character at a
time and stops when it reaches the character position defined to end:
str: "abcdef"
end: find str "d"
for s str end 1 [print s]
abcdef
bcdef
cdef
def
7.4 Foreach
The foreach function provides a convenient way to repeat the
evaluation of a block for each element of a series. It works for all types of
block and string series.
In the example below, each word in the block will be printed:
colors: [red green blue]
foreach color colors [print color]
red
green
blue
In the next example, each character in a string will be printed:
string: "REBOL"
foreach char string [print char]
R
E
B
O
L
In the example below, each filename in a directory block will be printed:
files: read %.
foreach file files [
if find file ".t" [print file]
]
file.txt
file2.txt
newfile.txt
output.txt
When a block contains groups of values that are related, the
foreach function can fetch all the values of the group at the
same time. For example, here is a block that contains a time, string, and price.
By providing the foreach function with a block of words for the
group, each of their values can be fetched and printed.
movies: [
8:30 "Contact" $4.95
10:15 "Ghostbusters" $3.25
12:45 "Matrix" $4.25
]
foreach [time title price] movies [
print ["watch" title "at" time "for" price]
]
watch Contact at 8:30 for $4.95
watch Ghostbusters at 10:15 for $3.25
watch Matrix at 12:45 for $4.25
In the above example, the foreach value block,
[time title price], specifies that three values are
to be fetched from movies for each evaluation of the block.
The variables used to hold the foreach values are local to
the block. Their value are only set within the block that is being repeated.
Once the loop has exited, the variables return to their previously set
values.
7.5 Forall and Forskip
Similar to foreach, the forall function
evaluates a block for every value in a series. However, there are some important
differences. The forall function is handed the series that is
set to the beginning of the loop. As it proceeds through the loop,
forall modifies the position within the series.
colors: [red green blue]
forall colors [print first colors]
red
green
blue
In the above example, after each evaluation of the block, the series is
advanced to its next position. When forall returns, the
color index is at the tail of the series.
To continue to use the series you will need to return it to its head position
with the following line:
colors: head colors
The forskip function evaluates a block for groups of values
in a series. The second argument to forskip is the count of how
many elements to move forward after each cycle of the loop.
Like forall, forskip is handed the series
with the series index set to where it is to begin. Then,
forskip modifies the index position as it continues the loop.
After each evaluation of the body block, the series index is advanced by the
skip amount to its next index position. The following example demonstrates
forskip :
movies: [
8:30 "Contact" $4.95
10:15 "Ghostbusters" $3.25
12:45 "Matrix" $4.25
]
forskip movies 3 [print second movies]
Contact
Ghostbusters
Matrix
In the above example, forskip returns with the
movies series at its tail position. You will need to use the
head function to return the series back to its head
position.
7.6 Forever
The forever function evaluates a block endlessly or until a
it encounters the break function.
The following example uses forever to check for the
existence of a file every ten minutes:
forever [
if exists? %datafile [break]
wait 0:10
]
7.7 Break
You can stop the repeated evaluation of a block with the
break function. The break function is useful when a
special condition is encountered and the loop must be stopped.
The break function works with all types of loops.
In the following example, the loop will break if a number is
greater than 5.
repeat count 10 [
if (random count) > 5 [break]
print "testing"
]
testing
testing
testing
The break function does not return a value from the loop
unless a /return refinement is used:
print repeat count 10 [
if (random count) > 5 [break/return "stop here"]
print "testing"
"normal exit"
]
testing
testing
testing
stop here
In the above example, if the repeat terminates without the condition
occurring, the block returns the string normal exit. Otherwise,
break/return will return the string stop here.
8. Selective Evaluation
There are several methods to selectively evaluate expressions in REBOL. These
methods provide a way for evaluation to branch many different ways, based on a
key value.
8.1 Select
The select function is often used to obtain a particular
value or block, given a target value. If you define a block of values and
actions, you can use select to search for the action that
corresponds to a value.
cases: [
center [print "center"]
right [print "right"]
left [print "left"]
]
action: select cases 'right
if action [do action]
right
In the previous example, the select function finds the word
right and returns the block that follows it. (If for some reason the
block was not found, then none would have been returned.) The block is
then evaluated. The values used in the example are words, but they can be any
kind of value:
cases: [
5:00 [print "everywhere"]
10:30 [print "here"]
18:45 [print "there"]
]
action: select cases 10:30
if action [do action]
here
8.2 Switch
The select function is used so often that there is a special
version of it called switch, which includes the evaluation of
the resulting block. The switch function makes it easier to
perform inline selective evaluation. For instance, to switch on a simple numeric
case:
switch 22 [
11 [print "here"]
22 [print "there"]
]
there
The switch function also returns the value of the block it
evaluates, so the previous example can also be written as:
str: copy "right "
print switch 22 [
11 [join str "here"]
22 [join str "there"]
]
right there
and:
car: pick [Ford Chevy Dodge] random 3
print switch car [
Ford [351 * 1.4]
Chevy [454 * 5.3]
Dodge [154 * 3.5]
]
2406.2
The cases can be any valid datatype, including numbers, strings, words,
dates, times, urls, and files. Here are some examples:
Strings:
person: "kid"
switch person [
"dad" [print "here"]
"mom" [print "there"]
"kid" [print "everywhere"]
]
everywhere
Words:
person: 'kid
switch person [
dad [print "here"]
mom [print "there"]
kid [print "everywhere"]
]
everywhere
Datatypes:
person: 123
switch type?/word [
string! [print "a string"]
binary! [print "a binary"]
integer! [print "an integer number"]
decimal! [print "a decimal number"]
]
an integer number
Files:
file: %rebol.r
switch file [
%user.r [print "here"]
%rebol.r [print "everywhere"]
%file.r [print "there"]
]
everywhere
URLs:
url: ftp://ftp.rebol.org
switch url [
http://www.rebol.com [print "here"]
http://www.cnet.com [print "there"]
ftp://ftp.rebol.org [print "everywhere"]
]
everywhere
Tags:
tag: <LI>
print switch tag [
<PRE> ["Preformatted text"]
<TITLE> ["Page title"]
<LI> ["Bulleted list item"]
]
Bulleted list item
Times:
time: 12:30
switch time [
8:00 [send wendy@domain.dom "Hey, get up"]
12:30 [send cindy@rebol.dom "Join me for lunch."]
16:00 [send group@every.dom "Dinner anyone?"]
]
8.2.1 Default Case
A default case can be specified when none of the other cases
match. Use the default refinement to specify a default:.
time: 7:00
switch/default time [
5:00 [print "everywhere"]
10:30 [print "here"]
18:45 [print "there"]
] [print "nowhere"]
nowhere
8.2.2 Common Cases
If you have common cases, where the result would be the same for several
values, you can define a word to hold a common block of code:
case1: [print length? url] ; the common block
url: http://www.rebol.com
switch url [
http://www.rebol.com case1
http://www.cnet.com [print "there"]
ftp://ftp.rebol.org case1
]
20
8.2.3 Other Cases
More than just blocks can be evaluated for cases. This example evaluates the
file that corresponds to a day of the week:
switch now/weekday [
1 %monday.r
5 %friday.r
6 %saturday.r
]
So, if it's Friday, the friday.r file is evaluated and its result is
returned from the switch. This type of evaluation also works for URLs:
switch time [
8:30 ftp://ftp.rebol.org/wakeup.r
10:30 http://www.rebol.com/break.r
18:45 ftp://ftp.rebol.org/sleep.r
]
The cases for switch are enclosed in a block, and therefore
can be defined apart from the switch statement:
schedule: [
8:00 [send wendy@domain.dom "Hey, get up"]
12:30 [send cindy@dom.dom "Join me for lunch."]
16:00 [send group@every.dom "Dinner anyone?"]
]
switch 8:00 schedule
9. Stopping Evaluation
Evaluation of a script can be stopped at any time by pressing
the escape key (ESC) on the keyboard or by using the halt and
quit functions.
The halt function stops evaluation and returns you to the
REBOL console prompt:
if time > 12:00 [halt]
The quit function stops evaluation and exits the REBOL
interpreter:
if error? try [print test] [quit]
10. Trying Blocks
There are times when you want to evaluate a block, but should an
error occur, you do not want to stop the evaluation of the rest
of your script.
For example, you might be performing a number division, but do
not want your script to stop if a divide-by-zero occurs.
The try function allows you to catch errors during the
evaluation of a block. It is almost identical to do. The
try function will normally return the result of the block;
however, if an error occurs, it will return an error value
instead.
In the following example, when the divide by zero occurs, the
script will pass an error back to the try function, and
evaluation will continue from that point.
for num 5 0 -1 [
if error? try [print 10 / num] [print "error"]
]
2
2.5
3.33333333333333
5
10
error
More about error handling can be found in the Errors Appendix.
|