Approximate overview
; Set up the parameters of our experiment so that they are easy to change
(define size 10000) ; The size of our structure
(define rounds 50000) ; The number of times we look in the structure
; Build a list/vector
(define list-of-values (range size))
; (define vector-of-values (list->vector list-of-values))
; We will be ref-ing the list/vector randomly. For clarity and
; replicability, we're going to make a list of all of the values
; we plan to ref (the "probes")
(define list-of-probes (random-nums rounds size))
; We time the cost of ref-ing the vector. So that the result of
; the lots and lots of probing does not show up on the screen, we
; store it in `list-result`.
(define list-result
(time (map (section list-ref list-of-values <>) list-of-probes)))
What you might have found
list-ref scales with the position in
the listvector-ref does not depend on the
size of the vector.Broader idea: How do we test assertions about speed, particularly when most individual operations are lighting fast?
Question: Why does the time for list-ref scale with the position in
the list?
To get to element
k, you have to callcdrktimes.
In a vector, you do a little math and you can figure out where in the vector you are.
Could you review recursion over vectors?
Sure. There are generally two kinds of recursion we do over vectors: Recursion in which we are grabbing elements from the vector and recursion in which we are changing/setting elements in the vector.
In both cases, we need to write a helper that keeps track of our position in the vector (along with the vector). That is, in effect, numeric recursion.
We can count down from the top index (length - 1) to zero, or up from 0 to the top index. Some of us prefer counting down so that we don’t have to repeatedly call
vector-length. However,vector-lengthis a cheap operation.
; Extraction, counting down
(if (zero? pos)
(base-case-computation (vector-ref vec 0))
(combine (vector-ref vec pos)
(recurse vec (- pos 1))))
; Extraction, counting up
(if (= pos (vector-length vec))
base-value
(combine (vector-ref vec pos)
(recurse vec (+ pos 1))))
; Mutation, counting down
(when (>= pos 0)
(do-something-to! vec pos)
(recurse vec (- pos 1)))
; Mutation, counting up
(when (< pos (vector-length vec))
(do-someting-to! vec pos)
(recurse vec (+ pos 1)))
For example, let’s build a simplified version of
rangethat constructs a vector from 0 to n-1.
(define iota
(lambda (n)
(iota/helper 0 (make-vector n) n)))
(define iota/helper
(lambda (pos vec n)
(when (<= pos n)
(vector-set! vec pos pos)
(iota/helper (+ 1 pos) vec n))))
Whoops. This version has (at least) two problems. Can you figure out what they are?
n is not a valid index, so the “continue” policy should be (< pos n).
*Let’s fix it.
(define iota
(lambda (n)
(let ([vec (make-vector n)])
(iota/helper 0 vec n)
vec)))
(define iota/helper
(lambda (pos vec n)
(when (< pos n)
(vector-set! vec pos pos)
(iota/helper (+ 1 pos) vec n))))
(test-equal? "zero" (iota 0) (vector))
(test-equal? "five" (iota 5) '#(0 1 2 3 4))
vectors-continued.rkt