Compare elements in the list - scheme

I need compare every second element in the list but I don't know how. Here is an example:
(compare? '(1 x 2 x 3 x 4)) -> #t
(compare? '(1 x 2 x 3 o)) -> #f
I can only compare second and fourth element:
(define compare?
(lambda (list)
(equal? (cadr list) (cadddr list))))
I need 6th, 8th, 10th etc.... I don't know length of the list. Please, help me.

Try this answer, filling-in the blanks:
(define (compare? lst)
(if <???> ; if the list has at most two elements
#t ; then return true
(let ((elt (cadr lst))) ; grab the first element to be compared
(let loop ((lst (cddr lst))) ; step on the second group of elements
(cond (<???> #t) ; if there's only one or zero elements left
(<???> #f) ; if the second element is not equal to `elt`
(else (loop (cddr lst)))))))) ; otherwise continue iterating

Let's look at the example of (compare? '(1 x 2 x 3 x 4)).
You want to ensure that (compare? '(2 x 3 x 4)) is true, and that the 1 x before that also matches.
That then means that you want to ensure that (compare? '(3 x 4)) is true (which it is, by definition), and that the 2 x before that also matches.
Notice how we are working with smaller and smaller lists each time. We can do that because lists have structural induction. Because of structural induction, you don't actually have to know the length of the list. The algorithm just works on smaller and smaller sublists until it hits a base case.
Sample skeletal solution (fill in the <???> with suitable code):
(define (compare? lst)
(if (or (null? lst) (null? (cdr lst)))
#t
(let ((item (cadr lst))
(next (compare? (cddr lst))))
(case next
((#f) <???>)
((#t) <???>)
(else (and <???> <???>))))))
(Technically the #f clause is not necessary, but, it may make it clearer to you what the solution approach should be.)
This solution will only work correctly if the matched slots in the list are not #t or #f. Since you're using symbols in your example, it will work correctly.

Related

Scheme: Trying to find largest element in two lists

I have 3 functions union, largest, and largest_of_two. Union takes two lists and combines them into one (this function has been tested and works). largest is supposed to return the largest element in a given list but only returns #f whether I call it via largest_of_two or on its own. Any help will be greatly appreciated.
(define (union l1 l2)
(cond (
(null? l1) l2)
((cons (car l1) (union (cdr l1) l2)))
)
)
(define (largest x a_list)
(cond
((null? a_list) x)
((< x (car a_list)) (= x (car a_list)))
(else (largest x (cdr a_list)))
)
)
(define (largest_of_two l1 l2)
(largest (car l1) (cdr (union l1 l2)))
)
(display(largest_of_two '(19 30 13 29 38) '(1 50 5 20 41)))
The fundamental problem is that (= x (car a_list)) is a comparison, but you need to make a recursive call here:
(define (largest x a_list)
(cond ((null? a_list) x)
((< x (car a_list)) ; if x is less than the first element
(largest (car a_list) (cdr a_list))) ; call with first element and cdr
(else
(largest x (cdr a_list)))))
When x is less than the first element of the list, you want to call largest again with the first element and a reduced list as arguments.
Yet, calling largest with two arguments like this seems awkward. If I want to find the largest element of the list (1 6 1 8 0 3) I have to call (largest 1 '(6 1 8 0 3)), which is not ideal. A better approach would be to discard the smaller of the first two elements on each iteration until there is only one element left:
(define (largest xs)
(cond ((null? xs) #f) ; empty input: no largest member
((null? (cdr xs)) ; only one member
(car xs))
((> (car xs) (cadr xs)) ; first member is larger than the second
(largest (cons (car xs) (cddr xs)))) ; keep the first member
(else
(largest (cdr xs))))) ; discard the first member
It doesn't make sense to return a numeric result when the input list is empty, so #fis returned in that case. If the input list contains only a single value, then that value is returned (note that this code does not verify that a list of one element contains a number, so (largest '(z)) --> z).
Otherwise the list contains at least two values. If the first is larger than the second, the first is consed onto the rest of the list with the second removed ((cddr xs)) and largest is called again on the result. Otherwise the first value is not larger than the second, so the first element is discarded ((cdr xs)) and largest is called on that result.
There is another minor issue with the posted definition of union in the conditional form:
((cons (car l1) (union (cdr l1) l2)))
There is only one test expression here. Now, this is legal Scheme because when a selected conditional clause contains only a test expression, the value of the test expression is returned. But this is not idiomatic, and it is hard to read. The posted definition for largest used else in a similar situation, and it should be used here, too. Or, just use an if form:
(define (union xs ys)
(if (null? xs)
ys
(cons (car xs) (union (cdr xs) ys))))
Be consistent; use formatting and line breaks to make code clear. And while we are talking about style, please don't scatter parentheses about haphazardly, and prefer kebab-case (aka lisp-case) to snake_case for identifiers in lisps.
With the new definition for largest, largest-of-two has a simpler definition:
(define (largest-of-two xs ys)
(largest (union xs ys)))
> (largest-of-two '(1 4 2 6 3 11 6 -2) '(3 8 -3 7 10 4))
11
Filtering is often called reduce in Scheme and functional languages. It is already explained in detail here. I quote the implementation:
(define (reduce fn list init)
(if (null? list) init
(fn (car list)
(reduce fn (cdr list) init))))
This is a very general function, which takes the operation, the list to operate on and an accumulator, which holds the value of each step.
You just need to implement your maximum function for two arguments.
(define (max a b)
(if (> a b)
a
b))
And then you can pass it to reduce. You just need to add an initial value.
(reduce max '(19 30 13 29 38) 0)
In your case it might be better to split your input list, because all elements can be negative.
(let ((lst '(19 30 13 29 38)))
(reduce max (cdr lst) (car lst)))
If you want to do anything twice or for even more arguments, just use map.
(map (lambda (lst)
(reduce max (cdr lst) (car lst)))
'((19 30 13 29 38)
(1 50 5 20 41)))
The following puts everything in one function.
(define (largest-of . lists)
(define (reduce fn list init)
(if (null? list) init
(fn (car list)
(reduce fn (cdr list) init))))
(define (max a b)
(if (> a b)
a
b))
(map (lambda (lst)
(reduce max (cdr lst) (car lst)))
lists))
(largest-of '(19 30 13 29 38) '(1 50 5 20 41)) ;; => (38 50)
This works also for more than two lists. It is just limited by the maximum number of arguments of your Scheme implementation.
And it works for just one argument.
(largest-of (largest-of '(19 30 13 29 38) '(1 50 5 20 41))) ;; => (50)
You just need to unbox the value with car.

scheme function to filter a list

Write a scheme function that returns a list containing all elements of a given
list that satisfy a given predicate eg. (lambda (x) (< x 5)) '(3 9 5 8 2 4 7) => '(3 2 4)
Any hints how to begin about this ?
Basically you are creating filter.
(define (filter included? lst)
...)
You need to check if the argument has elements and if the first element is to be included or not. Including would involve cons-ing the first element to the recursing with the cdr with the same predicate while not including would not include consing but the exact same thing as the tail in a conse situation.
(filter odd? '(3 4))
; ==> (cons 3 (filter odd? '(4)))
(filter odd? '(4))
; ==> (filter odd? '())
(filter odd? '())
; ==> '()
Putting them together shows that (filter odd? '(3 4)) should produce the result of (cons 3 '())
Here is a skeleton of what I would have done:
(define (filter included? lst)
(cond ((null? lst) <??>)
((included? (car lst)) <??>) ; since it is not null? it has to have at least one element
(else <??>))) ; neither null? nor included? => skip

How to define my own version of filter procedure in racket?

I am new in Racket and I was assigned to do my own filter procedure. It should work similar to the Racket filter procedure. Currently, my-filter has two arguments: the even procedure to check the items in the list, and a list of items.
So far, I have been only able to check whether the items in the list are even or not. my-filter is supposed to iterate through a list of numbers, retrieve the numbers that are even and save them in a second list. How can I iterate through the list and store the even numbers in the second list?
(define (my-filter f lst)
(if (empty? lst)
empty
(cons
(f (first lst))
(my-filter f (rest lst)))))
> (my-filter even? '(1 2 3 4 5 6))
'(#f #t #f #t #f #t)
There are three cases that you need to consider:
Input list is empty -> we're done.
Current element satisfies the predicate function -> add it to the output and continue with next element.
Current element doesn't satisfy the predicate function -> skip it and continue with next element.
You're mixing the last two cases into a single case. And notice that you must not add (f (first lst)) to the output, that's just the condition that we want to evaluate, we should add (first lst) instead. This is what I mean:
(define (my-filter f lst)
(cond ((empty? lst) empty)
((f (first lst))
(cons (first lst) (my-filter f (rest lst))))
(else (my-filter f (rest lst)))))
It works as expected:
(my-filter even? '(1 2 3 4 5 6))
=> '(2 4 6)

How to know what parameters a foldr's combine function should take?

For the built-in function foldr, I know the function blueprint is the following:
(foldr combine base alist)
combine is supposed to take in two parameters:
an item that foldr consumes
the result of applying foldr to the rest of alist
I cannot seem to understand how to put point #2 in parameter form ever. How did you do it?
combine is not a built-in function. I would have to code it myself based on the requirements.
Think of second parameter as the accumulated value so far. For example, if we are adding the elements, then acc is the sum of all the previous eles and we need to add the current element:
(foldr (lambda (ele acc) (+ ele acc))
0 ; we're adding numbers, so the base is 0
'(1 2 3 4 5))
=> 15
Another example - if we're copying the list, then acc contains the previous eles in the list (starting from the last one and going back from there) and we have to cons the current element at the head :
(foldr (lambda (ele acc) (cons ele acc))
'() ; we're creating a list, so the base is an empty list
'(1 2 3 4 5))
=> '(1 2 3 4 5)
The exact nature of acc depends on the problem to be solved, but you should be able get the idea from the previous examples.
Think of it as the result computed so far and that foldr iterates from end to beginning while a foldl iterates from beginning to end. It's easier to see if you look at a simple implementation of it:
(define (foldr1 f init lst)
(let r ((lst lst))
(if (null? lst)
init
(cons (f (car lst)) (r (cdr lst))))))
(foldr1 combine base '(1 2 3)) ; ==
(combine 1 (combine 2 (combine 3 base)))
(define (foldl1 f init lst)
(let r ((lst lst) (acc init))
(if (null? lst)
acc
(r (cdr lst) (f (car lst))))))
(foldl1 combine base '(1 2 3)) ; ==
(combine 3 (combine 2 (combine 1 base)))
Also note that the order or the arguments change in some implementations. Racket and SRFI-1 always have the accumulator as the last argument, but in R6RS the argument order changes for fold-left (but not fold-right):
#!r6rs
(import (rnrs))
;; swap argument order
(fold-left (lambda (acc e) (cons e acc)) '() '(1 2 3))
; ==> (3 2 1)

Return the first and last element in the list Scheme

Pretty straightforward question. My initial approach was to define another procedure to find the last element of lst within first-last. After finding the last element I appended it with the first element of lst (car lst).
This is how append works.
(append list1 list2)
e.g., (append '(1 2 3) '(2 1 5)) -> (1 2 3 2 1 5)
I'm wondering if the problem is just with my syntax but I am not sure.
(define (first-last lst)
(define (last lst)
(cond ((null? (cdr lst))(car lst))
(else (last (cdr lst)))))
(append(car lst)(last lst)))
The error occurs in the
(append(car lst)(last lst)))
"mcar: contract violation
expected: mpair?
given: 1"
This is my first question on stack, so I'm sorry if the question is not presented in the correct way.
append is only for joining two or more lists. Here, though, you're not joining existing lists, but building a list from two elements. For that, use list:
(list (car lst) (last lst))
If you can use match, a neat solution is possible:
(define first-last
(lambda (x)
(match x
((first rest ... last)
(list first last))
((only) (list only only))
(_ #f))))
Of course, you could return something other than #f in the catch-all clause.

Resources