Why can't I use car inside this let statement in Scheme? - list

The following Scheme code works fine:
(define (same-parity x . numbers-input)
(define (check-parity a) (= (remainder x 2) (remainder a 2)))
(define (iter numbers-left)
(cond ((null? numbers-left) nil)
((check-parity (car numbers-left)) (cons (car numbers-left) (iter (cdr numbers-left))))
(else (iter (cdr numbers-left)))))
(cons x (iter numbers-input)))
It is supposed to output a list with the first element being an integer x and the subsequent elements being all the integers from numbers-input which have the same parity as x.
If anyone is interested, this is my attempt at solving the exercise 2.20 from the book Structure and Interpretation of Computer Programs.
Now, I wanted to replace (car numbers-left) and (cdr numbers-left) with the variables "first" and "rest".
(define (same-parity x . numbers-input)
(define (check-parity a) (= (remainder x 2) (remainder a 2)))
(define (iter numbers-left)
(let ((first (car numbers-left))
(rest (cdr numbers-left)))
(cond ((null? numbers-left) nil)
((check-parity first) (cons first (iter rest)))
(else (iter rest)))))
(cons x (iter numbers-input)))
Trying to call this function now gives me an error:
> (same-parity 1 2 3 4 5)
. . mcar: contract violation
expected: mpair?
given: ()
Racket is highlighting the (car numbers-left) inside of the let-statement. Even when I never actually call "first" or "rest" in the body of the function and just leave it the way it was before, I get the same error.
However, in the following code I tried to copy the structure of the above procedure in a simple test definition and surprisingly, this works as you would expect.
(define (test x . testlist)
(define (test2 test2list)
(let ((first (car test2list)))
first))
(test2 testlist))
> (test 1 2 3 4 5)
2
It turns out that if I replace my (cond ...) in my original program with a simple call it works fine as well, so somehow the (cond ...) statement prohibits me from using the variables.
I know this is a very specific thing and I don't know if this ever really matters (except maybe if you want to make the code more readable) but I would really like to know why exactly it behaves this way.
Any insight would be appreciated!

Variables' values are evaluated as soon as you define them, whether you use them or not. Therefore you are calling (car '()) at the end of your recursion, once the list has become empty. You never make it to the part of your cond that attempts to exit.

Related

Scheme function to reverse a list

