How to reverse nested lists in Scheme - 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.

Related

How to create a function that receives a list and creates a new list in Scheme

I'm trying to create a function called evenElememt that takes a list as a parameter and appends all the elements in even positions to a new list and displays the new list.
My attempt:
(define newList '())
(define elementHelper
(lambda lst
((cdr lst)
(cons newList (car lst))
(elementHelper(cdr lst)))
)
)
(define evenElement
(lambda (lst)
(cond
((null? lst) ())
((null? (cdr lst)) ())
(else (elementHelper lst)
(display lst))
)
)
)
Example output: if I enter (evenElement '('a 'b 'c 'f 't 'y)), the output should be (b f y).
This is essentially the same as Can I print alternate elements of a list in Racket?, except that you want to print even positions (1-indexed) instead of odd positions.
(define (even-positions lst)
(cond ((null? lst)
'())
((null? (cdr lst))
'())
(else
(cons (second lst)
(even-positions (cddr lst))))))
(even-positions '(a b c f t y)) returns '(b f y).
Then, this is the function you are looking for:
(define (display-even-positions lst)
(display (even-positions lst)))
You don't need elementHelper. Just make evenElement return the result instead of displaying it.
You also don't need the global variable newList. The function should construct the result as it goes.
(define evenElement
(lambda (lst)
(cond
((null? lst) '())
((null? (cdr lst)) '())
(else (cons (car (cdr lst))
(evenElement (cdr (cdr lst)))))
)
)
)
(display (evenElement '(a b c f t y)))

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)))))))

Racket Append Undesired Reversal of List

I wrote a small function that takes a list and returns a list composed of only positive numbers. This all works fine, but for some reason, it is reversing the order. More information below on that. Could someone please tell me if this is normal, or if I miss-wrote something? Thank you in advance.
(define (positive-nums-only lst)
(if (empty? lst)
'()
(append (positive-nums-only (cdr lst))
(if (>= (car lst) 0)
(list (car lst))
'()))))
(positive-nums-only '(1 2 -4 90 -4))
The above test case returns '(90 2 1)
You did not make a mistake, the program is making what you asked.
See, the program finishes the recursion calls first, before going into resolving the if statement. This causes the (list ... ) to start listing from the last element that is positive, in this example it will be 90.
Changing the code order will produce the result you want.
(define (positive-nums-only lst)
(if (empty? lst) '()
(append (if (>= (car lst) 0 )
(list (car lst))
'())
(positive-nums-only (cdr lst)))
)
)
On the other hand, this kind of recursion could be expensive to the computer. I'd use tail recursion, like this:
(define positive-nums-only-tail
(λ (lst r)
(cond ((empty? lst) (reverse r))
((positive? (car lst))
(positive-nums-only-tail (cdr lst)
(cons (car lst) r)))
(else (positive-nums-only-tail (cdr lst) r))
)
)
)
Have you tried reversing the append?
(define (positive-nums-only lst)
(if (empty? lst)
'()
(append (if (>= (car lst) 0) (list (car lst)) '())
(positive-nums-only (cdr lst)))))
Personally I find it more natural to write it like this:
(define (positive-nums-only lst)
(if (empty? lst)
'()
(let ((rest (positive-nums-only (cdr lst))))
(if (>= (car lst) 0)
(cons (car lst) rest)
rest))))

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

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