Linearly recursive list-difference function in Common Lisp - algorithm

I was going through this tutorial for fun, and got stuck on the very last thing he says, "Exercise: Give a linearly recursive implementation of union and difference." (for a list)
Union, no sweat.
Difference, sweat.
An attempt looks like this. . .
(defun list-diff (L1 L2)
(cond
((null L1) L2)
((null (member (first L1) L2)) (cons (first L1) (list-diff (rest L1) L2)))
(t (list-diff (rest L1) L2))
)
)
Now, that returns all the elements that are in L1 that aren't in L2, but it just returns all of L2 (obviously). Similarly, if I change the L2 in line 3 to "nil", then it just returns all of L1 that aren't in L2, but none of L2.
My attempts at work-arounds don't look recursive, and when they are, I end up getting stack-overflows (like if I try calling (list-diff L2 L1) somewhere).
Any of his other exercises, such as list-intersection, only require running through the elements of L1. Here, I want to strike the crucial elements from L2, or run (list-diff L2 L1) and then union the results from both, but that's not really linearly-recursive anymore.
Thoughts?
(not homework, really. I just thought I'd try to look at some LISP for fun.)
EDIT: a function that does this properly, based on the responses is:
(defun list-diff (L1 L2)
(cond
((null L1) nil)
((null (member (first L1) L2)) (cons (first L1) (list-diff (rest L1) L2)))
(t (list-diff (rest L1) L2))
)
)

The set difference operation L1 \ L2 is defined as all elements e such that e is in L1 but e not in L2. So it looks to me like your second attempt is actually correct:
Similarly, if I change the L2 in line
3 to "nil", then it just returns all
of L1 that aren't in L2, but none of
L2.
It looks like you're trying to compute the symmetric difference, although it isn't clear to me that this is what the exercise requests.
If you want to be clever about it, you can probably pass a third list into the function to serve as an accumulator. When L1 has elements, push the first element into the accumulator (and call recursively) when (null (member (first L1) L2)). When L1 is null, check the elements of L2 against the accumulator list, doing the same thing. When L1 and L2 are null, return the accumulator list.

In Lisp this is the definition of 'set difference':
set-difference list1 list2 &key (test #'eql) test-not (key #'identity)
Function
Returns a list of elements of list1 that do not appear in list2.
That's your modified implementation:
(defun list-diff (L1 L2)
(cond
((null L1) L1)
((member (first L1) L2) (list-diff (rest L1) L2))
(t (cons (first l1) (list-diff (rest l1) l2)))))

Related

Remove match elements from list using Scheme

I am trying to write a function which takes in two lists, L1 and L2, and returns a list which contains all of L1 except for the elements which are also found in L2.
Example: Pass in '(1 2 3) '(3 4 5) and the return should be '(1 2)
I have written a helper function called removeOne which is working perfectly fine:
(define (removeOne x L)
(cond ((null? L) '())
((= (car L) x) (removeOne x (cdr L)))
(#t (cons (car L) (removeOne x (cdr L))))))
My issue is with the main function which is supposed to use removeOne to get rid of all matching elements from L1. I have written this so far:
(define (removeAll L1 L2)
(cond ((null? L1) '())
((null? L2) '())
((= (car L1) (car L2)) (removeOne (car L2) L1))
(#t (removeAll (cdr L1) L2))))
This is currently returning () no matter what lists I use as input.
I feel like I'm close to getting this to work. Any help would be greatly appreciated!
I have hard time following your logic in removeAll. Since you have written a removeOne, shouldn't you just call removeOne with all elements of L2?
Besides, this condition
((null? L2) '())
is strange.
You are supposed to return all elements of L1 that are not in L2. And L2 is empty. So, all elements of L1 that are not in '()... how can this be the empty list? No elements are in (). So, "all elements of L1 that are not in ()" is just a convoluted way to say "L1". So, clearly, case (null? L2) should be associated to result L1.
So, if I summarize the recursion of removeAll, what you want to do is remove (car L2) from L1, then remove all of (cdr L2) from that result (or the other way, it doesn't really matter). Unless L2 is empty, in which case, removeAll has nothing to remove.
So
(define (removeAll L1 L2)
(if (null? L2)
L1
(removeAll (removeOne (car L2) L1) (cdr L2))))
Or,
(define (removeAll L1 L2)
(if (null? L2)
L1
(removeOne (car L2) (removeAll L1 (cdr L2)))))
(same result. The 1st one remove first (car L2) from L1, then remove (cdr L2) from the result. The second one remove first (cdr L2) from L1, then remove (car L2) from the result. In other words, the 1st one remove one by one elements of L2 from L1, in the normal order, while the second one does it in reverse order.
There are a few atomic functions for searchcing lists, like member, that you can use. Using such a function you will reduce your problem to a recursive function with 3 cases: null, match and otherwise. Depending on your interest to compare objects, you will use member or other one, they use all a different equality operator.

Trying to append recursively, what am I doing wrong in Scheme?

Trying to recursively append every element from list a to list c. Currently only one elements gets appended. What am I doing wrong? I', trying to reverse a list. But currently stuck understanding how to even append a list to another list.
(define a '(1 2 3))
(define c '())
(define fun
(lambda (() l1) l1
(l l1) (append l1 (car l)) (fun (cdr l) l1)
))
(fun a c)
I'm using a scheme like interpreter, where I cannot use any new built-in functions except of the listfunctions.
You need to use
(lambda (l l1)
(if (null? l)
l1
(append l1 (car l)) (fun (cdr l) l1)))
what you wrote looks like it would work if there was a pattern matching facility, but our lambda is more basic and doesn't pattern match.

Interleave Scheme, cons contract violation

I am new to writing scheme for a class. We were asked to write an interleave method, and here is what I have so far:
(define (Interleave L1 L2)
(if (null? L1) L2)
(if (null? L2) L1)
(cons (car L1) (Interleave (L2) (cdr L1))))
I am trying to run it in DrRacket using (Interleave '(1 2 3) '(4 5 6))
My expected output should be (1 4 2 5 3 6), but instead I keep getting an error:
car: contract violation
expected: pair?
given: ()
And I have no idea how to fix it. Any help is appreciated!
Since you mention DrRacket I assume you have written it in some other editor and pasted the code in? DrRacket usually indent correctly. You can force it with CTRL+i. Here is your code with that done in DrRacket:
(define (Interleave L1 L2)
(if (null? L1) L2) ; dead code
(if (null? L2) L1) ; dead code
(cons (car L1) (Interleave (L2) (cdr L1)))) ; always done
The last line always happen so your test if either are null? doesn't do anything. they evaluate to L2 and L2 but since it is not the tail expression. In order for you if to have an impact the rest of your code have to be in them. You can also use cond which prevents the need to nest if so that it looks more like elseif does on other languages.
Since your procedure does not have a base case it fails when car or cdr fails when the argument is the empty list.
PS: I notice that you check if L2 is null? but on the last line you do (L2) which means you call it as if L2 is a procedure like cons and car. That will not work.

Racket find shared elements between lists

I'm trying to create a specific response for a given list if it has shared elements with another list. As in if I have a list that is (My name is John) and I have another list of (John Adam Jacob) I would want the first list to be able to see that John is in the second list, and be able to print something along the lines of (this is a known name) or something similiar.
The code I have thought of uses map, and member.
(define (specific-reply user-list)
(cond (member (map (lambda (user-list)) '(John Adam Jacob)))
(write (this is a known name))
(else
(write (this is not a known name)))))
I'm extremely knew to both racket and scheme however and I haven't really gotten it to compile yet so I think I'm largely off.
Any help would be greatly appreciated.
You don't need to complicate the problem if your task is to just find if a is a member of (a b c),
Here's a piece of Scheme code that can tell if a is a member of lat.
It's just a simple recursive function that compares each element of lat with a for a match.
(define member?
(lambda (a lat)
(cond
((null? lat) #f)
((eq? a lat) #t)
(else
(member? a (cdr lat))))))
If you want to take this further and find the intersection of two lists, we can do something like this!
(define intersect
(lambda (set1 set2)
(letrec
((I (lambda (set)
(cond
((null? set) (quote ()))
((member? (car set) set2)
(cons (car set)
(I (cdr set))))
(else (I (cdr set)))))))
(I set1))))
You can use this code as such. Tested from guile compiler
(begin
(display (intersect `(1 2 3) `(1 3 4 5 2)))
(newline))
>> (1 2)
EDIT
I recommend you read The Little Schemer and the The Seasoned Schemer to get more familiar with these kind of concepts
Why not use set in racket:
(define (list-intersect-2 lst1 lst2)
(set->list
(set-intersect (list->set lst1)
(list->set lst2))))
For a solution that takes one or more lists:
(define (list-intersect lst1 . lstn)
(set->list
(foldl set-intersect
(list->set lst1)
(map list->set lstn))))
(list-intersect '(1 2 3) '(2 3 4) '(3 4 8))
; ==> (3)
One can also use built-in functions filter and member to find intersection of 2 lists:
(define (intersection l1 l2)
(remove-duplicates
(filter (λ (x) (member x l1))
l2)))
Above checks each item of l2 to keep it only if it is a member of l1 also.
One can also use for/list to check each element and return a list of common items:
(define (intersect l1 l2)
(remove-duplicates
(for/list ((i l1)
#:when (member i l2))
i)))
Both above function remove duplicates. Just avoiding use of remove-duplicates may result in different result if simply the order of l1 and l2 is interchaged. If one wants that the repeated elements to come repeatedly in outcome list, one can use following function in which common items are removed before proceeding:
(define (intersection2 l1 l2)
(let loop ((l1 l1)
(l2 l2)
(ol '()))
(cond
[(empty? l1) (reverse ol)]
[(member (first l1) l2) ; first item of l1 is common
(loop (rest l1) ; loop with rest of l1
(remove (first l1) l2) ; remove common item from l2
(cons (first l1) ol))] ; add common item to outlist
[else
(loop (rest l1)
l2
ol)])))
Testing:
(intersection2 '(2 4 2 7 2 10) '(10 2 9 2 0 11))
Output:
'(2 2 10)

Scheme, sublist

The function is called sublist? with two arguments (both lists). It checks whether l2 is a sublist of l1 and returns #t or #f.
I have this so far, but seems the exists function is not working properly
(define (sublist? l1 l2)
(cond ((null? l2) #t)
((exists l1 (car l2)) #t)
(else (sublist? l1 (cdr l2)))))
(define (exists l p)
(if (null? l) #f
(or (equal? p (car l)) (exists (cdr l) p))))
updated
First of all I think in your exists function, you are missing an equals?
And it seems the first parameter is the one that's supposed to be an atom, but in your sublist function you send the list first and the atom next, you need to switch the parameters.
(define (exists l p)
(if (null? l) #f
(or (equal? p (car l)) (exists (cdr l) p))))
That should work fine.
Also, its convention to call your predicate functions as a question, you should name it exists?
[Edit]
Also upon closer inspection, it looks like your sublist? function is incorrect. It will return #t even if only one element of the sublist exists in the list. You need to modify it a bit into:
(define (sublist? l1 l2)
(cond ((null? l2) #t)
((not (exists l1 (car l2))) #f)
(else (sublist? l1 (cdr l2)))))
Now you are saying:
1) Is empty? Then is a sublist.
2) Is this element NOT in the list? Then is not.
3) If it is, then check the rest of the elements.
I believe the previous answer only checks subsets, not sublists. Order matters for sublists, so you can't simply check the existence of elements in the list. You need to do something like the following.
(define (sublist? l1 l2)
(cond ((null? l2) #t)
((null? l1) #f)
((headlist? l1 l2) #t)
(else (sublist? (cdr l1) l2)))
(define (headlist? l1 l2)
(cond ((null? l2) #t)
((null? l1) #f)
((not (equal? (car l1) (car l2))) #f)
(else headlist? (cdr l1) (cdr l2))))

Resources