For my programming languages class I'm supposed to write a function in Scheme to reverse a list without using the pre-made reverse function. So far what I got was
(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (CONS (reverseList(CDR lst)) (CAR lst)))
))
The problem I'm having is that if I input a list, lets say (a b c) it gives me (((() . c) . b) . a).
How am I supposed to get a clean list without multiple sets of parenthesis and the .'s?
The problem with your implementation is that cons isn't receiving a list as its second parameter, so the answer you're building isn't a proper list, remember: a proper list is constructed by consing an element with a list, and the last list is empty.
One possible workaround for this is to use a helper function that builds the answer in an accumulator parameter, consing the elements in reverse - incidentally, this solution is tail recursive:
(define (reverse lst)
(reverse-helper lst '()))
(define (reverse-helper lst acc)
(if (null? lst)
acc
(reverse-helper (cdr lst) (cons (car lst) acc))))
(reverse '(1 2 3 4 5))
=> '(5 4 3 2 1)
You are half way there. The order of the elements in your result is correct, only the structure needs fixing.
What you want is to perform this transformation:
(((() . c) . b) . a) ; input
--------------------
(((() . c) . b) . a) () ; trans-
((() . c) . b) (a) ; for-
(() . c) (b a) ; mation
() (c b a) ; steps
--------------------
(c b a) ; result
This is easy to code. The car and cdr of the interim value are immediately available to us. At each step, the next interim-result is constructed by (cons (cdr interim-value) interim-result), and interim-result starts up as an empty list, because this is what we construct here - a list:
(define (transform-rev input)
(let step ( (interim-value input) ; initial set-up of
(interim-result '() ) ) ; the two loop variables
(if (null? interim-value)
interim-result ; return it in the end, or else
(step (car interim-value) ; go on with the next interim value
(cons ; and the next interim result
(... what goes here? ...)
interim-result )))))
interim-result serves as an accumulator. This is what's known as "accumulator technique". step represents a loop's step coded with "named-let" syntax.
So overall reverse is
(define (my-reverse lst)
(transform-rev
(reverseList lst)))
Can you tweak transform-rev so that it is able to accept the original list as an input, and thus skip the reverseList call? You only need to change the data-access parts, i.e. how you get the next interim value, and what you add into the interim result.
(define (my-reverse L)
(fold cons '() L)) ;;left fold
Step through the list and keep appending the car of the list to the recursive call.
(define (reverseList lst)
(COND
((NULL? lst) '())
(ELSE (APPEND (reverseList(CDR lst)) (LIST (CAR lst))))
))
Instead of using cons, try append
(define (reverseList lst)
(if (null? lst)
'()
(append (reverseList (cdr lst)) (list (car lst)) )
)
)
a sample run would be:
1]=> (reverseList '(a b c 1 2 + -))
>>> (- + 2 1 c b a)
car will give you just one symbol but cdr a list
Always make sure that you provide append with two lists.
If you don't give two lists to the cons it will give you dotted pair (a . b) rather than a list.
See Pairs and Lists for more information.

Scheme Function to Count Atomic Elements in a List

I am trying to write a simple recursive definition in Scheme (LISP).
The goal is to count the number of atomic elements in a list and recursively count internal list atoms as well.
For example:
(num_items '((a b) c d))
should return:
4
because "a", "b", "c", and "d" are the four atomic elements in the lists/sub-lists.
My code so far is as follows:
(define (num_items X)
(cond
((null? X) 0)
(list? (car X) (+ (num_items(car X)) (num_items(cdr X))))
(else (+ 1 (num_items(cdr X))))
))
(display(num_items '((a b) c d)))
The error is thrown on the 4th line:
(list? (car X) (+ (num_items(car X)) (num_items(cdr X))))
As far as I can tell, the (num_items(car X)) recursion in the addition seems to be causing the error. If I replace that part of the line with a 1 for example to get:
(list? (car X) (+ 1 (num_items(cdr X))))
Then the code compiles and runs, but then it doesn't solve the problem.
I am using Compile Online to test/run my code. The error it throws is:
$gosh main.scheme
gosh: "error": pair required, but got a
A pair of parentheses are missing in the fourth line, try this:
(define (num_items X)
(cond
((null? X) 0)
((list? (car X)) (+ (num_items (car X)) (num_items (cdr X))))
(else (+ 1 (num_items (cdr X))))))
Other than that, the code is fine. Just a minor suggestion: don't close parentheses in a separate line, close them all at the end. Also, notice that you can replace list? with pair? (or cons?, depending on which one is available in your interpreter). This is cheaper than testing if the value is a list.

Procedures on lists (scheme)

I am writing a procedure which returns a list with all of the negative odd and positive
even integers removed (strings can stay), by using lambda in the primitive filter procedure. I also am avoiding using recursion, but that is what's stumping me.
What I have so far is:
(define (f2b lst)
(cond ((null? lst)'()) ; if the list is empty, return the empty list
((pair? (car lst)) ; if the current element isn't a list
(filter (lambda (x) (or (even? x) (positive? x))) (car lst))
(filter (lambda (x) (or (odd? x) (negative? x))) (car lst)))
(else (string? (car lst)) ;otherwise, if the current element is a string,
(car lst) ; then return that element
(f2b (cdr lst)))))
I'm also not sure how I can apply both of the filter procedures at the same time.
It's way simpler than that. All you have to do is filter the list. You just need the appropriate predicate.
When do you want to keep an element? You phrased it in terms of what you want to remove, so let's start with that. You want to remove if it's a negative odd or a positive even integer, and leave everything else in. It's easier to break it down into smaller functions.
(define (positive-even? x) (and (positive? x) (even? x)))
(define (negative-odd? x) (and (negative? x) (odd? x)))
(define (remove-num? x) (or (positive-even? x) (negative-odd? x)))
This defines whether to keep a number. But the list element might not be a number. So we
keep it if it's not a number, or if it doesn't match remove-num?:
(define (keep-element? x) (or (not (number? x)) (not (remove-num? x))
Then your function just has to call filter:
(define (f2b lst) (filter keep-element? lst))
Seems to work:
(f2b '(-4 -3 -2 -1 0 1 2 3 4 "a string" "another"))
=> (-4 -2 0 1 3 "a string" "another")
Here's how it would look as one big honkin' function:
(define (f2b lst)
(filter
(lambda (x)
(or (not (number? x))
(not (or (and (positive? x) (even? x))
(and (negative? x) (odd? x))))))
lst)
Personally, the nested or not or and gets a bit hard to read for my taste...
Ok, apparently you have nested lists. All you have to do here is map the result of the filter with a function which:
when given a list, returns (f2b lst)
otherwise, returns the element unchanged.
I will leave it as an exercise for you since, if you thought my function could possibly work on a nested list, clearly you have a lot of learning to do...

How to get the even elements in a list recursively

I'm trying to create a function that will return the even numbered elements in a list.
For example:
(evens '(a b c d))
should return
(b d)
The code below seems to work for lists that have and odd numbers of elements, but if I give it a list with an even number of elements, it is incorrect.
For example:
(evens '(a b c d e))
will return
(b d)
But:
(evens '(a b c d))
will return
(a c)
Any thoughts?
Changed my code to:
(DEFINE (evens lis)
(cond
((null? lis) '())
(else (cons (cadr lis) (evens (cdr lis))))
))
Gets a error saying that the object passed to safe-car is not a pair?
The problem is that if your list has an even number of elements, the modulo branch is matched and you start consing with the car of the list ... hence in your example, you get the a, and so on.
More importantly, though, you don't need to use length for this function ... and you shouldn't: since length takes linear time in the length of the list, evens now takes quadratic time.
Suggestion: your program should 'remember' whether it's in an 'odd' or 'even' location at each recursive step ... how could you do this (there are several ways)?
Your code has few missing checks and a bit of incorrect logic.
(define (evens lis)
(cond
((null? lis) '())
((eq? (cdr lis) '()) '()) ;missing condition
(else (cons (cadr lis) (evens (cddr lis)))))) ; it is cddr not cdr
I'm going to answer your question with commented examples, in the hope that you actually learn something instead of just being given code that works. Actually, looking at several pieces of code may be more enlightening, assuming that you're new to scheme.
Your original definition looked like this:
(define (evens lis)
(cond (;; Check: Recursion stop condition
(null? lis)
'())
(;; Wrong: Calling length at each step => O(n^2)
;; Wrong: Assuming even element if list has even number of elements
(= (modulo (length lis) 2) 0)
;; Wrong: Recursing with the rest of the list, you'll get odds
(cons (car lis) (evens (cdr lis))))
(else
;; Wrong: Recursing with the rest of the list with cdr, you'll get odds
(evens (cdr lis)))))
Afterwards, you've edited your question to update it to something like this:
(define (evens lis)
(cond (;; Check: Recursion stop condition
(null? lis)
'())
(else
;; Check: Building list with second element
;; Wrong: If lis only has 1 element,
;; (cdr lis) is null and (car (cdr list)) is an error.
(cons (cadr lis)
;; Wrong: Recursing with cdr, you'll get odds
(evens (cdr lis))))))
A solution is to check if the list has at least a second element:
(define (evens lis)
(cond (;; Check: Recursion stop condition 1
(null? lis)
'())
(;; Check: Recursion stop condition 2: list of length = 1
(null? (cdr lis))
'())
(else
;; Check: Building list with second element
;; The previous cond clauses have already sorted out
;; that lis and (cdr lis) are not null.
(cons (cadr lis)
;; Check: Recurse "the rest of the rest" of lis with cddr
(evens (cddr lis)))))
Exercise: Use if and or to simplify this solution to only have 2 branches.
This same question has been asked time and again over the last couple of days. I'll give a direct answer this time, to set it straight:
(define (evens lst)
(if (or (null? lst) ; if the list is empty
(null? (cdr lst))) ; or the list has a single element
'() ; then return the empty list
(cons (cadr lst) ; otherwise `cons` the second element
(evens (cddr lst))))) ; and recursively advance two elements
And here's how to do some error checking first:
(define (find-evens lst)
(if (list? lst)
(evens lst)
(error "USAGE: (find-evens [LIST])")))

Trying to apply a function on two lists

I wrote the following code in order to apply a function for two lists
which are part of a list of lists but for some reason I'm getting #<void> values in the result.
The code:
(define (applyFunc list)
(cond ((null? list) ())
((null? (cdr list)) (car list))
(else (cons (func (car list) (car (cdr list)))
(applyFunc (cdr (cdr list)))))))
func is a function that applies a function on two given lists
What I get from tracing my code is:
>(applyFunc '((1) (1 1) (1 1 1) (1 1 1 1)))
> (applyFunc '((1 1 1) (1 1 1 1)))
> >(applyFunc '())
< <'()
< '(#<void>)
<'(#<void> #<void>)
(#<void> #<void>)
[assuming the input was '((1) (1 1) (1 1 1) (1 1 1 1))]
A couple of errors to be taken care of first:
You should not call list a procedure parameter (or anything else for that matter), since that's a built-in procedure in Scheme and you'd be overwriting it.
In the first condition, the usual is to return '(), not ()
Other than that, your procedure works fine:
(define (applyFunc lst)
(cond ((null? lst) '())
((null? (cdr lst)) (car lst))
(else (cons (func (car lst) (car (cdr lst)))
(applyFunc (cdr (cdr lst)))))))
It's possible that the problem is in the func procedure, make sure that it does indeed work with two lists. I tested your code with this, and it worked without a hitch:
(define (func l1 l2)
(append l1 l2))
I agree with Oscar, above. The main problem with your code was that on the empty case, you were returning () instead of '().
In terms of your naming conventions, list should technically run just fine, but, as mentioned by Oscar, it is generally considered bad to run the risk of overwriting a built-in procedure.
I updated the code by changing the () to a '(), and it ran flawlessly.