1

I am trying to do a function that takes a list of characters as input, and returns a list that contains all the characters before a specific character given in a condition, so that I can evaluate a postfix expression.

Example: the user enters the string " 5 =b 10 * =c "

First step I do is to convert this string to a list using string->list, so I get a list like this

(#\5 #\space #\=#\b #\space #\10 #\space #\* #\space #\=#\c #\space)

Then I start reading the list and I stop once I read the character #\=, and I put all the characters before it in a list1, and then all what's after the character in list2. So I get list1 (#\5) and list2 is (#\b #\space #\10 #\space #\* #\space #\=#\c #\space)

And I assign the 5 to the b which is the (car list2), then continue reading list2 until I read another =, then I put all the elements before the second = in a list which is (#\b #\space #\10 #\space #\*) in a list, then I calculate b * 10 , and stock the result in c which is the first element of the last list.

I wrote this function

(define affect
  (lambda(l1 l2 l3)
    (cond ((null? l1)'())
          ((eq? (car l1) #\=)(append(cons (cdr l1)l2)))
          (else (affect (cdr l1)(cons(car l1)l2)l3)))))

but it seems like it is not really what I want and everything is getting more complicated... Any idea? Just suggest an idea how to solve such a problem.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Zok
  • 355
  • 2
  • 15

1 Answers1

3

You don't mention which implementation you're using so I'll go with Racket which is what I am most familiar with.

Some remarks:

  • I don't see why you would manage variables here because they are not used (yet)
  • this is an RPN calculator so you need to have some kind of stack (a list is very handy here)
  • I would rather work with strings here than characters

I created a working implementation using a named let which is the looping construct that I would use here. I am also using let* and a few handy procedures from Racket like substring and string-split. I don't know if you are allowed to use those but you can always create them yourself.

So here is an example implementation which is neither complete nor fool-proof but it correctly processes your example input:

(define (evaluate str)
  (let loop ((lst (string-split str)) ; named let for looping
             (stack '())
             (vars '()))
    (printf "lst=~v  stack=~a  vars=~a\n" lst stack vars) ; display, for debugging purposes
    (if (null? lst)
        (car stack)  ; done, return top of stack
        (let* ((c  (car lst))           ; element to process
               (n  (string->number c))  ; try to convert c to a number; returns #f if ko
               (op (string->symbol c))) ; convert c to a symbol
          ; recursive call to loop
          (loop
           ; first element is replaced by rest of list
           (cdr lst)
           ; second element is the (updated) stack
           (cond
             (n           (cons n stack))
             ((eq? op '*) (cons (* (first stack) (second stack)) (cddr stack)))
             (else        stack))
           ; third element is the (updated) list of variables
           (if (string=? (substring c 0 1) "=")
               (cons (list (substring c 1) (car stack)) vars)
               vars))))))

The display will show you the intermediary results:

> (evaluate "5 =b 10 * =c ")
lst='("5" "=b" "10" "*" "=c")  stack=()  vars=()
lst='("=b" "10" "*" "=c")  stack=(5)  vars=()
lst='("10" "*" "=c")  stack=(5)  vars=((b 5))
lst='("*" "=c")  stack=(10 5)  vars=((b 5))
lst='("=c")  stack=(50)  vars=((b 5))
lst='()  stack=(50)  vars=((c 50) (b 5))
50

EDIT

Here's a version using a list of chars. I have added an additional list token which collects the chars to be processed. Whenever a token needs to be processed I'm converting it back to a string which should be acceptable.

I have also added a helper function cdr0 which is incredibly handy in order not to make the recursive calls to loop completely unreadable:

(define (evaluate str)
  (define (cdr0 lst) (if (null? lst) lst (cdr lst))) ; helper: skip one element if the list is not yet empty
  (let loop ((lst (string->list str)) (token '()) (stack '()) (vars  '()))
    (printf "lst=~v  token=~v  stack=~a  vars=~a\n" lst token stack vars) ; display, for debugging purposes
    (cond
      ; end of processing -> return top of stack, we're done
      ((and (null? token) (null? lst)) (car stack))
      ; end of word or end of list -> process
      ((and (or (null? lst) (eqv? (car lst) #\space))
            (not (null? token)))
       (let* ((token-string (list->string (reverse token)))
              (n            (string->number token-string)))  ; try to convert c to a number; returns #f if ko
         (cond
           ; number
           (n
            (loop (cdr0 lst) '() (cons n stack) vars))
           ; variable assignment
           ((string=? (substring token-string 0 1) "=")
            (loop (cdr0 lst) '() stack (cons (list (substring token-string 1) (car stack)) vars)))
           ; multiplication
           ((string=? token-string "*")
            (loop (cdr0 lst) '() (cons (* (first stack) (second stack)) (cddr stack)) vars))
           ; none of these
           (else (error "wot?")))))
      (else
       (loop (cdr lst) (cons (car lst) token) stack vars)))))

Testing:

> (evaluate "5 =b 10 * =c")
lst='(#\5 #\space #\= #\b #\space #\1 #\0 #\space #\* #\space #\= #\c)  token='()  stack=()  vars=()
lst='(#\space #\= #\b #\space #\1 #\0 #\space #\* #\space #\= #\c)  token='(#\5)  stack=()  vars=()
lst='(#\= #\b #\space #\1 #\0 #\space #\* #\space #\= #\c)  token='()  stack=(5)  vars=()
lst='(#\b #\space #\1 #\0 #\space #\* #\space #\= #\c)  token='(#\=)  stack=(5)  vars=()
lst='(#\space #\1 #\0 #\space #\* #\space #\= #\c)  token='(#\b #\=)  stack=(5)  vars=()
lst='(#\1 #\0 #\space #\* #\space #\= #\c)  token='()  stack=(5)  vars=((b 5))
lst='(#\0 #\space #\* #\space #\= #\c)  token='(#\1)  stack=(5)  vars=((b 5))
lst='(#\space #\* #\space #\= #\c)  token='(#\0 #\1)  stack=(5)  vars=((b 5))
lst='(#\* #\space #\= #\c)  token='()  stack=(10 5)  vars=((b 5))
lst='(#\space #\= #\c)  token='(#\*)  stack=(10 5)  vars=((b 5))
lst='(#\= #\c)  token='()  stack=(50)  vars=((b 5))
lst='(#\c)  token='(#\=)  stack=(50)  vars=((b 5))
lst='()  token='(#\c #\=)  stack=(50)  vars=((b 5))
lst='()  token='()  stack=(50)  vars=((c 50) (b 5))
50
uselpa
  • 18,732
  • 2
  • 34
  • 52
  • Your function really helped me and this is nearly what I want to do ! Expect that the string-split is undefined at me and I am asked to convert the input string into list of characters at the beginning and then work on the list of characters.. – Zok Nov 22 '15 at 17:14
  • So what implementation are you using? – uselpa Nov 22 '15 at 18:43
  • Updated to use chars. I guess I like that version better. – uselpa Nov 22 '15 at 19:46
  • Yeah this is what I was asked to do , except your function can not recover the variable's values. I mean if I do (evaluate "10 =b"), it stocks 10 with b , but if I call (evaluate "10 =b 20 =c c b *") I get an error, I think I need to handle this by adding a condition to check if the variable exists in the stack already so it recovers its value.I already made a function that can change the variable's value , for example if I do (evaluate "10 =b") then (evaluate "20 =b"), I change the value of b in the vars from 10 to 20. – Zok Nov 22 '15 at 23:03
  • Yes you need to add some code in the inner `cond` to retrieve the contents of a variable. Getting the value of `b` for example is done with `(second (assoc "b" vars))`. – uselpa Nov 23 '15 at 05:31
  • Okay I did the necessary modifications 1)to test if the variable was stocked before in the vars, it changes its value in the var without doing adding a new list. 2)to test if the input is a char without = before it, it recovers its value and put it in the stack – Zok Nov 23 '15 at 07:05
  • I think I will need your help before I must use this function in another function that does a loop like (main) so that the calculator works untill I quit the program. I will try to work on that tomorrow, I may reply back if I am blocked.. thank you so much – Zok Nov 23 '15 at 07:06
  • Excuse me , soemthing I do not really understand in the code, where is the part of the code that adds the chars into the token ?? Isn't the token supposed to be an empty list ? How does it get elements inside without a cons – Zok Nov 23 '15 at 07:27
  • This happens in the last line of the code, in the recursive call to `loop`: `(cons (car lst) token)` is the part of the expression that does this. As for your question of integrating this in a main loop, try a little bit for yourself and post *a different* question if you need help. – uselpa Nov 23 '15 at 07:45