Scheme procedure in a list

587 views Asked by At

So imagine i have a list '(+ (* (x) (5)) (2)) How would i make a procedure that changes the x to whichever parameter i give and then evaluates the function inside the list? ((calculate expression x))

I had some ideas but didn't get it to work. These are the helprocedures i made:

(define (atom? x)
  (not (pair? x)))

(define (deep-map f l)
  (cond
    ((null? l) '())
    ((atom? l) (f l))
    (else
     (cons (deep-map f (car l))
           (deep-map f (cdr l))))))


(define (deep-change e1 e2 l)
  (deep-map (lambda (x) (if (eq? x e1) e2 x)) l))

(define (go-through-list list)
  (if (null? list)
      '()
      ((car list) (go-through-list (cdr list)))))

Here is the main code:

    (define (calculate expression x)
  (let ((expressie (deep-change 'x x expression)))
    (('+ (deep-change '+ (+) expression)))
    (('- (deep-change '- (-) expression)))
    (('* (deep-change '* (*) expression)))
    (('/ (deep-change '/ (/) expression)))
    (go-through-list expression)))


  

I managed to change the x in to to parameter i give but have problems with the * and + inside the list.

2

There are 2 answers

0
emanrbesu On
(define (replace x y tree)
  (cond ((null? tree) tree)
         ((not (or (pair? tree) (list? tree))) (if (eq? x tree) y tree))
         (else (cons (replace x y (car tree))
                     (replace x y (cdr tree))))))

