Scheme if-statement - scheme

I'm currently studying Scheme with The Little Schemer and ran into an odd trouble.
Here is my code:
(define rember
(lambda (a lat)
((if (null? lat)
'()
(cond
((eq? a (car lat)) (cdr lat))
(else (rember a (cdr lat))))))))
(rember 'aaa '(bbb aaa))
I used an "if" instead of "cond" in the textbook. When returning from the tail-recursion, it shows this error:
application: not a procedure;
expected a procedure that can be applied to arguments
given: '()
arguments...: [none]
I guess this is because it treats '() in the if-statement as a function and the return value of the tail-recursion as its argument. But since the book doesn't give me so many details about the language, could you explain this a bit for me? (e.g. Is this in fact some kind of language feature? Is there any way that I can stick to "if" in this piece of code? And when can I use "if" safely?)
Thanks.

You have an extra set of parentheses around your if. This
((if (null? lat)
'()
(cond
((eq? a (car lat)) (cdr lat))
(else (rember a (cdr lat))))))
should be this:
(if (null? lat)
'()
(cond
((eq? a (car lat)) (cdr lat))
(else (rember a (cdr lat)))))
Those extra parentheses tell Scheme that you want to call the result of the if like a function, so you get the error saying that '() isn't a function.

Related

How to check if first and last element in a list are identical (Language: Scheme)

