I've been really stuck on implementing this function for some time, now, and cannot find the right resource to help me out further.
I am trying to implement a number sort, that sorts values in a list in ascending order.
My current implementation is:
(define num-sort
(lambda (ls)
(if(null? (cdr ls))
ls
(if(< (car ls) (cadr ls))
(append (car ls) (num-sort(cdr ls)))
(if(> (car ls) (cadr ls))
(append (num-sort(cdr ls)) (car ls))
(num-sort(cdr ls))
)))))
I've been having an EXTREMELY rough time with scheme and understanding logically how things are done.
My "Append" function is a function I implemented prior to this, and it appears to work as intended:
(define append
(lambda (ls1 ls2)
(if (null? ls1)
ls2
(if (pair? ls1)
(cons (car ls1) (append (cdr ls1) ls2))
(cons ls1 ls2)))))
I understand there is a problem with the recursion, here, in that when I compare two elements and decide to do another recursive call, it completely screws up the order entirely - however I'm not sure how to fix this.
In my head, what I'm thinking of doing is assembling a list by comparing each element to neighboring elements. Normally, I'd try doing this by storing and accessing indices of an array/variables in another language, but I can't wrap my head around how to sort properly in this one.
If you'd like to implement Selection Sort, here's a start:
(define (num-sort ls)
(if (null? ls)
ls
(num-sort-helper (car ls) '() (cdr ls))))
(define (num-sort-helper min-so-far checked unchecked)
# min-so-far is no bigger than anything in checked
# nothing in unchecked has been looked at
# return a list made up of min-so-far and the elements of
# checked and unchecked such that that first element returned
# is no bigger than anything else in that list
# For example, (num-sort-helper 5 '(6 9 7) '(8 2 3)) returns
# a list of all 7 numbers with 2 at the beginning
(cond ((null? unchecked) ???) # nothing is bigger than min-so-far
((<= min-so-far (car unchecked)) ???) # min-so-far is still the "winner"
(else ???))) # (car unchecked) is now the "winner"
Related
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.
(define (sqr-tail lst)
(define (helper lst res)
(if (null? lst)
res
(cond ((list? (car lst))
(helper (cdr lst)
(cons (helper (car lst) ())
result)))
(else (helper (cdr lst)
(cons (expt (car lst) 2) res))))))
(helper lst ()))
I have this tail recursion function in scheme which sqr every element in the list, but unfortunately the result is reversed to what I suppose to output.
for input :
> (sqr-tail (list 1 2 4 3 (list 1 2 (list 1)) 3 3))
the output :
< (9 9 ((1) 4 1) 9 16 4 1)
thanks.
This is something that is inherent in the way Lisp/Scheme lists work: because there are only really conses, the way to build up lists is backwards. So the common tail-recursive-loop-with-an-accumulator approach as you've used ends up building the result backwards. The simple answer to this is that you need to reverse the result when you return it, and in your case, since you are recursing (not tail-recursing) into nested lists as well you need to reverse them as well of course.
Here is a somewhat cleaned-up and error-protected version of your original function (note this was written in Racket -- it may not be quite legal Scheme, but it is close):
(define (square-nested-list/reversed l)
(define (snl-loop lt accum)
(cond [(null? lt)
accum]
[(cons? lt)
(let ([head (car lt)]
[tail (cdr lt)])
(cond [(list? head)
(snl-loop tail (cons (snl-loop head '())
accum))]
[(number? head)
(snl-loop tail (cons (* head head) accum))]
[else (error "mutant horror death")]))]
[else (error "mutant death horror")]))
(snl-loop l '()))
So to get the result forwards we need to reverse the accumulator when we return it. This is a very small change to the above function:
(define (square-nested-list/forward l)
(define (snl-loop lt accum)
(cond [(null? lt)
(reverse accum)]
[(cons? lt)
(let ([head (car lt)]
[tail (cdr lt)])
(cond [(list? head)
(snl-loop tail (cons (snl-loop head '())
accum))]
[(number? head)
(snl-loop tail (cons (* head head) accum))]
[else (error "mutant horror death")]))]
[else (error "mutant death horror")]))
(snl-loop l '()))
If you want to be annoyingly clever and purist you can now notice that the tail-recursive-loop-with-accumulator approach produces results in reverse, so the trivial case of it is, in fact, reverse:
(define (square-nested-list/forward/stupidly-purist l)
(define (rev l)
(define (rev-loop lt a)
(if (null? lt) a (rev-loop (cdr lt) (cons (car lt) a))))
(rev-loop l '()))
(define (snl-loop lt accum)
(cond [(null? lt)
(rev accum)]
[(cons? lt)
(let ([head (car lt)]
[tail (cdr lt)])
(cond [(list? head)
(snl-loop tail (cons (snl-loop head '())
accum))]
[(number? head)
(snl-loop tail (cons (* head head) accum))]
[else (error "mutant horror death")]))]
[else (error "mutant death horror")]))
(snl-loop l '()))
People who do this are generally just trying to score points on the internet though (there are even more stupidly pure approaches for which you get more points).
And here are the results of calling those three functions:
> (define test-data '((1 2 3) (4 5) 6))
> (displayln (square-nested-list/reversed test-data))
(36 (25 16) (9 4 1))
> (displayln (square-nested-list/forward test-data))
((1 4 9) (16 25) 36)
> (displayln (square-nested-list/forward/stupidly-purist test-data))
((1 4 9) (16 25) 36)
Some other approaches
One issue with this 'reverse the result' is that it involves walking the result to reverse it, and also making a copy of it. Once upon a time this was something that was a real problem, because machines had only a tiny amount of memory and were very slow. Indeed, if your lists are enormous it still is a problem. More commonly it is a problem which exists in the minds of people who either, like me, remember machines which were very slow and had only tiny memory, or whose minds have been damaged by languages which encourage you to behave as if you were using such machines ('C programmers know the cost of everything but the value of nothing').
One partial answer to this problem offered by older Lisps is a function which is like reverse but works destructively: it reverses a list in place, destroying the original structure. This function is called nreverse in Common Lisp. If it existed in Scheme it would be called reverse! I suppose.
A more complete answer is to build the list forwards in the first place. You do this by trickery involving keeping a reference to the final cons of the list, and repeatedly replacing its cdr with a new final cons whose car is the object you are collecting. If you want to do this without your code looking horrible you need to use a macro: the one I wrote (for Common Lisp, not Scheme) was called collecting as it collected lists forwards. There are many others. Note that this approach requires mutable conses and also is not clearly efficient in the presence of modern garbage collectors.
Macros like collecting still have their place I think: not because they make your code faster, but because they can make it clearer: if you want collect some results into a list, then do that, don't do this weird reversing thing.
You are almost there.
All you need to do is reverse the return value for each sublist:
(defun sqr-tail (lst)
(labels ((helper (lst res)
(cond ((null lst)
(reverse res))
((listp (car lst))
(helper (cdr lst)
(cons (helper (car lst) ())
res)))
(t (helper (cdr lst)
(cons (expt (car lst) 2) res))))))
(helper lst ())))
(sqr-tail (list 1 2 4 3 (list 1 2 (list 1)) 3 3))
==> (1 4 16 9 (1 4 (1)) 9 9)
or, in scheme:
(define (sqr-tail lst)
(define (helper lst res)
(cond ((null? lst)
(reverse res))
((list? (car lst))
(helper (cdr lst)
(cons (helper (car lst) ())
res)))
(else (helper (cdr lst)
(cons (expt (car lst) 2) res)))))
(helper lst ()))
I am trying to check the first atom in a list against the other atoms in a list, so if the first atom is 3 and the 3rd atom is three, I would want to evaluate it to false.
I have something like
(define (doubles a_list)
(cond
((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))
I have a feeling that this will only check atoms next to each other and not the one atom with the rest of them. what can i do to check all the items with the first one?
You want to repeatedly compare the first two elements of your list, reducing the cdr as you go:
(define (unique-first? x)
; uncomment the following line to trace the execution
;(display x) (newline)
(or (null? x) (null? (cdr x))
(and (not (eq? (car x) (cadr x)))
(unique-first? (cons (car x) (cddr x))))))
note on special cases:
(unique-first? '()) -> #t
(unique-first? '(a)) -> #t
(which, indeed, verify the "first element doesn't appear twice" criteria).
I've got my tea, my code-review hat and a few spare minutes. You know what time it is.
What you want is a predicate that will tell you if there are duplicates of the first atom in a list.
(define (doubles a_list)
(cond ((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))
Regardless of anything else, this won't work because it has unbalanced parentheses.
(define (doubles a_list)
(cond ((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))))
That revised version won't work because it has a malformed second clause in the case. Oddly, it does seem to evaluate fine, but when you call it, you'll get an odd error message
Welcome to Racket v5.3.6.
> (define (doubles a_list)
(cond ((null? a_list) #t)
(not ((eq? (car a_list) (car (cdr a_list)))) (doubles(cdr a_list)))))
> (doubles (list 1 2 3 4 5 6 7 3))
application: not a procedure;
expected a procedure that can be applied to arguments
given: #f
arguments...: [none]
>
The direct cause is this bit
... ((eq? (car a_list) (car (cdr a_list)))) ...
Because of the extra parens surrounding this expression, what it actually means is
"See if the first element of a_list is the same as the second, then call the result of that check as a function".
This is not what you want. The correct resolution is getting that not into these parens, which would make that a valid cond clause.
(define (doubles a_list)
(cond ((null? a_list) #t)
((not (eq? (car a_list) (car (cdr a_list))))
(doubles (cdr a_list)))))
You can't call car on the empty list in Scheme. This works fine, for some definition of "fine", in Common Lisp but will throw you an error here.
> (define (doubles a_list)
(cond ((null? a_list) #t)
((not (eq? (car a_list) (car (cdr a_list))))
(doubles (cdr a_list)))))
> (doubles (list 1 2 3 4 5 6 7 3))
car: contract violation
expected: pair?
given: '()
>
The cause of this error is that you're checking whether a_list is null?, but then calling (car (cdr a_list)) later on. You'll get this error in the situation where a_list is something like (3). The fix for this is checking whether a_list or its cdr are null?
(define (doubles a_list)
(cond ((or (null? a_list) (null? (cdr a_list))) #t)
((not (eq? (car a_list) (car (cdr a_list))))
(doubles (cdr a_list)))))
> (doubles (list 1 2 3 4 5 6 7 3))
#t
Now that we've got a version of your function that doesn't error out, lets take a look at your logic. The procedure for finding doubles in a list is
for lists of length zero or one, the answer is False, since there can be no doubles in a list shorter than two elements
otherwise, check if the first element of the list is in the rest.
if it is, the answer is True since we've found a duplicate
Now, you've named your function doubles, but your prose explanation tells me that it really should have been unique-first?. Because you're not looking for doubles, your're looking to check if the first element of your list is unique among its peers. What you really want to do is
for lists of length one, the answer is True, since that single element must be unique (I'm going to assume that you want the same for lists of length zero, but that might not actually make sense depending on the application)
otherwise, check that the first element of the list is not in the rest.
That translates to
(define (unique-first? a_list)
(if (or (null? a_list) (null? (cdr a_list)))
#t
(not (member? (car a_list) (cdr a_list)))))
The member? function is fairly straightforward, and works on the same principles.
(define (member? elem lst)
(cond ((null? lst) #f)
((eq? elem (car lst)) #t)
(else (member? elem (cdr lst)))))
Finally, a couple of style points. The Scheme convention is to name predicates with a trailing ?, which will hint to your functions' callers that it will return #t or #f (I've done this above), and to use spinal-case rather than snake_case for names.
(define (unique-first? a-list)
(if (or (null? a-list) (null? (cdr a-list)))
#t
(not (member? (car a-list) (cdr a-list)))))
I've got a program I'm writing for a class to substitute the left-most occurrence of a variable with a new variable. (It actually allows you to provide an equivalence relation yourself, as well). The thing is, in Chez Scheme 8.2, this substitutes the right-most occurrence, if the left most is inside a list. We use a server running some version of scheme (I'm not sure which version), and on the server it substitutes, correctly, the left-most occurrence. Below is the code:
(define subst-leftmost
(lambda (new old ls proc)
(let ([keep-going? #t])
(letrec ([helper
(lambda (ls)
(cond [(null? ls) ls]
[(or (pair? (car ls)) (null? (car ls)))
(cons (helper (car ls)) (helper (cdr ls)))]
[(and keep-going? (proc old (car ls)))
(set! keep-going? #f) (cons new (cdr ls))]
[else (cons (car ls) (helper (cdr ls)))]))]) (helper ls))))
This is called like so: (subst-leftmost 'x 'a '(d b c (a) b a) eq?) which should produce the output (d b c (x) b a), and does on the server. In Chez scheme, however, it produces (d b c (a) b x). I think the difference is due to the line
[(or (pair? (car ls)) (null? (car ls)))
(cons (helper (car ls)) (helper (cdr ls)))]
evaluating the helper of the car and the helper of the cdr in a not-set order.
My question is this: Which version of scheme is following the standard, and how can I modify my code so that it works correctly in both versions?
(I've already talked to my professor about this. He's going to address the class about it on Monday, once he can think about it some, but I'm curious. I also already got the full points for the assignment, so don't worry about the ethics of helping me, in that regard.)
There isn't any, sorry. Here's the relevant legalese. Use LETs or LET* if you need to evaluate sub-expressions in a particular order.
Scheme guarantees no specific order (as Cirno has said). If your code has no side-effects, this doesn't matter.
However, your code is side-effecting (because of the set! to an outside variable), so, you have some choices:
Use Racket (which is committed to using left-to-right order, last time I talked to a Racket dev)
Structure your code to remove side-effects, so that your helper function doesn't change any variable or state outside it
Use appropriate lets to ensure the ordering you need (as Cirno suggested); in particular, change (cons (helper (car ls)) (helper (cdr ls))) to:
(let ((depth-first (helper (car ls))))
(cons depth-first (helper (cdr ls))))
I am doing a scheme program which takes in a list and then reverse it. So far it works for simple list, which does not contain any sublist, but when I test it for a list contains sublist, it fails. Please help me where is wrong with it.
Here is the code:
(define deep-reverse
(lambda (L)
(cond
((empty? L) '())
(else (append (deep-reverse (rest L)) (list (first L)))))))
(define (deeprev L)
(if (null? L) '()
(if (list? (car L))
(if (chek (car L)) (append (deeprev (cdr L)) (list (reverse (car L))))
(append (deeprev (cdr L)) (list (deeprev (car L)))))
(append (deeprev (cdr L)) (list (car L))))))
First of all, you are using undefined Scheme functions. I am going to work off the following assumptions:
empty? is null?
rest is cdr
first is car
Your code works by taking the first element in a list and adding it to another list. However, that first element of a list can be a list itself. You need to test to see if the element that you're working on is atomic or a list. If it's a list, then you call deep-reverse recursively.
If you would like to see code appended to this, leave a comment.