How to check if first and last element in a list are identical (Language: Scheme) - 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.

Related

How to fix this code in Scheme, where I am trying to find the last two elements in the list

I am trying to write a code in scheme -last_two(lst) where it should output the last two elements in the list.
(define (last_two lst)
(if (null? lst)
'() )
(if (null? (cdr lst))
lst)
(if(null? (cddr lst)))
lst)
(last_two (cdr lst)))
last_two(1 2 3 4) -> (3 4)
A procedure returns the value of the last expression that's executed in the body. In your procedure, when one of the first if conditions is true, you don't return the value from the procedure, you just go on to test the next condition. Eventually you get to the end, which is the recursive call -- you never stop at the base case. Eventually you'll get an error when you try to call (cdr lst) on an empty list.
You can use cond to test a series of conditions and return the value from the first one that's true.
Also, you should be returning lst when the conditions match; your code will always return (), since you're returning the same thing that matched the null? condition.
(define (last_two lst)
(cond ((null? lst) '())
((null? (cdr lst)) lst)
((null? (cddr lst)) lst)
(else (last_two (cdr lst)))))
(last_two '(1 2 3 4))
You might notice that the first 3 conditions all return lst, so you could combine them using or.
(define (last_two lst)
(if (or (null? lst) (null? (cdr lst)) (null? (cddr lst)))
lst
(last_two (cdr lst))))

Inserting word beside another word starting from the end of list

I have code which is inserting new word on the right side of choosen word
(define insertR
(lambda (new old lst)
(cond
((null? lst) (lst))
(else (cond
((eq? (car lst) old)
(cons old
(cons new (cdr lst))))
(else (cons (car lst)
(insertR new old
(cdr lst)))))))))
i need to make it insert that word beside first appearance of word starting from the end of list. Tried to work with reverse but could not get that to work.
There are two strategies you can take to add it next to the last occurence.
The first is to use a helper and start off with the reverse list. This is very simple and my preferred solution.
(define (insert-by-last-match insert find lst)
(let loop ((lst (reverse lst)) (acc '()))
(if (null? lst)
acc
(let ((a (car lst)))
(if (equal? a find)
(append (reverse (cdr lst))
(list* find insert acc))
(loop (cdr lst) (cons a acc)))))))
The other one is kind of obscure. Whenever you find the element you replace last-match with a callback that replaces the computation since it was made and until it gets called with the replacement and the rest of the list, which of course is the correct result. The work done until the end of the list is simply discarded since it is not used, but we do it since we are not sure if we are going to find a later one and then all the work uptil that is of course included in the result.
(define (insert-by-last-match insert find lst)
(define (helper lst last-match)
(if (null? lst)
(last-match)
(let* ((a (car lst)) (d (cdr lst)))
(cons a
(if (equal? a find)
(let/cc k
(helper d (lambda () (k (cons insert d)))))
(helper d last-match))))))
(helper lst (lambda () lst)))
call/cc (or its variant let/cc) is often described as time travel or advanced goto. It is not very intuitive. Here is a CPS version:
(define (insert-by-last-match insert find lst)
(define (helper lst last-match k)
(if (null? lst)
(last-match)
(let* ((a (car lst)) (d (cdr lst)) (k2 (lambda (v) (k (cons a v)))))
(if (equal? a find)
(helper d (lambda () (k2 (cons insert d))) k2)
(helper d last-match k2)))))
(helper lst (lambda () lst) (lambda (v) v)))
Basically this is the same as the previous only that here I have written the CPS code and with the let/cc version the implementation does it for me and I get to use k exactly where I need it. In this version you see there is no magic or time travel but the execution that should happen later is simply replaced at a point.
Write in a similar way insertL and apply it to the reversed list.
And reverse the result. Then you will have an insertion beside first appearance of word starting from the end of list
(define insertL
(lambda (new old lst)
(cond ((null? lst) '())
((eq? (car lst) old) (cons new lst))
(else (cons (car lst) (insertL new old (cdr lst)))))))
(define last-insertR
(lambda (new old lst)
(let* ((rlst (reverse lst))
(result (insertL new old rlst)))
(reverse result))))
test:
(last-insertR 'aa 'a '(b c d a h i a g))
;; '(b c d a h i a aa g)
By the way, the beauty of cond is that you can put the conditions always at the beginning - listed one under the other.
So one can write your insertR nicer as:
(define insertR
(lambda (new old lst)
(cond ((null? lst) '())
((eq? (car lst) old) (cons old (cons new (cdr lst))))
(else (cons (car lst) (insertR new old (cdr lst)))))))

Scheme: sort elements by pivot

I'm new to Scheme and to functional programming so please be gentle.
I'm trying to implement a function that takes a list and a pivot and return a list that contains the following 2 lists:
One for all elements that are less or equal to the pivot,
and one for all the elements that are greater than the pivot.
So I wrote the following code (EDITED (& WORKING) CODE - PROBLEM SOLVED):
define helper (lambda (lst pivot)
(define lst1 null)
(define lst2 null)
(define my-split (lambda (lst pivot lst1 lst2)
(if (null? lst)
(list lst1 lst2)
(if (<= (car lst) pivot)
(my-split (cdr lst) pivot (cons (car lst) lst1) lst2)
(my-split (cdr lst) pivot lst1 (cons (car lst) lst2))))))
(my-split lst pivot lst1 lst2)))
My current problem is that lst1 and lst2 are null at the end of the run so I guess the problem is with the lines (cons (car lst) lst1) & (cons (car lst) lst2))).
I saw some implementations on the web which use some complex commands that I'm not allowed to use (yes, it's homework).
Please offer ways to fix my code rather than offering your own.
Thanks
You correctly identified the two lines that are the main problem. cons just builds and returns a new list, whereas you're trying to mutate the variables lst1 and lst2. The correct way to do this is (set! lst1 (cons (car lst) lst1)) and (set! lst2 (cons (car lst) lst2)). Keep in mind, though, that good functional programming style avoids mutation. A good way to do that in this case would be to pass the two sub-lists as arguments as you recur down the main list, then return them when you get to the end.
Just like an expression like str.concat("hey") in Java doesn't change what str is (cons 1 lst1) doesn't change what lst1 is. It just returns a new value. Most of your function consist of dead code and if you really want to learn functional programming then altering bindings and objects are off limits.
You need to do something like this:
(define (count-odds lst)
(define (helper lst odds)
(cond ((null? lst)
odds)
((odd? (car lst))
(helper (cdr lst) (+ 1 odds)))
(else
(helper (cdr lst) odds))))
(helper lst 0))
(count-odds '(1 2 3))
; ==> 2
We never alter odds, we just update what is sent to the next recursion. Since Scheme has tail call elimination this is just like updating the variable in a while loop without actual mutation.

How to reverse nested lists in Scheme

Consider:
(define (nested-reverse lst)
(cond ((null? lst) '())
((list? (car lst)) (nested-reverse (car lst)))
(else
(cons (nested-reverse (cdr lst))
(list (car lst))))))
When I input,
(nested-reverse '((a b c) 42))
it gives me ((() 42) (a b c)). It's supposed to give me (42 (c b a)). How I would change my code so that the nested lists also get reversed?
Keep in mind that a list (1 2 3) is (cons 1 (cons 2 (cons 3 '()))). Using append is a very poor choice on how to reverse a list since append is implemented like this:
(define (append lst1 lst2)
(if (null? lst1)
lst2
(cons (car lst1) (append (cdr lst1) lst2))))
A list can be iterated from the first element towards the end while it can only be made in reverse. Thus the obvious none recursive reverse would look like something like this:
(define (simple-reverse lst)
(let loop ((lst lst) (result '()))
(if (null? lst)
result
(loop (cdr lst) (cons (car lst) result)))))
To make it work for nested list you check if you need to reverse (car lst) by checking of it's a list or not and use the same procedure as you are creating to do the reverse on the element as well. Other than that it's very similar.

How to define Sets in scheme

I'm having trouble using my member? function. I need to recurse on my set? function until the last element in my list 'lst' is reached. I believe I have the navigation down correctly, but maybe my inputs syntax is wrong. I know there are three cases:
1) What happens if the list is empty? that means that there aren't any duplicates in it
2) What happens if the current element of the list exists somewhere in the rest of the list? then it means that there's a duplicate in the list (hint: the member procedure might be useful)
3) If none of the above are true, continue with the next element.
Here is my code.
(define (member? e lst)
(if (null? lst) #f
(if (equal? e (car lst)) #t
(member? e (cdr lst)))))
(define (set? lst)
(if (null? lst) #t ;Case1
(if (member? (car lst) lst) #f ;Case2
(set? (cdr lst))))) ;Case3
;Example tests for the set? function
(set? '(x y z))
(set? '(a 1 b 2 c 3))
(set? '())
(set? '(6 2 2))
(set? '(x y z x))
There's a small mistake with your code, look how it gets fixed:
(define (set? lst)
(if (null? lst)
#t
(if (member? (car lst) (cdr lst)) ; in here
#f
(set? (cdr lst)))))
In particular, notice what this line is doing:
(member? (car lst) lst)
That won't work: the test is checking whether the first element in lst is a member of lst - and that'll always be true. The solution is simple, just check to see if the current element is in the rest of the list, if it's there, then we know that we've found a duplicate:
(member? (car lst) (cdr lst))
And by the way, the above code would look much nicer using cond, which is great when you have nested ifs:
(define (set? lst)
(cond ((null? lst) #t)
((member? (car lst) (cdr lst)) #f)
(else (set? (cdr lst)))))

Resources