I am writing a program in Scheme and having difficulty with this one part. Below is an example to make my question clear
(endsmatch lst) should return #t if the first element in the list is the same as the last element in the list and return #f otherwise.
For example:
(endsmatch '(s t u v w x y z)) should return: #f
and
(endsmatch (LIST 'j 'k 'l 'm 'n 'o 'j)) should return: #t
Here is what I have so far (just error handling). The main issue I am having is solving this recursively. I understand there are easier solutions that are not recursive but I need to solve this using recursion.
My code so far:
(define (endsmatch lst)
(if (not(list? lst))
"USAGE: (endsmatch [list])"
(if (or (null? lst)
(= (length lst) 1))
#t
(equal? ((car lst)) (endsmatch(car lst)))
)))
I believe my code starting at "(equal? " is where it is broken and doesn't work. This is also where I believe recursion will take place. Any help is appreciated!
Easiest way is to use a (recursive) helper function to do the looping:
(define (endsmatch lst)
(define (helper no1 lst)
(if (null? (cdr lst))
(equal? no1 (car lst))
(helper no1 (cdr lst))))
(if (or (not (list? lst)) (null? lst))
"USAGE: (endsmatch [list])"
(helper (car lst) lst)))
The reason I pass lst and not (cdr lst) as the second argument in the last line is so that it also works for 1-element lists.
I tend to use KISS when programming. aka. "Keep it simple, stupid!"
With that regard I would have oped for:
(define (ends-match? lst)
(or (null? lst)
(equal? (car lst)
(last lst))))
Now last we can define like this:
(define (last lst)
(foldl (lambda (e a) e) last lst))
It's not perfect. It should signal an error if you pass an empty list, but in the ends-match? you check for this and thus it's not a problem.

Combining count and flatten functions in scheme

So i have these two functions that work fine alone. I am trying to write one function to accomplish both but i keep getting a car error. Any guidance on the best way to solve this?
(define (countNumbers lst)
(cond
((null? lst) 0)
((number? (car lst))(+ 1 (countNumbers (cdr lst))))
(else (countNumbers (cdr lst)))))
(define (flatten x)
(cond ((null? x) '())
((pair? x) (append (flatten (car x)) (flatten (cdr x))))
(else (list x))))
I tried something like this im rather new to functional programming in general so im still trying to wrap my mind around it it says the problem is after number?(car lst)
(define (flatten lst)
(cond ((null? lst) '())
((pair? lst) (append (flatten (car lst)) (flatten (cdr lst))))
(else (list(cond
((null? lst) 0)
((number? (car lst))(+ 1 (flatten (cdr lst))))
(else (flatten (cdr lst))))))))
As I mentioned in my comment, I don't think it's a good idea to stick everything in a single function. Anyway, you were kinda on the right track, but we have to remember that if we're going to return a number as the final result, then our base case should reflect this and also return a number (not an empty list), and the combining step should add numbers, not append them. This is what I mean:
(define (count-flatten lst)
(cond ((null? lst) 0)
((pair? lst)
(+ (count-flatten (car lst))
(count-flatten (cdr lst))))
((number? lst) 1)
(else 0)))
But I'd rather do this:
(define (count-flatten lst)
(countNumbers (flatten lst)))
We can even write an idiomatic solution using only built-in procedures, check your interpreter's documentation, but in Racket we can do this:
(define (count-flatten lst)
(count number? (flatten lst)))
Anyway, it works as expected:
(count-flatten '(1 x (x 2) x (3 (4 x (5) 6) 7)))
=> 7

checking the first atom against the other atoms in a list

I am trying to check the first atom in a list against the other atoms in a list, so if the first atom is 3 and the 3rd atom is three, I would want to evaluate it to false.
I have something like
(define (doubles a_list)
(cond
((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))
I have a feeling that this will only check atoms next to each other and not the one atom with the rest of them. what can i do to check all the items with the first one?
You want to repeatedly compare the first two elements of your list, reducing the cdr as you go:
(define (unique-first? x)
; uncomment the following line to trace the execution
;(display x) (newline)
(or (null? x) (null? (cdr x))
(and (not (eq? (car x) (cadr x)))
(unique-first? (cons (car x) (cddr x))))))
note on special cases:
(unique-first? '()) -> #t
(unique-first? '(a)) -> #t
(which, indeed, verify the "first element doesn't appear twice" criteria).
I've got my tea, my code-review hat and a few spare minutes. You know what time it is.
What you want is a predicate that will tell you if there are duplicates of the first atom in a list.
(define (doubles a_list)
(cond ((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))
Regardless of anything else, this won't work because it has unbalanced parentheses.
(define (doubles a_list)
(cond ((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))))
That revised version won't work because it has a malformed second clause in the case. Oddly, it does seem to evaluate fine, but when you call it, you'll get an odd error message
Welcome to Racket v5.3.6.
> (define (doubles a_list)
(cond ((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))))
> (doubles (list 1 2 3 4 5 6 7 3))
application: not a procedure;
expected a procedure that can be applied to arguments
given: #f
arguments...: [none]
>
The direct cause is this bit
... ((eq? (car a_list) (car (cdr a_list)))) ...
Because of the extra parens surrounding this expression, what it actually means is
"See if the first element of a_list is the same as the second, then call the result of that check as a function".
This is not what you want. The correct resolution is getting that not into these parens, which would make that a valid cond clause.
(define (doubles a_list)
(cond ((null? a_list) #t)
((not (eq? (car a_list) (car (cdr a_list))))
(doubles (cdr a_list)))))
You can't call car on the empty list in Scheme. This works fine, for some definition of "fine", in Common Lisp but will throw you an error here.
> (define (doubles a_list)
(cond ((null? a_list) #t)
((not (eq? (car a_list) (car (cdr a_list))))
(doubles (cdr a_list)))))
> (doubles (list 1 2 3 4 5 6 7 3))
car: contract violation
expected: pair?
given: '()
>
The cause of this error is that you're checking whether a_list is null?, but then calling (car (cdr a_list)) later on. You'll get this error in the situation where a_list is something like (3). The fix for this is checking whether a_list or its cdr are null?
(define (doubles a_list)
(cond ((or (null? a_list) (null? (cdr a_list))) #t)
((not (eq? (car a_list) (car (cdr a_list))))
(doubles (cdr a_list)))))
> (doubles (list 1 2 3 4 5 6 7 3))
#t
Now that we've got a version of your function that doesn't error out, lets take a look at your logic. The procedure for finding doubles in a list is
for lists of length zero or one, the answer is False, since there can be no doubles in a list shorter than two elements
otherwise, check if the first element of the list is in the rest.
if it is, the answer is True since we've found a duplicate
Now, you've named your function doubles, but your prose explanation tells me that it really should have been unique-first?. Because you're not looking for doubles, your're looking to check if the first element of your list is unique among its peers. What you really want to do is
for lists of length one, the answer is True, since that single element must be unique (I'm going to assume that you want the same for lists of length zero, but that might not actually make sense depending on the application)
otherwise, check that the first element of the list is not in the rest.
That translates to
(define (unique-first? a_list)
(if (or (null? a_list) (null? (cdr a_list)))
#t
(not (member? (car a_list) (cdr a_list)))))
The member? function is fairly straightforward, and works on the same principles.
(define (member? elem lst)
(cond ((null? lst) #f)
((eq? elem (car lst)) #t)
(else (member? elem (cdr lst)))))
Finally, a couple of style points. The Scheme convention is to name predicates with a trailing ?, which will hint to your functions' callers that it will return #t or #f (I've done this above), and to use spinal-case rather than snake_case for names.
(define (unique-first? a-list)
(if (or (null? a-list) (null? (cdr a-list)))
#t
(not (member? (car a-list) (cdr a-list)))))

Scheme replacement problems

This code replaces first person words with second person words and vice versa. However, it goes through each pair for each word in the phrase, so sometimes it will change back.
Here is the code:
(define (replace pattern replacement lst replacement-pairs)
(cond ((null? lst) '())
((equal? (car lst) pattern)
(cons replacement
(many-replace (cdr replacement-pairs) (cdr lst))))
(else (cons (car lst)
(many-replace (cdr replacement-pairs) (cdr lst))))))
(define (many-replace replacement-pairs lst)
(cond ((null? replacement-pairs) lst)
(else (let ((pat-rep (car replacement-pairs)))
(replace (car pat-rep)
(cadr pat-rep)
(many-replace (cdr replacement-pairs)
lst) replacement-pairs)))))
(define (change-person phrase)
(many-replace '((i you) (me you) (am are) (my your) (are am) (you i) (your my))
phrase))
For example if I entered
(change-person '(you are not being very helpful to me))
it would change you to i but then back to you. How do I fix this?
The procedures replace and many-replace are overly complicated, and the mutual recursion is not doing what you think. If we simplify those procedures and make sure that only a single pass is performed over the input list, we can get a correct answer:
(define (replace replacement-pairs pattern)
(cond ((null? replacement-pairs)
pattern)
((equal? (caar replacement-pairs) pattern)
(cadar replacement-pairs))
(else
(replace (cdr replacement-pairs) pattern))))
(define (many-replace replacement-pairs lst)
(if (null? lst)
'()
(cons (replace replacement-pairs (car lst))
(many-replace replacement-pairs (cdr lst)))))
The keen eye will notice that the previous procedures can be expressed in a succinct way by using some higher-order procedures. A more idiomatic solution could look like this:
(define (replace replacement-pairs pattern)
(cond ((assoc pattern replacement-pairs) => cadr)
(else pattern)))
(define (many-replace replacement-pairs lst)
(map (curry replace replacement-pairs) lst))
Either way, it works as expected:
(change-person '(you are not being very helpful to me))
=> '(i am not being very helpful to you)
I've written a slightly easier solution:
(define (many-replace pattern phrase)
(let loop ((phrase phrase) (result '()))
(if (empty? phrase) (reverse result)
(let* ((c (car phrase)) (a (assoc c pattern)))
(if a
(loop (cdr phrase) (cons (cadr a) result))
(loop (cdr phrase) (cons c result)))))))
(change-person '(you are not being very helpful to me))
=> '(i am not being very helpful to you)

Detecting the caller of a function in Scheme or Racket

In Scheme or Racket is it possible to detect the caller of a function?
For example, I can write a function to test if a list is a list of atoms as follows:
(define atom? (lambda (x) (and (not (pair? x)) (not (empty? x)))))
(define lat? (lambda (l)
(define latt?
(lambda (l)
(cond
((null? l) #t)
((atom? (car l)) (latt? (cdr l)))
(else #f))))
(if (null? l) #f (latt? l))))
but instead of the above, is there a function like "called-by" to do something like this:
(define lat?
(lambda (l)
(cond
((and (null? l) (called-by "lat?")) #t)
((atom? (car l)) (lat? (cdr l)))
(else #f))))
The usual way to do this is to add some argument to the function, or make a loop via an internal definition as you did. Other than that, there is no reliable way to find out the caller of a function.
But in your case, it seems like a good lack of feature -- using it for the above problem is pretty bad. There's nothing wrong with the internal helper version. (It's also quite similar to any other language.)
Finally, I'd expect (lat? null) to return #t since it is a list that has only atoms as elements.

Resources