Remove duplicates from a list in Scheme - scheme

I need to remove consecutive duplicates from a list in Scheme for ex:
(remove '(e f f g h h e e))
should return
(e f g h e)
This is what I have but I keep getting an error:
(define (remove lst)
(cond
((null? lst '())
((null? (cdr lst)) '())
((equal? (car lst)(car(cdr lst)))(remove(cdr lst)))
(cons(car lst)(remove (cdr lst))))))
I thought I was on the right track but I can't see what I'm doing wrong.

The parentheses are incorrect in the first case, and in the last case you forgot to write the else. Also, the second case is incorrect; this is what I mean:
(define (remove lst)
(cond
((null? lst) '())
((null? (cdr lst)) lst)
((equal? (car lst) (car (cdr lst))) (remove (cdr lst)))
(else (cons (car lst) (remove (cdr lst))))))

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

Is there anything wrong with my "sum of list" code in scheme?

My else statement line is giving me an error. Is any of my other line of codes affecting the else expression?
(define (sumAdd list)
(cond
((null? list) '())
((null? (cdr list)) list)
((symbol? list) sumAdd(cdr list))
(else (+ (car list)(sumAdd (cdr list))))
)
)
If I understand correctly, you want to sum all the numbers in a list with mixed element types. If that's the case, there are several errors in your code:
(define (sumAdd list) ; `list` clashes with built-in procedure
(cond
((null? list) '()) ; base case must be zero for addition
((null? (cdr list)) list) ; why discard the last element?
((symbol? list) sumAdd(cdr list)) ; that's not how procedures are called
(else (+ (car list) (sumAdd (cdr list)))))) ; this line is fine :)
This is the correct way to implement the procedure:
(define (sumAdd lst)
(cond
((null? lst) 0) ; base case is zero
((symbol? (car lst)) (sumAdd (cdr lst))) ; skip current element
(else (+ (car lst) (sumAdd (cdr lst)))))) ; add current element
It works as expected:
(sumAdd '(1 a 2 b 3 c))
=> 6

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

Writing flatten method in Scheme

I have been working on the following function flatten and so far have it working for just lists. I was wondering if someone could provide me with some insight on how to get it work with pairs? For example (flatten '(a .a)) would return (a a). Thanks.
(define (flatten list)
(cond ((null? list) null)
((list? (car list)) (append (flatten (car list)) (flatten (cdr list))))
(else
(cons (car list) (flatten (cdr list))))))
Here's one option:
(define (flatten x)
(cond ((null? x) '())
((pair? x) (append (flatten (car x)) (flatten (cdr x))))
(else (list x))))
This does what you want, without requiring append, making it o(n). I walks the list as a tree. Some schemes might throw a stack overflow error if the list is too deeply nested. In guile this is not the case.
I claim no copyright for this code.
(define (flatten lst)
(let loop ((lst lst) (acc '()))
(cond
((null? lst) acc)
((pair? lst) (loop (car lst) (loop (cdr lst) acc)))
(else (cons lst acc)))))
(define (flatten l)
(cond
[(empty? l) empty]
[(list? l)
(append (flatten (first l))
(flatten (rest l)))]
[else (list l)]))

How to remove non-duplicate elements from a list in Scheme?

Given a list,
(define ll '(a a a b c c c d e e e e))
I want to remove all non-duplicate elements and leave only one copy of the duplicate one, i.e. after removing, the result would be
(a c e)
My algorithm is:
Traverse through the list, comparing current element with next element.
If they're equal, then cons the current element with the list of the next recursive call. For example,
(a a a b c)
Move from left to right, encounter a and a.
(cons a (remove-nondup (cddr lst)))
Otherwise, skip current and next element.
(remove-nondup (cddr lst))
The problem I'm having is
(define (remove-nondup lst)
(if (>= (length lst) 2)
(if (eq? (car lst) (cadr lst))
(cons (car lst) (remove-nondup (cdr lst)))
(remove-nondup (cddr lst)))
lst))
The problem that I'm having is if there are more than 3 consecutive elements, I have no way to keep track of the previous-previous one. So I wonder should I use another procedure to remove all duplicates? or I can just put them into one procedure?
So my alternative current solution was,
(define (remove-dup lst)
(if (>= (length lst) 2)
(if (eq? (car lst) (cadr lst))
(cons (car lst) (remove-dup (cddr lst)))
(cons (car lst) (remove-dup (cdr lst))))
lst))
(define (remove-nondup-helper lst)
(if (>= (length lst) 2)
(if (eq? (car lst) (cadr lst))
(cons (car lst) (remove-nondup-helper (cdr lst)))
(remove-nondup (cddr lst)))
lst))
; call the helper function and remove-dup
(define (remove-nondup lst)
(remove-dup (remove-nondup-helper lst)))
Here's my solution: first, grab bagify (any version will do). Then:
(define (remove-singletons lst)
(define (singleton? ass)
(< (cdr ass) 2))
(map car (remove singleton? (bagify lst))))
remove is from SRFI 1. If you're using Racket, run (require srfi/1) first. Or, use this simple definition:
(define remove #f) ; Only needed in Racket's REPL
(define (remove pred lst)
(cond ((null? lst) lst)
((pred (car lst)) (remove pred (cdr lst)))
(else (cons (car lst) (remove pred (cdr lst))))))
Here's a way that uses only standard library functions and only tail calls, though it performs linear searches to see if an item has already been seen or put in the result:
(define remove-nondup
(λ (ls)
(reverse
(let loop ([ls ls] [found '()] [acc '()])
(cond
[(null? ls)
acc]
[(memq (car ls) found)
(loop (cdr ls)
found
(if (memq (car ls) acc)
acc
(cons (car ls) acc)))]
[else
(loop (cdr ls)
(cons (car ls) found)
acc)])))))
(remove-nondup '(a a a b c c c d e e e e)) =>
(a c e)
(remove-nondup '(a a a b c c c d e e e e f a a f)) =>
(a c e f)
The loop is a "named let": a handy way to stick a helper procedure inside a procedure without a lot of syntactic clutter.
If you only want to shrink consecutive duplicates down to one item, and remove items only when they don't occur twice consecutively, then here's a way to "remember" the item two cells ago without searching for it, and using only tail calls:
(define remove-nonconsecdup
(λ (ls)
(reverse
(letrec (
[got1 (λ (ls prev acc)
(cond
[(null? ls)
acc]
[(eq? prev (car ls))
(got2 (cdr ls) (cons prev acc))]
[else
(got1 (cdr ls) (car ls) acc)]))]
[got2 (λ (ls acc)
(cond
[(null? ls)
acc]
[(eq? (car acc) (car ls))
(got2 (cdr ls) acc)]
[else
(got1 (cdr ls) (car ls) acc)]))])
(if (null? ls)
'()
(got1 (cdr ls) (car ls) '()))))))
(remove-nonconsecdup '(a a a b c c c d e e e e)) =>
(a c e)
(remove-nonconsecdup '(a a a b c c c d e e e e f a a f)) =>
(a c e a)
I don't like reversing lists, but calling reverse is easy. If the extra cons'ing done by reverse is a problem, you could do non-tail calls or stick the items at the end of the list, but that's harder to do efficiently (but easy with a non-standard library macro).

Resources