-1

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.

E.T.
  • 1
  • what is the call you're making? what are the results? (unexpected answer, error -- if so, what is the error message? -- etc.) please include everything relevant. – Will Ness Nov 27 '21 at 15:23
  • `(let ((expressie ;....` -- is this a typo? As far as I can tell `expressie` is never used again. – ad absurdum Nov 27 '21 at 16:13
  • `(+)` is not the procedure `+`, it is 0. If `x` is a procedure, `(x)` calls the procedure without any arguments. – molbdnilo Nov 27 '21 at 21:19
  • The `calculate` procedure makes no sense at all. What do you think an expression like `(('+ (deep-change '+ (+) expression)))` does? – molbdnilo Nov 27 '21 at 21:23
  • `(+)` is applying `+` with no arguments. It is not the same as the variable `+` that evaluates to the procedure you are applying in your logic. Same for `-` and `(/)` and `(*)` but they have `1` as default accumulator instead of 0. – Sylwester Nov 29 '21 at 10:24
  • 1
    Question title is "**Scheme procedure** in a **list**", but the list `'(+ (* (x) (5)) (2))` does not represent a Scheme _expression_ (nor a procedure or function). @E.T. it's good that you had some ideas and wrote some code, but can you edit the question to clarify the sorts of values allowed for `expression` and `x` in `(calculate expression x)` ? (or respond to questions in comments?) – mnemenaut Nov 29 '21 at 17:21

2 Answers2

1
(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))
Dharman
  • 30,962
  • 25
  • 85
  • 135
emanrbesu
  • 15
  • 5
1

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)))))

mnemenaut
  • 684
  • 2
  • 11
  • The OP list `'(+ (* (x) (5)) (2))` does not represent a Scheme _expression_ (or 'procedure'), and can't be `eval`ed. `calculate` could be modified to ignore extra parentheses (but `(x)` would be a special case). – mnemenaut Dec 01 '21 at 09:22