I'm learning scheme and bumping into the limits of my current knowledge. I suspect that a macro might be the right tool for this job, but...
I would like to write a procedure where the contents of an association list are manipulated based on a user-supplied expression. If we have this association list,
(define a '((x (1 2 3)) (y (4 5 6)) (z (7 8 9))))
then I would like to have a procedure that adds new elements to the association list based on the current elements of the association list.
(define (add-element alist proc)
;; this is part that I'm struggling with
)
Here a couple examples of usage:
(add-element a '(w (* x y z)))
(add-element a '(w (- (+ y z) (/ y 2)))
I want to transform those inputs into the following (for the 2nd example):
(cons
(list 'w
(map (lambda (y z)
(- (+ y z) (/ y 2)))
(cadr (assoc 'y a))
(cadr (assoc 'z a))))
a)
There are pieces of this problem that seem approachable. For example, the structure of the user-defined proc is such that (car proc)
provides the key for the new association list element and (cadr proc)
provides the lambda
body. I wrote a procedure (not shown here) to flatten (car proc)
, extract symbols that are found in the keys of the association list, and remove duplicates. I can then use that list of symbols to map over the association list and get a list of the relevant elements from the association list such that I could use the apply map
pattern with the lambda
function. But I can't figure out how to put together the lambda
function with a list of symbols. Or how to use (cadr proc)
in the lambda
body.
EDIT: Adding code to show my failed attempt to use eval
.
;; https://stackoverflow.com/questions/8382296/scheme-remove-duplicated-numbers-from-list
(define (remove-duplicates ls)
(cond [(null? ls)
'()]
[(member (car ls) (cdr ls))
(remove-duplicates (cdr ls))]
[else
(cons (car ls) (remove-duplicates (cdr ls)))]))
;; https://stackoverflow.com/questions/28753729/how-to-manually-flatten-a-list-in-racket-scheme
(define (flatten obj)
(cond [(null? obj) '()]
[(list? obj)
(append (flatten (car obj))
(flatten (cdr obj)))]
[else (list obj)]))
(define (alist-contains? alist symbol)
(if (member symbol (map car alist)) #t #f))
(define (extract-alist-keys alist expr)
(remove-duplicates
(filter
(lambda (x)
(and (symbol? x) (alist-contains? alist x)))
(flatten expr))))
(define (add-element alist proc)
(define new-key (car proc))
(define body (cadr proc))
(define key-list (extract-alist-keys alist body))
(define values-list (map (lambda (x) (cadr (assoc x alist))) key-list))
(define new-values (eval '(apply map (lambda ,key-list body) ,values-list)))
(cons (list new-key new-values) alist))
(add-element a '(w (- (+ y z) (/ y 2))))