I am trying to write a procedure that takes a list that may or may not include duplicates, and then return that list without duplicates, and in sorted order. What I came up with so far is:
(define (remove-duplicated list)
(if (null? list)
'()
(if (= (car list) (cadr list))
(cdr list)
(cons (car list) (remove-duplicates (cdr list))))))
I'm not quite sure what the problem is, besides sorting the list. For example, if I input
(remove-duplicates '(3 3 4 5 6 6 7))
returns
(3 4 5 6 6 7)
a fairly simple procedure that will take in a list that may or may not
include duplicats, and then return that list without any duplicates
included, and in sorted order.
There are at least two ways that you could do this:
sort the list once the duplicates are removed; or
remove the duplicates after the list has been sorted.
Óscar López pointed out that
[Your] implementation fails because you're only testing for two
consecutive values, you have to search the current element in the rest
of the list, use member for that.
This will be an issue if you remove the duplicates before sorting, since a given element in the list could have duplicates anywhere else in the list. However, if you sort the list first, then you would be guaranteed that any duplicate elements do immediately follow the original, so you wouldn't need to check the whole list. Removing duplicates is easier if the list is sorted, but sorting a list isn't really any easier after duplicate elements are removed, so it really does make sense to sort the list first and then remove duplicates. (I suppose you could be even more efficient, and write your own sort-and-remove-duplicates procedure, but almost certainly not really necessary.)
Your code, if you're assuming that list is already sorted, is almost correct. There are two adjustments necessary:
In the base case, you're only checking whether (null? list). However, for a non-null list, you then compare (car list) and (cadr list), but if list only has one element, then (cadr list) is an error. Fortunately, lists with only one element have no duplicates, so your base case can be (or (null? list) (null? (cdr list))).
The then part of the second if needs to be (remove-duplicated (cdr list)), not (cdr list), since list can still have more duplicates farther down (e.g., (x x x ...) or (x x y y ...)).
This is your code with those modifications and some comments:
(define (remove-duplicated list)
;; remove duplicates from a *sorted* list. Because the
;; list is sorted, any duplicates of an element will
;; immediately follow the first occurrence of the element.
;;---------------------------------------------------------
;; If the list has the form () or (x)
(if (or (null? list)
(null? (cdr list)))
;; then it has no duplicates, so return it
list
;; otherwise, if the list looks like (x x ...)
(if (= (car list) (cadr list))
;; then you can discard the first element, but you
;; still need to remove duplicates from the rest of
;; the list, since there can be more duplicates later
(remove-duplicated (cdr list))
;; otherwise, you need the first element of the list
;; and can simply remove-duplicated from the rest.
(cons (car list) (remove-duplicated (cdr list))))))
This works as expected:
(remove-duplicated '(1 1 2 3 3 4 5 6))
;=> '(1 2 3 4 5 6)
The fact that the input list might be sorted slipped my mind. What I'm about to describe will work for removing duplicate elements from any list, sorted or not. For the general case of removing duplicates in a list, you have to search the current element in the rest of the list, using member for that.
Also, you have to advance the recursion in both cases, and be aware that in the last line you're calling remove-duplicates (which is a built-in procedure in some interpreters, so maybe you don't have to implement it from scratch!), but you named the procedure remove-duplicated. As a side note, it's a bad idea to name a parameter list, that'll clash with a built-in function - I took the liberty of renaming it. This will fix the problems, and it's a more general solution:
; if the input list is not sorted, use this
(define (remove-duplicated lst)
(if (null? lst)
'()
(if (member (car lst) (cdr lst)) ; changes here
(remove-duplicated (cdr lst)) ; and here
(cons (car lst)
(remove-duplicated (cdr lst))))))
Now, if the input list is sorted to begin with, this is how to fix your code. Most of my comments apply, except that you don't have to use member and the base case is a little different:
; if the input list is sorted, use this
(define (remove-duplicated lst)
(if (or (null? lst) (null? (cdr lst))) ; changes here
lst
(if (= (car lst) (cadr lst))
(remove-duplicated (cdr lst)) ; and here
(cons (car lst)
(remove-duplicated (cdr lst))))))
Either way, the procedure will work as expected as long as you use the right one for the input (the first implementation is for sorted or unsorted input lists, the second one works only for sorted lists):
(remove-duplicated '(3 3 4 5 6 6 7)) ; sorted input, both implementations work
=> '(3 4 5 6 7)
Finally, if you need to make sure that the output list will always be sorted, but have no guarantees that the input list was sorted, then use my first implementation of remove-duplicated and sort it afterwards, check your interpreter to find out which sorting procedures are available - the following will work in Racket:
(sort (remove-duplicated '(3 6 3 7 4 5 6)) <) ; using my first remove-duplicated
=> '(3 4 5 6 7)
… Or sort the list first and then use my second implementation of remove-duplicated. You have so many options to solve this problem!
(remove-duplicated (sort '(3 6 3 7 4 5 6) <)) ; using my second remove-duplicated
=> '(3 4 5 6 7)
Related
I have to create a recursive Scheme function for my programming class that will take all the odd-numbered elements of a list, and then return them in reversed order.
I have a function for reversing a list, and another function for getting the odd-numbered elements, but can't figure out how to combine the two into a new function, as they both are recursive.
It has to be one function that doesn't call any functions other than itself. It can not call odd or reverse, and it has to have the same functionality as calling both would have.
Odd Function:
(define (odd lst)
(if (null? lst)
'()
(cons (car lst)
(odd (cddr lst)))))
Reverse Function:
(define (reverse lst)
(if (null? lst)
'()
(append (reverse (cdr lst))
(list (car lst)))))
Any help would be appreciated!
Your reverse mostly does what you want, except that it includes the even-numbered elements as well, in its result.
For example, if you were trying to reverse (1 2 3 4 5 6 7) with your reverse, it would make a recursive call to reverse the list (2 3 4 5 6 7); if you could get it to leave off the 2 from that recursive call, you'd be in good shape here (although you'll have to deal with an edge case which you'll discover quickly enough).
Im very new to scheme and the ideas of car, cdr, etc. I have this function to return the last element of a list, but right now it just returns an empty list.
(define (last mylist)
(if (list? mylist)
(if (null? mylist)
(if (null? (cdr mylist))
'()
(last (cdr mylist))
)
)
)
)
The book How To Design Programs helps you answer this problem by giving you a specific and detailed design recipe. This particular problem is covered in section 9.2, "Non-empty Lists". Broadly, here are the steps you need to follow:
formulate a data definition for non-empty lists (or take it from the book)
write the purpose statement, signature, and header for your function
WRITE TEST CASES (your test cases are going to help a lot here. (keep in mind that you don't need to test inputs that aren't allowed by your data definition)
add the template associated with your data definition (also appears in the book)
fill in the blank places in the template to complete your definition
debug.
By your indentation alone, it's very evident that you're coming to Scheme from another programming language
But you're also using if incorrectly – in Scheme, you cannot have a single-branch if statement. Well, there's no statements in Scheme at all, only expressions, and if expressions will always take 3 operands (arguments)
the predicate (condition)
the consequent (what happens if the predicate is true)
the alternative (what happens when the predicate is false)
Your program is close tho. Just a little adjustment and you're right where you need to be – take note of how the indentation makes it easy to see if's 3 operands.
(define (last mylist)
(if (null? mylist)
#f
(if (null? (cdr mylist))
(car mylist)
(last (cdr mylist)))))
Lastly, Scheme offers cond that helps prevent unnecessary code nesting for sequences of conditions
(define (last mylist)
(cond ((null? mylist)
#f)
((null? (cdr mylist))
(car mylist))
(else
(last (cdr mylist)))))
(last '())
;; #f
(last '(1))
;; 1
(last '(1 2))
;; 2
(last '(1 2 3))
;; 3
Beyond the scope of this answer is the return value #f for (last '()) – I would argue that calling last on an empty list should have the same effect of calling car on an empty list. But I'll leave it up to you.
If (null? mylist) does not hold, what is it? A non-empty list.
Non-empty lists may have one, or more, elements.
How many elements have such lists that their last element is their first?
What can be said about such lists' cdr? You should use this to stop your recursion earlier. Right now it continues until the list is empty, but you need to stop earlier than that.
(define (last mylist)
(if (list? mylist)
(if (null? mylist)
'()
;; {1}
(last (cdr mylist))
;;
)))
at {1} you call (last (cdr mylist)) unconditionally. But what if you've reached the end of your list? What if only one element is left? You'll need to return it as the result, in such case. So, replace the unconditional code with an if expression to accomplish this.
When you asked the question (null? (cdr mylist)), you should have returned (car mylist) if that's true instead of '(). Since at that point it means that mylist is a single-atom list.
(define (last mylist)
(cond ((null? mylist) '())
((null? (cdr mylist)) (car mylist))
(else (last (cdr mylist)))))
You could use cond instead of if to avoid nested conditions, since cond handle many arms while if is often used for when you have only two options for a condition.
This book the Little Schemer did best at helping me visualize what's going on in a Scheme program.
I think this comes closest to your initial code:
(define (last mylist)
(if (list? mylist)
(if (null? mylist)
'() ; input list is empty
(if (null? (cdr mylist))
(car mylist) ; list only has one remaining element so this is it
(last (cdr mylist)))) ; otherwise, recurse
#f)) ; input is not a list
When using if, be sure to always fill out both the true and the false branches.
Lets say I have a list containing arguments, how can I display it without the parentheses, example:
(define lst (list 1 2 3)) (display lst)
-> (1 2 3)
But I want it to appear as: 1 2 3
My attempt:
(define (clean-list lst)
(if
(null? lst) (display (null))
(display (car lst)))
(display #\space)
(clean-list (cdr lst)))
It returns the the list without parentheses, but with an error message... Anyone who could help me with this? Also note that im new to racket and racket is my first programming language. Appreciates all answers!
The error is caused by the fact that you always call recursively the function, after the if, even when the list is null.
Here is a correct version:
(define (clean-list lst)
(when (cons? lst)
(display (car lst))
(display #\space)
(clean-list (cdr lst))))
Note that this function prints only the elements on the first level of the list without parentheses, but if an element is a list it is printed with parentheses.
I've been looking over this and I can add "something" to the end of the list, but the issue I'm arriving at is adding, specifically, the first element of a list to the end of that same list.
For example:
{1, 2, 3, 4} becomes {1, 2, 3, 4, 1}.
Here is the code that I'm having problems with:
(define (copy-first-to-end lst)
(cond [(empty? lst)
(cons (first lst) empty)]
[else (cons (first lst)
(copy-first-to-end (rest lst)))]))
The issue with this code is that, for the empty? condition, the answer calls (first lst) but because it is recursive, the first element of this list is empty. Since scheme is dynamically typed, I can't store the first element anywhere (at least I don't think I can).
How can I get this to work, using only the basic list functions? (e.g., cons, cons?, empty? first, last, rest)
You can use closures to store anything you want.
Here's the solution I wrote, with a few details removed to give you some space to think. :-)
(define (copy-first-to-end lst)
(define top ???)
(define (inner lst)
(if (null? lst) ???
(cons ??? (inner ???))))
(inner ???))
In this case, inner is a closure that, among other things, has access to the top variable (which you will use to stash your value of interest).
So you are essentially trying to write your own implementation of the append function, with the special case that you will be appending a list containing the first s-expression from the list. Your problem is that you can't cons a flat list into a flat list and get a single flat list as a result. The only way to do this with cons is to break the first list down into its constituent s-expressions and then cons them into the second list in reverse order. Recursion should make this a simple task.
(define (append-first-to-end lst)
(define (append-to-end a lst)
(if (null? (cdr lst)) ???
(cons ??? (append-to-end a ???))))
(append-to-end (car lst) lst))
Between my example, Chris's example and my opening paragraph, you should be able to fill in the blanks.
I do hope the action required once you have the final s-expression from lst is obvious...
I would like to go through a list (which may have nested lists) and have it evaluate to one flattened list with all of the elements. I can't even get a recursive function to evaluate to anything other than nil
(defun pl(lst)
(if (atom lst)
lst
(progn
(pl (car lst))
(pl (cdr lst)))))
I'll then call with something like (pl '(1 (2 3) (4 5))), but it always evaluates to nil.
I changed
(if (atom lst) lst
to
(if (atom lst) (print lst)
and this doesn't even print any of the items of the list.
what concept I am missing here?
A function will normally only return one value, which is value of its body. If you look closely at pl you see that it is an if form, so pl either returns the value of lst or the value of progn.
The first thing I have to point out here is that the return value of a (progn ...) form is the value of its last expression, which in this case is the recursive call (pl (cdr lst)). As you do not do anything with the return value of (pl (car lst)) this call has no effect whatsoever. The value of the recursive call will at some point fall through the atom test. The second thing to point out here is that (atom nil) is also true. Remember that the last element of a list is nil, so when you give pl a list it will always return nil as you have observed.
If your print version shows nothing, it is probably because the print output is shown somewhere else, in another buffer for example.
As for the solution: you either want a pure recursive solution by using append instead of progn, because that's what in your homework assignment. In regular lisp you just use one of the iteration constructs.
My advice is to check any textbook on lisp or scheme to grasp the fundamentals of recursion and tail-recursion.
You might consider looking at an introductory text like "The Little Schemer". One of the first things it does is shows you how to take a consistent approach to problems just like this. The resulting function might look like:
(define (pl lst)
(cond ((null? lst) '())
((not (pair? lst)) (list lst))
(else (append (pl (car lst))
(pl (cdr lst))))))
Sure, this is Scheme, but the approach is the same for Lisp:
Check for the special case where the argument is nil
Check for the special case where the argument is an atom
Recursively apply the function to the car and cdr of the argument, appending the results
The same function in Lisp would look like:
(defun pl (lst)
(cond ((null lst) '())
((atom lst) (list lst))
(t (append (pl (car lst))
(pl (cdr lst))))))
Arguments can be made that this is not the most efficient code, but that isn't the point. This type of pattern occurs over and over again. It's a good idiom to learn.