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.
Related
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'm having trouble understanding how Scheme forms and then detects lists. Hows does it determine the difference between a list and a dotted pair?
A list structure is formed from pairs. A proper list is a chain of pairs where the last pair has the empty list as its cdr.. We can make list? like this:
(define (list? lst)
(cond ((null? lst) #t)
((pair? lst) (list? (cdr lst)))
(else #f)))
Or you could use and/or:
(define (list? lst)
(or (null? lst)
(and (pair? lst)
(list? (cdr lst)))))
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 have been working in DrRacket, trying to create a "prefix" function (#lang racket). It should take two lists as an input, and should output #t if pf is either null, or is equal to the beginning of ls.
My problem is that my code doesn't seem to return anything at all when pf is not a prefix of ls and ls isn't null. If I replace the #f in the if statement with something else, like '(), it will return that properly, but if I try to capture the '() and give an output based on that instead, it gives results that don't make sense (like saying that '() isn't null, or that '() doesn't equal '()). It seems to have something to do with having an if statement within a cond statement. Could anyone tell me what it's doing, or why? Is it possible to make this code work correctly, or am I going to need to rework it another way?
Thanks for the help!
(define prefix
(lambda (pf ls)
(cond
[(null? pf) #t]
[(null? ls) #f]
[(if (equal? (car pf) (car ls)) (prefix (cdr pf) (cdr ls)) #f)])
))
Having an if within a cond condition is usually a sign of doing something wrong. I think you meant to say this:
(define prefix
(lambda (pf ls)
(cond
[(null? pf) #t]
[(null? ls) #f]
[(equal? (car pf) (car ls)) (prefix (cdr pf) (cdr ls))]
[else #f])))