Here is the question from Mitchell Wand's Essentials of Programming Language:
Exercise 1.19
[⋆ ⋆](list-set lst n x)returns a list likelst, except that the n-th element, using zero-based indexing, isx.> (list-set ’(a b c d) 2 ’(1 2)) (a b (1 2) d) > (list-ref (list-set ’(a b c d) 3 ’(1 5 10)) 3) (1 5 10)
According to what the book says, I analyze
- the contract of this procedure, and
- the inductive data types involved in this procedure, which are
List-of-(SchemeValue)andNat.
Below are some test-cases:
(write (list-set '(a b c d) 2 '(1 2)))
(newline)
;; should output (a b (1 2) d)
(write (list-ref (list-set '(a b c d) 3 '(1 5 10)) 3))
(newline)
;; should output (1 5 10)
(write (list-set '(a b c d e) 0 '(x y)))
(newline)
;; should output ((x y) b c d e)
(write (list-set '(a b c d e) 4 '(x y)))
(newline)
;; should output (a b c d (x y))
Here is my first try with racket, and it works:
#lang eopl
;; Contract : list-set : List-of-(SchemeValue) × Nat × SchemeValue → List-of-(SchemeValue)
;; Usage : (list-set lst n x ) = replace the nth element of lst by x
;; Syntax 1 : Nat ::= 0 | 1 + Nat
;; Syntax 2 : List-of-(SchemeValue) ::= () | (SchemeValue . List-of-(SchemeValue))
(define (list-set lst n x)
(if (null? lst) '()
(let ([proc (lambda (tail)
(cons x tail))]
[head (car lst)]
[tail (cdr lst)])
(if (zero? n) (proc tail)
(let ([procN head]
[pred (- n 1)])
(cons procN
(list-set tail pred x)))))))
Here is my second try, it also works:
#lang eopl
;; Contract : list-set : List-of-(SchemeValue) × Nat × SchemeValue → List-of-(SchemeValue)
;; Usage : (list-set lst n x ) = replace the nth element of lst by x
;; Syntax 1 : Nat ::= 0 | 1 + Nat
;; Syntax 2 : List-of-(SchemeValue) ::= () | (SchemeValue . List-of-(SchemeValue))
(define (list-set lst n x)
(if (null? lst) '()
(let ([proc (lambda (tail)
(cons x tail))]
[head (car lst)]
[tail (cdr lst)])
(if (zero? n) (proc tail)
(let ([proc2R (lambda (tail)
(cons head tail))]
[pred (- n 1)])
(proc2R (list-set tail pred x)))))))
I thought it might be possible to re-implement the 2nd version of code with foldrNat, which is a foldr function for the Nat inductive data type. However, the result is not satisfied. Here is my third try:
#lang eopl
;; Contract : list-set : List-of-(SchemeValue) × Nat × SchemeValue → List-of-(SchemeValue)
;; Usage : (list-set lst n x ) = replace the nth element of lst by x
;; Syntax 1 : Nat ::= 0 | 1 + Nat
;; Syntax 2 : List-of-(SchemeValue) ::= () | (SchemeValue . List-of-(SchemeValue))
(define (list-set lst n x)
(if (null? lst) '()
(letrec ([proc (lambda (tail)
(cons x tail))]
[head (car lst)]
[tail (cdr lst)]
[proc2R (lambda (tail)
(cons head tail))]
[i (proc tail)])
(foldrNat proc2R i n))))
;; contract : foldrNat : (1 -> B -> B) × B × Nat → B
;; usage : (foldrNat proc2R i n) = ...
;; syntax : Nat ::= 0 | 1 + Nat
(define (foldrNat proc2R i n)
(if (zero? n) i
(let ([pred (- n 1)])
(proc2R (foldrNat proc2R i pred)))))
;; contract : foldrLst : (A -> B -> B) × B × List-of-(A) -> B
;; uage : (foldrLst proc2R i lst ) = ...
;; syntax : List-of-(A) ::= () | (A . List-of-(A))
(define (foldrLst proc2R i lst)
(if (null? lst) i
(let ([head (car lst)]
[tail (cdr lst)])
(proc2R head (foldrLst proc2R i tail)))))
I am still frustrated by the problem: why doesn't the 3rd attempt not work, and how can I fix it?
I would appreciate if you can analyze this from Haskeller's perspective, i.e., Algebra, co-Algebra, foldable, etc. — because I feel like this involves the foldr for a product of inductive type List-of-(SchemeValue) × Nat.