Ok. Let's take the first attempt in your question, and clean it up a little, with proper indentation and normal )
placement:
(define (cxr cmdString) ; named function to call
(lambda (X) ; beginning of un-named function to return
((cond
; if the string passed in is empty, return the arguments to the second function
[(= 0 (string-length cmdString)) 'X]
; if its an 'a', add a call to car to the list and call the function again with a shorter command string.
[(char=? #\a (string-ref cmdString 0))
(list (quote car) (cxr (substring cmdString 1 (string-length cmdString))))]
; if its a 'd', add a call to cdr to the list and call the function again with a shorter command string.
[(char=? #\d (string-ref cmdString 0))
(list (quote cdr) (cxr (substring cmdString 1 (string-length cmdString))))]))))
((cxr "a") '(1 2 3)) ; call the function
Now it's slightly easier to see the problems. The first problem is a simple paren error, where you wrapped the body of the lambda in a set of parentheses that shouldn't be there. Keep in mind that in scheme, parentheses normally mean a function call. That's why you were getting the application: not a procedure
error. Now with that fixed:
(define (cxr cmdString) ; named function to call
(lambda (X) ; beginning of un-named function to return
(cond
; if the string passed in is empty, return the arguments to the second function
[(= 0 (string-length cmdString)) 'X]
; if its an 'a', add a call to car to the list and call the function again with a shorter command string.
[(char=? #\a (string-ref cmdString 0))
(list (quote car) (cxr (substring cmdString 1 (string-length cmdString))))]
; if its a 'd', add a call to cdr to the list and call the function again with a shorter command string.
[(char=? #\d (string-ref cmdString 0))
(list (quote cdr) (cxr (substring cmdString 1 (string-length cmdString))))])))
((cxr "a") '(1 2 3)) ; call the function
The code gives the result:
'(car #<procedure>)
It's returning a list with two elements, equivalent to (list 'car #<procedure>)
. That's probably not what you meant. I'm guessing you wanted it to return the car
of the list '(1 2 3)
, which is 1
. So first, write that in a comment:
;; cxr : CmdString -> (ConsTree -> Any)
;; Given a command string containing 'a' and 'd' characters, returns
;; a function that gets the corrosponding element in the cons tree.
;; For example, (cxr "a") should return a function equivalent to car,
;; (cxr "d") should return a function equivalent to cdr, and
;; (cxr "add") should return a function equivalent to caddr.
So now that we've documented what (cxr ...)
returns, we can do something with it. We can use it properly in the recursive case. It returns a function that accepts a cons tree, so we can apply it to X
, which is our cons tree: ((cxr ...) X)
. That returns the sub-piece of X
corresponding to the rest of the string, which is (cddr X)
for the "add"
case. So then all you need is to apply car
or cdr
to that:
; 'a' case
(car ((cxr ...) X))
; 'd' case
(cdr ((cxr ...) X))
In context:
(define (cxr cmdString) ; named function to call
(lambda (X) ; beginning of un-named function to return
(cond
; if the string passed in is empty, return the arguments to the second function
[(= 0 (string-length cmdString)) 'X]
; if its an 'a', call car on a recursive call using the rest of the command string.
[(char=? #\a (string-ref cmdString 0))
(car ((cxr (substring cmdString 1 (string-length cmdString))) X))]
; if its a 'd', call cdr on a recursive call using the rest of the command string.
[(char=? #\d (string-ref cmdString 0))
(cdr ((cxr (substring cmdString 1 (string-length cmdString))) X))])))
((cxr "a") '(1 2 3)) ; call the function
Now this gives this error:
car: contract violation
expected: pair?
given: 'X
Where did the 'X
come from? The base case. We need to fix the base case as well, but that's easy, just use X
instead of 'X
:
;; cxr : CmdString -> (ConsTree -> Any)
;; Given a command string containing "a" and "d" characters, returns
;; a function that gets the corrosponding element in the cons tree.
;; For example, (cxr "a") should return a function equivalent to car,
;; (cxr "d") should return a function equivalent to cdr, and
;; (cxr "add") should return a function equivalent to caddr.
(define (cxr cmdString) ; named function to call
(lambda (X) ; beginning of un-named function to return
(cond
; if the string passed in is empty, return the argument to the second function
[(= 0 (string-length cmdString)) X]
; if its an 'a', call car on a recursive call using the rest of the command string.
[(char=? #\a (string-ref cmdString 0))
(car ((cxr (substring cmdString 1 (string-length cmdString))) X))]
; if its a 'd', call cdr on a recursive call using the rest of the command string.
[(char=? #\d (string-ref cmdString 0))
(cdr ((cxr (substring cmdString 1 (string-length cmdString))) X))])))
Testing it:
> ((cxr "a") '(1 2 3))
1
> ((cxr "d") '(1 2 3))
'(2 3)
> ((cxr "add") '(1 2 3))
3
> ((cxr "adda") '((1 2 3) 4))
3
Update
In the spirit of @Sylwester's compose
answer, you can translate this code to use compose
instead of (lambda (X) ...)
, while keeping some of the same structure from your original code:
;; cxr : CmdString -> (ConsTree -> Any)
;; Given a command string containing "a" and "d" characters, returns
;; a function that gets the corrosponding element in the cons tree.
;; For example, (cxr "a") should return a function equivalent to car,
;; (cxr "d") should return a function equivalent to cdr, and
;; (cxr "add") should return a function equivalent to caddr.
(define (cxr cmdString)
(cond
; if the string passed in is empty, return the identity function.
[(= 0 (string-length cmdString)) identity]
; if its an 'a', compose car with a recursive call using the rest of the command string.
[(char=? #\a (string-ref cmdString 0))
(compose car (cxr (substring cmdString 1 (string-length cmdString))))]
; if its a 'd', compose cdr with a recursive call using the rest of the command string.
[(char=? #\d (string-ref cmdString 0))
(compose cdr (cxr (substring cmdString 1 (string-length cmdString))))]))
If you look at the structure, this isn't too different from what you wrote originally. It doesn't have the lambda because it builds up the function using compose
, it returns the identity function for the base case instead of the symbol 'X
, and for the recursive cases it uses compose
where you used list
and quote
.