Previous
Up
Next
Compound data types are built by combining values from
other data types in structured ways.
2.2.1 Strings
Strings are sequences of characters (not to be confused with
symbols, which are simple data that have a sequence of
characters as their name). You can specify strings by
enclosing the constituent characters in double-quotes.
Strings evaluate to themselves.
"Hello, World!"
=> "Hello, World!"
The procedure string takes a bunch of characters and
returns the string made from them:
(string #\h #\e #\l #\l #\o)
=> "hello"
Let us now define a global variable greeting.
(define greeting "Hello; Hello!")
Note that a semicolon inside a string datum does not
trigger a comment.
The characters in a given string can be individually
accessed and modified. The procedure string-ref takes a
string and a (0-based) index, and returns the character at
that index:
(string-ref greeting 0)
=> #\H
The procedure string-set! replaces the character at an
index:
(string-set! greeting 1 #\a)
greeting
=> "Hallo; Hello!"
New strings can be created by appending other strings:
(string-append "E "
"Pluribus "
"Unum")
=> "E Pluribus Unum"
You can make a string of a specified length, and fill it
with string-set! later.
(define a-3-char-long-string (make-string 3))
The predicate for checking stringness is string?.
2.2.2 Vectors
Vectors are sequences like strings, but their elements can
be anything, not just characters. Indeed, the elements can
be vectors themselves, which is a good way to generate
multidimensional vectors.
Here's a way to create a vector of the first five integers:
(vector 0 1 2 3 4)
=> #(0 1 2 3 4)
Note Scheme's representation of a vector value: a #
character followed by the vector's contents enclosed in
parentheses.
In analogy with make-string, the procedure
make-vector makes a vector of a specific length:
(define v (make-vector 5))
The procedures vector-ref and vector-set! access and
modify vector elements.
The predicate for checking if something is a vector is vector?.
2.2.3 Dotted pairs and lists
A
dotted pair is a compound value made by combining
any two arbitrary values into an ordered couple. The
first element is called the
car, the second
element is called the
cdr, and the combining
procedure is
cons.
(cons 1 #t)
=> (1 . #t)
Dotted pairs are not self-evaluating, and so to specify
them directly as data (ie, without producing them via
a cons-call), one must explicitly quote them:
'(1 . #t) => (1 . #t)
(1 . #t) -->ERROR!!!
The accessor procedures are car and cdr:
(define x (cons 1 #t))
(car x)
=> 1
(cdr x)
=> #t
The elements of a dotted pair can be replaced by the
mutator procedures set-car! and set-cdr!:
(set-car! x 2)
(set-cdr! x #f)
x
=> (2 . #f)
Dotted pairs can contain other dotted pairs.
(define y (cons (cons 1 2) 3))
y
=> ((1 . 2) . 3)
The car of the car of this list is 1.
The cdr of the car of this list is 2.
Ie,
(car (car y))
=> 1
(cdr (car y))
=> 2
Scheme provides procedure abbreviations for cascaded
compositions of the car and cdr procedures.
Thus, caar stands for ``car of car of'',
and cdar stands for ``cdr of car of'', etc.
(caar y)
=> 1
(cdar y)
=> 2
c...r-style abbreviations for upto four cascades are
guaranteed to exist. Thus, cadr, cdadr, and
cdaddr are all valid. cdadadr might be pushing it.
When nested dotting occurs along the second element,
Scheme uses a special notation to represent the
resulting expression:
(cons 1 (cons 2 (cons 3 (cons 4 5))))
=> (1 2 3 4 . 5)
Ie, (1 2 3 4 . 5) is an abbreviation for (1
. (2 . (3 . (4 . 5)))). The last cdr of this
expression is 5.
Scheme provides a further abbreviation if the last cdr
is a special object called the empty list, which
is represented by the expression (). The empty
list is not considered self-evaluating, and so one
should quote it when supplying it as a value in a
program:
'() => ()
The abbreviation for a dotted pair of the form (1
. (2 . (3 . (4 . ())))) is
(1 2 3 4)
This special kind of nested dotted pair is called a
list. This particular list is four elements
long. It could have been created by saying
(cons 1 (cons 2 (cons 3 (cons 4 '()))))
but Scheme provides a procedure called list that
makes list creation more convenient. list takes
any number of arguments and returns the list containing
them:
(list 1 2 3 4)
=> (1 2 3 4)
Indeed, if we know all the elements of a list, we can use
quote to specify the list:
'(1 2 3 4)
=> (1 2 3 4)
List elements can be accessed by index.
(define y (list 1 2 3 4))
(list-ref y 0) => 1
(list-ref y 3) => 4
(list-tail y 1) => (2 3 4)
(list-tail y 3) => (4)
list-tail returns the tail of the list
starting from the given index.
The predicates pair?, list?, and null?
check if their argument is a dotted pair, list, or the
empty list, respectively:
(pair? '(1 . 2)) => #t
(pair? '(1 2)) => #t
(pair? '()) => #f
(list? '()) => #t
(null? '()) => #t
(list? '(1 2)) => #t
(list? '(1 . 2)) => #f
(null? '(1 2)) => #f
(null? '(1 . 2)) => #f
2.2.4 Conversions between data types
Scheme offers many procedures for converting among
the data types. We already know how to convert between
the character cases using
char-downcase and
char-upcase. Characters can be converted into
integers using
char->integer, and integers can be
converted into characters using
integer->char.
(The integer corresponding to a character is usually
its ascii code.)
(char->integer #\d) => 100
(integer->char 50) => #\2
Strings can be converted into the corresponding list of
characters.
(string->list "hello") => (#\h #\e #\l #\l #\o)
Other conversion procedures in the same vein are
list->string, vector->list, and
list->vector.
Numbers can be converted to strings:
(number->string 16) => "16"
Strings can be converted to numbers. If the string
corresponds to no number, #f is returned.
(string->number "16")
=> 16
(string->number "Am I a hot number?")
=> #f
string->number takes an optional second argument,
the radix.
(string->number "16" 8) => 14
because 16 in base 8 is the number fourteen.
Symbols can be converted to strings, and vice versa:
(symbol->string 'symbol)
=> "symbol"
(string->symbol "string")
=> string
Previous
Up
Next