0

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))))
  • 1
    You would need to implement `eval` since the symbol `+` and the procedure that is the result of evaluating `+` has nothing in common. If you want a macro then you need to loose the quote as it is misplaced and gives the impression you can pass evaluated expressions, which you cannot. – Sylwester Nov 23 '19 at 14:16
  • I've added code that hopefully reveals my confusion around how to use `eval`. – Travis Hinkelman Nov 23 '19 at 15:09
  • 1
    `'(apply map (lambda ,key-list body) ,values-list)` doesn't look right. `uquote` only works with `quasiquote`, not `quote`. – Sylwester Nov 23 '19 at 19:11
  • For interested readers: This question reflected some confusion I had about how to approach this type of problem. I more recently asked a more focused question related to the same general problem and arrived at a solution based on macros. https://stackoverflow.com/questions/60625913/chez-scheme-macro-for-hiding-lambdas – Travis Hinkelman Mar 14 '20 at 00:29

0 Answers0