And then you can simply (replace 'x 42 expr).

Assuming the tree is then valid scheme code. You can simply eval it.

If you're trying to replace multiple variables, it might be wise write a replace-multiple function that will handle arbitrary number of variables so that you can do something like this

(replace-multiple '(x y z) '(1 2 3) expr)

Implementing this function is basically calling replace multiple times. e.g.

(replace 'x 1 (replace 'y 2 (replace 'z 3 expr)))

So you might want to use recursion there.

If your scheme has the fold operator (provided by srfi-1), use it because it essentially achieves the above.

It would probably look something like this:

(define (replace-multiple xs ys tree)
  (fold replace tree xs ys))
1
mnemenaut On

This could be an interesting question; consider this clarification:

Evaluate list representing expression with free variable without using eval

What is a way to develop a function to evaluate a Scheme expression built from the four arithmetic operations, numbers, and a free variable x, without using eval?
The function is given a list representing the expression and a value for x.
Example: (calculate '(+ (* x 5) 2) 3) => 17

Development is presented as a sequence of elaborations of the calculate function; each define has a Signature comment on the same line, informally describing argument/result types; function and following example(s) can be copy-pasted into a REPL.
Note: not all errors are detected; there is a compact version without comments at the end.

Getting started: write a function which works for the given example:

(define (calculate-0 expression-in-x value-for-x)  ;; '(+ (* x 5) 2) Number -> Number
  (if (equal? expression-in-x '(+ (* x 5) 2))
    (+ (* value-for-x 5) 2)
    (error #f "wrong expression" expression-in-x)))

(calculate-0 '(+ (* x 5) 2) 3) ;=> 17

Real function will have to extract pieces of expression
a simple example with all elements is '(+ x 1):

(define (calculate-1 expression value)  ;; '(+ x <n>) Number -> Number
  (let ([procedure  (car expression)]
        [argument-1 (cadr expression)]
        [argument-2 (caddr expression)])
    (if (and (eq? procedure '+)
             (eq? argument-1 'x)
             (number? argument-2))
      (+ value argument-2)
      (error #f "expression" expression))))

(calculate-1 '(+ x 1) 2) ;=> 3

+ in Scheme accepts any number of arguments, so replace if with map/cond:

(define (calculate-2 expression value)  ;; '(+ x|<n> ...) Number -> Number
  (let ([arguments (cdr expression)])
    (let ([arguments
            (map (lambda (argument)
                (cond         ;; (compare with (if ...) in calculate-1)
                  [(eq? argument 'x)  value ]
                  [(number? argument) argument ]
                  [else (error #f "argument" argument)]))
              arguments)])
      (apply + arguments))))

(calculate-2 '(+ 1 x) 2)    ;=> 3
(calculate-2 '(+ x 1 x) 3)  ;=> 7
(calculate-2 '(+) 99)       ;=> 0

Get all four operations working:

(define (calculate-3 expression value)  ;; '(op x|<n> ...) Number -> Number
  (let ([procedure (car expression)]
        [arguments (cdr expression)])
    (let ([arguments          ;; (same as calculate-2)
            (map (lambda (argument)
                (cond
                  [(eq? argument 'x)  value ]
                  [(number? argument) argument ]
                  [else (error #f "argument" argument)]))
              arguments)])
      (apply (case procedure
              [(+)  + ]
              [(-)  - ]
              [(*)  * ]
              [(/)  / ]
              [else (error #f "procedure" procedure)])
        arguments))))
        
(calculate-3 '(* x 5) 3)  ;=> 15

Allowing nested sub-forms needs just one small change:

(define (calculate-4 expression value)  ;; '(op x|<n>|Expr ...) Number -> Number
  (let ([procedure (car expression)]
        [arguments (cdr expression)])
    (let ([arguments
            (map (lambda (argument)
                (cond
                  [(eq? argument 'x)  value ]
                  [(number? argument) argument ]
                  [(pair? argument)                 ;; (<- only change)
                     (calculate-4 argument value) ] ;;
                  [else (error #f "argument" argument)]))
              arguments)])
      (apply (case procedure
              [(+)  + ]
              [(-)  - ]
              [(*)  * ]
              [(/)  / ]
              [else (error #f "procedure" procedure)])
        arguments))))
        
(calculate-4 '(+ (* x 5) 2) 3) ;=> 17

So there it is: try calculate-4 with the original example in the REPL:

$ scheme

> (calculate-4 '(+ (* x 5) 2) 3)
17
> ; works with all Scheme Numbers:
  (calculate-4 '(+ (* x 15/3) 2+2i) 3.0)
17.0+2.0i
> 

Not so fast ... expression is a list with the form of a Scheme expression using four operations, Numbers, and x. But the question doesn't require value to be a Number: procedures are first-class values in Scheme
(expression could be '(+ (x 3) 2) with value (lambda (n) (* n 5)) ):

(define (calculate-5 expression value)  ;; '(x|op x|<n>|Expr ...) Number|Lambda -> Value
  (let ([procedure (car expression)]
        [arguments (cdr expression)])
    (let ([arguments
            (map (lambda (argument)
                (cond
                  [(eq? argument 'x)  value ]
                  [(number? argument) argument ]
                  [(pair? argument)   (calculate-5 argument value) ]
                  [else (error #f "argument" argument)]))
              arguments)])
      (let ([procedure
              (cond           ;; (compare with argument cond above)
                [(eq? procedure 'x) value ]
                [(pair? procedure)  (calculate-5 procedure value)]
                [else (case procedure
                        [(+)  + ]
                        [(-)  - ]
                        [(*)  * ]
                        [(/)  / ]
                        [else (error #f "procedure" procedure)]) ]) ])
        (apply procedure arguments)))))
        
(calculate-5 '(+ (x 3) 2) (lambda (n) (* n 5))) ;=> 17

(And so, finally, our calculate function is "Hello World!" capable :)

$ scheme

> ;(copy-paste calculate-5 here)

> (calculate-5 '(x) (lambda _ 'Hello!))
Hello!
> 

Compact version (returns #f on error):


(define (calculate expr value)
  (call/cc (lambda (error)
  (let* ([proc (car expr)]
     [args (map (lambda (arg) (cond
              [(eq? arg 'x) value]
              [(number? arg) arg]
              [(pair? arg)
                (or (calculate arg value) (error #f))]
              [else (error #f)]))
            (cdr expr))]
     [proc (cond
            [(eq? proc 'x) value ]
            [(pair? proc) (calculate proc value)]
            [else (case proc [(+) +] [(-) -] [(*) *] [(/) /]
                    [else (error #f)])])])
    (apply proc args)))))