In the reading on vectors, we developed some
procedures that took vectors as arguments and constructed and returned
other vectors as results -- for instance, the double-every-element
procedure:
;;; double-every-element: construct a vector similar to a given vector of ;;; numbers, but with each element doubled ;; Given: ;; VEC, a vector of numbers. ;; Result: ;; DOUBLE-VEC, a vector of numbers. ;; Preconditions: ;; None. ;; Postconditions: ;; (1) The length of DOUBLE-VEC is equal to the length of VEC. ;; (2) Every element of DOUBLE-VEC is twice the corresponding element of ;; VEC. (define double-every-element (lambda (vec) ((vector-generator (lambda (position) (* 2 (vector-ref vec position)))) (vector-length vec))))
As we saw in the reading on side effects,
however, it is sometimes appropriate to replace the elements of the
original vector with the newly computed elements, instead of constructing a
completely new vector. For instance, we could write a double-every-element! procedure that doubles each element of a vector
in place. The following interactions demonstrate the effect of a
call to this procedure:
> (define sample-vector (vector 3 1 4 1 5 9)) > sample-vector #(3 1 4 1 5 9) > (double-every-element! sample-vector) > sample-vector #(6 2 8 2 10 18)
Here's how to write it:
;;; double-every-element!: destructively replace every element of a vector ;;; of numbers with its double. ;; Given: ;; VEC, a vector of numbers. ;; Results: ;; None. ;; Preconditions: ;; None. ;; Postcondition: ;; Every element of VEC is twice the element initially stored at the ;; same position in VEC. (define double-every-element! (lambda (vec) (let ((size (vector-length vec))) (let kernel ((position 0)) (if (< position size) (begin (vector-set! vec position (* 2 (vector-ref vec position))) (kernel (+ position 1))))))))
As the exclamation point at the end of double-every-element!
indicates, calling the procedure causes an irreversible change of state in
its argument, sample-vector. The original contents of that vector
are gone. Each of the original elements has been replaced by its double.
This ``destructive'' procedure is likely to be more efficient than double-every-element, because it is unnecessary to allocate storage for a
result vector, but it is also somewhat trickier to use. The timing
is important: one must not replace the elements of the vector too soon, at
a time when one still needs some of the old values for other computations.
This pattern of computation is common enough that it is useful to define a
destructive version of vector-map. Here is a demonstration of the
effect of this vector-map! procedure:
> (define sample-vector (vector 3 1 4 1 5 9)) > (vector-map! square sample-vector) > sample-vector #(9 1 16 1 25 81)
And here is its definition:
;;; vector-map!: replace each element of a given vector with the result ;;; of applying a given procedure to that element ;; Given: ;; PROC, a unary procedure. ;; VEC, a vector. ;; Results: ;; None. ;; Precondition: ;; Every element of VEC satisfies any conditions that PROC imposes on its ;; argument. ;; Postcondition: ;; Each element of VEC is the result of applying PROC to the value ;; initially stored at the same position in VEC. (define vector-map! (lambda (proc vec) (let ((size (vector-length vec))) (let kernel ((position 0)) (if (< position size) (begin (vector-set! vec position (proc (vector-ref vec position))) (kernel (+ position 1))))))))
Other built-in Scheme data structures have mutator procedures as well:
The string-set! procedure takes three arguments -- a string
str, a natural number k less than the length of str,
and a character ch -- and, as a side effect, replaces the character
at position k in str with ch:
> (define sample-string (string #\s #\a #\m #\p #\l #\e)) > sample-string "sample" > (string-set! sample-string 1 #\i) > sample-string "simple"
Like vectors named by quoted mesh-and-parentheses expressions, strings
named by constants beginning and ending with double quotation marks are
``immutable,'' and it is an error to apply string-set! to
them. (DrScheme does not object to the erroneous operation, but some
other implementations of Scheme do.)
The set-car! procedure takes two arguments -- a pair pr and a
Scheme value obj -- and, as a side effect, replaces the car of pr with obj.
> (define sample-pair (cons 'alpha 'beta)) > sample-pair (alpha . beta) > (set-car! sample-pair 'gamma) > sample-pair (gamma . beta)
Similarly, the set-cdr! procedure takes pr and obj as
arguments and, as a side effect, replaces the cdr of pr with obj.
Pairs that are named by quoted list or dotted-pair expressions are
immutable, and it is an error to apply set-car! or set-cdr!
to such a pair.
The set-cdr! procedure in particular should be used with great
caution, since it is all too easy to create (by accident) a data structure
for which the box-and-pointer diagram contains a cycle -- for instance, a
structure in which box A contains a pointer to box B, which contains a
pointer back to box A.
When a list-recursive procedure is confronted with such a structure, a runaway recursion results, since you can't reach a null list by repeatedly taking the cdr. Even printing out such a structure can be problematical, as the following interaction with the SCM implementation of Scheme demonstrates:
> (define a-box (cons 'a 'who-cares)) #<unspecified> > (define b-box (cons 'b 'irrelevant)) #<unspecified> > (set-cdr! a-box b-box) #<unspecified> > (set-cdr! b-box a-box) #<unspecified> > a-box (a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b user interrupt
Since the procedure that does the printing works by writing out the car and then recursively dealing with the cdr, the runaway recursion produces runaway output, continuing until the user steps in to interrupt the computation. DrScheme avoids this problem by using a special output syntax for pair structures containing cycles:
> a-box #0=(a b . #0#)