1

I'm trying to create a function that mimics the "adda" behavior of car and cdr which I can pass any string of 'a's and 'd's. The idea is to return a function which then executes on the list that follows. A call to this function might look like:

((cxr "dd") '(1 2 3))

I've incrementally built up to where I think it should be working, first by creating a letrec that returns the correct string of car and cdr. Then moved the body of the letrec to the body of a lambda inside a define.

(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

Using Repl.it, I get an awful error message that makes no sense to me. I've finagled it a bit to get it to work a different way, but I want to know what I'm doing wrong.

Letrec I built from, returns the correct string of car and cdr:

(letrec
    (
        (cxr
            (lambda (cmdString)
                (cond
                    ((= 0 (string-length cmdString)) 'X)
                    ((char=? #\a (string-ref cmdString 0)) (list (quote car) (cxr (substring cmdString 1 (string-length cmdString)))))
                    ((char=? #\d (string-ref cmdString 0)) (list (quote cdr) (cxr (substring cmdString 1 (string-length cmdString)))))
                )
            )   
        )
    )
(cxr "daa") ;can change "daa" to any combination of 'd's and 'a's.
)

Finagled version that has the behavior I'm seeking, not very well tested (and kind of ugly):

(define (cxr X)
(list 'lambda '(X) 
    (letrec
        (
            (computecxr
                (lambda (cmdString)
                    (cond
                        ((= 0 (string-length cmdString)) 'X)
                        ((char=? #\a (string-ref cmdString 0)) (list 'car (computecxr (substring cmdString 1 (string-length cmdString)))))
                        ((char=? #\d (string-ref cmdString 0)) (list 'cdr (computecxr (substring cmdString 1 (string-length cmdString)))))
                    )
                )   
            )
        )
        (computecxr X)
    )
)
)

(print (cxr "a"))
((eval (cxr "dd")) '(1 2 3))

Any advice would be helpful. Thank you.

EDIT: Alexis, I don't see the correlation between my question, and the other. I'm not using an if in my program, nor is that the error I'm getting. Could you please explain why you think it's a duplicate?

Teravian
  • 35
  • 6
  • Possible duplicate of [I got "scheme application not a procedure" in the last recursive calling of a function](http://stackoverflow.com/questions/12183096/i-got-scheme-application-not-a-procedure-in-the-last-recursive-calling-of-a-fu) – Alexis King May 11 '16 at 00:40
  • If it's a duplicate, I apologize. I've looked at many of the Scheme articles on here that deal with returning a function. – Teravian May 11 '16 at 01:22
  • The reason it's possibly a duplicate is that both are caused by the same simple parenthesis error, wrapping statements in parens that don't belong. But if you fix those paren errors, there could be a good question in here, separate from that. – Alex Knauth May 11 '16 at 15:36
  • Okay, I didn't recognize that. This is from the 'exposure to functional programming' part of a much larger course where we discuss different programming paradigms, and do a little coding in each. Because of this, I'd say this is my...third?...time writing Scheme. And all I had for syntax reference (aside from the internet) were the slides my instructor gave with examples of different snippets of code. I probably made a mistake trying to monkey-see-monkey-do my code. – Teravian May 11 '16 at 19:28

3 Answers3

1

In your code, a part from syntax errors due to the wrong use of parenthesis, you are trying to build a list, using quote and list, which then must be evaluated to get the result.

But if you want to explore functional programming, you should avoid to think of functions in terms of concrete data structures, like lists. Instead, you should start to think to them as values of the language, exactly like integers and strings, for instance as functions returned by other functions, or expressions that are applied to other expressions since they evaluate to a function.

Here is a possible solution to your problem, written as high-level function, tested with DrRacket:

(define (cxr cmd-string)
  (if (= 0 (string-length cmd-string))
      (lambda (x) x) 
      (let* ((first-char (string-ref cmd-string 0))
             (f (if (char=? first-char #\a) car cdr)))
        (lambda (x) (f ((cxr (substring cmd-string 1 (string-length cmd-string))) x))))))

((cxr "add") '(1 2 3)) ;  => 3

The first test checks if the string is empty, and in this case returns the identity function (that is, the function the returns its argument).

Otherwise, the first character of the string is bound to first-char, then the corresponding function is bound to f (note that car and cdr are the two primitive functions), and finally the value returned is a new function, with a parameter x, that apply f to the result of applying to x the function resulting from the recursive call of cxr.

Added

To see the difference between the functional approach and the "build-a-list-representing-a-function" one, and in particular the different use of the recursion, compare the above function with the following:

(define (mk-cxr cmd-string)
  (define (mk-body string)
    (if (= 0 (string-length string))
        '(x)
        (let* ((first-char (string-ref string 0))
               (operation (if (char=? first-char #\a) 'car 'cdr)))
          (list (cons operation (mk-body (substring string 1 (string-length string))))))))       
  (cons 'lambda (cons '(x) (mk-body cmd-string))))

(mk-cxr "add")   ;  => (lambda (x) (car (cdr (cdr x))))
Renzo
  • 26,848
  • 5
  • 49
  • 61
  • This is very helpful in providing a way to re-approach the problem I'm trying to solve. The biggest piece is the assignment of a function to a variable in the second `let` statement. Though I'm still a bit fuzzy on how `x` gets bound to the list you want to perform the function on. That was one of my biggest stumbling blocks when I was writing the initial solution (it probably didn't help that we just did a "generate a string that can be `eval`ed to invoke a function" exercise). I kept wanting to add a second parameter to the `cxr` function or hardcode the value of `x`. – Teravian May 11 '16 at 06:19
  • I also thought the recursion was the problem, with the repeated calls to the external `cxr` function. I wanted to add a name to the internal lambda, so I could skip the outer function and just bounce straight to the inner one and give it the parameters directly. – Teravian May 11 '16 at 06:21
  • @Teravian, this exercise is not so easy, actually. If you want to build a list representing the function, and then call `eval` to apply it, you *have* to think in term of list building primitives, and recursion to build such a list (but in this case a macro would be a better choice). If, instead, you want to build a function, then you should start to think of functions as black boxes, in which you cannot “enter” to “modify", but that you can *only* apply (or bound to some identifier, like `f`, or return from another function). When you understand this, you understand functional programming. – Renzo May 11 '16 at 06:42
  • @Teravian, I edited the answer to compare the two approaches. – Renzo May 11 '16 at 08:29
1

The original accessors are not magically generated but usually global bindings that are compositions.

However it is possible to do this. A common procedure called compose is usually something that chains procedures together and that is what is happening here. With compose it's very simple to make cxr and without using eval. A compiler has little chance to optimize code that use eval and there are security issues if somehow the evaled code has elements from user input.

#!r6rs
(import (rnrs)
        (only (srfi :1) fold)
        (only (srfi :13) string-fold-right))

(define compose
  (let* ((apply-1
          (lambda (proc value)
            (proc value)))
         (gen
          (lambda (procs)
            (let ((initial (car procs))
                  (additional (cdr procs)))
              (lambda args
                (fold apply-1
                      (apply initial args)
                      additional))))))
    (lambda procs
      (cond ((null? procs) values)
            ((null? (cdr procs)) (car procs))
            (else (gen (reverse procs)))))))

(define (cxr receipt)
  (define (add-proc char acc)
    (cons (if (eqv? char #\a) car cdr) acc))

  (apply compose
         (string-fold-right add-proc
                            '()
                            receipt)))

;; test
(define test '(1 2 3 4 5 6))
(define my-caddr (cxr "add"))
(define compose-caddr (compose car cdr cdr))

(caddr test)         ; ==> 3
(my-caddr test)      ; ==> 3
(compose-caddr test) ; ==> 3
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • I had looked at the documentation for `compose` when I figured out I was doing something wrong, but I thought it was a bit complicated for what I was trying to do. And after looking at your example, I have to admit, I have no real idea what's going on... – Teravian May 11 '16 at 14:11
  • @Teravian `(compose p3 p2 p1)` makes an equivalent to `(lambda args (p3 (p2 (apply p1 args))))`. Its definition is slightly more complex since it works for any number of arguments. – Sylwester May 11 '16 at 14:44
1

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.

Alex Knauth
  • 8,133
  • 2
  • 16
  • 31
  • Darn it, now I have three fantastic answers. One showing the proper way to do things, one showing me what I did wrong originally, and one I don't really understand. Renzo and Alex Knauth, you have been very helpful, I really appreciate it. Sylwester, I'm sure with more exposure and experience, your answer would be very enlightening. I mean, I know what composure is relating to math, where you have f(g(x)) which is exactly what I was trying to do, but the example you gave is still too out of reach for me. – Teravian May 11 '16 at 20:34
  • Yes, composition is a great way to do it. Actually come to think of it I could take my answer one step further to use compose... – Alex Knauth May 11 '16 at 21:02
  • Seeing it that way, `compose` makes a lot more sense. In Sylwester's example, I just got lost in everything. All the `let`s and `lambda`s and `cond`s. I couldn't see what the compose was actually doing. With this, it's a lot more apparent. The `import` stuff threw me off too, we weren't told anything about having stuff to import. – Teravian May 12 '16 at 00:08