Data Structure:1. Functional-Style Linked Lists

1. Functional-Style Linked Lists

We frequently use linked lists in our programs, but Scheme makes that easy because it provides linked lists as a native data type, along with a rich set of operations on them. In today’s exercise we will implement lists in the functional style that Scheme provides natively.

In Scheme, a list is really nothing more than an array with two slots, known as the car and cdr of a pair; a list with no elements is called the null list. We frequently think of lists as a sequence of elements, but in fact a list is no more than a chain of pairs, of which the cdr of each pair is another list, the chain terminating in a null list. Depending on the version of Scheme that you use, the car and cdr of the pair may or may not be mutable; traditionally, they have been mutable, but the most recent approved standard R6RS makes pairs immutable (that is controversial, and many implementations of Scheme ignore it and leave pairs mutable). Still, immutable pairs are more closely aligned with the spirit of functional languages, and your implementation today should provide immutable pairs.

Your task is to implement a small library of list operators; you should include at least nil and a predicate to recognize it, a procedure to build pairs and two procedures to extract the pieces of a pair, functions to extract the nth item of a list and to determine its length, and functions to reverse the elements of a list and append two lists; you are welcome to provide more operators if you wish. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

We represent a pair as a vector with two slots. The null pair is represented as a zero-length vector. Head and tail extract the various elements:

(define nil (vector)) ; '()

(define (nil? xs) (zero? (vector-length xs))) ; null?

(define (pair x xs) (vector x xs)) ; cons

(define (head xs) ; car
  (if (nil? xs)
      (error 'head "exhausted")
      (vector-ref xs 0)))

(define (tail xs) ; cdr
  (if (nil? xs)
      (error 'tail "exhausted")
      (vector-ref xs 1))

With that, we can build a list like this:

> (define xs (pair 0 (pair 1 (pair 2 (pair 3 (pair 4 (pair 5 (pair 6 (pair 7 nil)))))))))
> (head xs)
0
> (head (tail (tail (tail xs))))
3

The nth item is found by counting down the elements of a list until the desired item is found; the length of a list is found by counting each element until the last is found. Old-time lispers call the operation of inspecting each element of a list cdr-ing down the list:

(define (nth xs n) ; list-ref
  (if (nil? xs)
        (error 'nth "exhausted")
      (if (zero? n)
          (head xs)
          (nth (tail xs) (- n 1))))

(define (len xs) ; length
  (if (nil? xs)
      0
      (+ (len (tail xs)) 1)))

> (nth xs 5)
5
> (len xs)
8

The append function must cdr down the first list; the second list is untouched:

(define (app x1 x2) ; append
  (if (nil? x1)
      x2
      (pair (head x1)
            (app (tail x1) x2))))

> (nth (app xs (pair 8 (pair 9 nil))) 9)
9
> (nth (app xs (pair 8 (pair 9 nil))) 10)
error: nth: exhausted

A naive reverse calls append for each item in the list; an iterative reverse simply cdrs down the input as it accumulates the output:

(define (rev-recursive xs) ; reverse
  (if (nil? xs)
      nil
      (app (rev-recursive (tail xs))
           (pair (head xs) nil))))

(define (rev-iterative xs) ; reverse
  (let loop ((xs xs) (rs nil))
    (if (nil? xs)
        rs
        (loop (tail xs) (pair (head xs) rs)))))

> (nth (rev-recursive xs) 4)
3
> (nth (rev-iterative xs) 4)
3

We’ll stop there; the Standard Prelude gives many more examples of list operations.

; functional-style linked lists

(define (error symb str)
  (display "error: ")
  (display (symbol->string symb))
  (display ": ")
  (display str)
  (newline))

(define nil (vector)) ; '()

(define (nil? xs) (zero? (vector-length xs))) ; null?

(define (pair x xs) (vector x xs)) ; cons

(define (head xs) ; car
  (if (nil? xs)
      (error 'head "exhausted")
      (vector-ref xs 0)))

(define (tail xs) ; cdr
  (if (nil? xs)
      (error 'tail "exhausted")
      (vector-ref xs 1)))

(define xs (pair 0 (pair 1 (pair 2 (pair 3 (pair 4 (pair 5 (pair 6 (pair 7 nil)))))))))
(display (head xs)) (newline)
(display (head (tail (tail (tail xs))))) (newline)

(define (nth xs n) ; list-ref
  (if (nil? xs)
    (error 'nth "exhausted")
    (if (zero? n)
        (head xs)
        (nth (tail xs) (- n 1)))))

(define (len xs) ; length
  (if (nil? xs)
      0
      (+ (len (tail xs)) 1)))

(display (nth xs 5)) (newline)
(display (len xs)) (newline)

(define (app x1 x2) ; append
  (if (nil? x1) x2
    (pair (head x1)
          (app (tail x1) x2))))

(display (nth (app xs (pair 8 (pair 9 nil))) 9)) (newline)
(display (nth (app xs (pair 8 (pair 9 nil))) 10)) (newline)

(define (rev-recursive xs) ; reverse
  (if (nil? xs) nil
    (app (rev-recursive (tail xs))
         (pair (head xs) nil))))

(define (rev-iterative xs) ; reverse
  (let loop ((xs xs) (rs nil))
    (if (nil? xs)
        rs
        (loop (tail xs) (pair (head xs) rs)))))

(display (nth (rev-recursive xs) 4)) (newline)
(display (nth (rev-iterative xs) 4)) (newline)

output:

0
3
5
8
9
error: nth: exhausted
#
3
3

See more on [ProgrammingProxis](http://programmingpraxis.com/contents/themes/#Data Structures)

你可能感兴趣的:(Data Structure:1. Functional-Style Linked Lists)