Related
The quasiquoted list `(1 ,#2 3) is invalid because 2 is not a list. However, `(1 2 ,#3) is valid and will return a dotted list: (1 2 . 3). I observe this result in Common Lisp and Scheme. Why is it possible to use unquote-splicing for non-lists at the end of a quasiquoted list? Why is the result a dotted list?
The expression `(1 2 ,#3) is not valid in either Scheme or Common Lisp.
Scheme
In R6RS Scheme (and similarly for R5RS), the behavior is not specified for operating on a non-list with unquote-splicing. The R6RS Scheme Standard requires (11.17 Quasiquotation):
If an (unquote-splicing <expression> ...) form appears inside a <qq
template>, then the <expression>s must evaluate to lists....
Common Lisp
The Common Lisp HyperSpec says first that (2.4.6 Backquote):
If a comma is immediately followed by an at-sign, then the form
following the at-sign is evaluated to produce a list of objects.
These objects are then "spliced" into place in the template.
In the sub-expression ,#3, 3 does not evaluate to a list. This seems a pretty strong argument that the expression is not valid. Even if the 3 were magically placed in a list before splicing, this would not result in a dotted list. The HyperSpec goes on to provide a formal summary of backquote syntax. The part of interest is:
`(x1 x2 x3 ... xn . atom) may be interpreted to mean
(append [ x1] [ x2] [ x3] ... [ xn] (quote atom))
where the brackets are used to indicate a transformation of an xj as follows:
-- [form] is interpreted as (list `form), which contains a backquoted form that must then be further interpreted.
-- [,form] is interpreted as (list form).
-- [,#form] is interpreted as form.
So in Common Lisp the original expression, which is equivalent to `(1 2 ,#3 . nil), can be interpreted as:
(append (list `1) (list `2) 3 (quote nil))
But, this is not a valid call to append, which requires proper lists for all arguments except the last one. So, there seems to be no support for the idea that the original expression was valid.
The fact that it worked for the OP in both Scheme and Common Lisp probably comes down to similar definitions for the backquote macro across different implementations. These definitions all seem to expect that the form following ,# will evaluate to a list; when that is not the case, the observed behavior (production of a dotted list) can't be relied upon, according to the standard. That said, I tested Chez Scheme, Guile Scheme, MIT Scheme, SBCL, CCL, and CLisp: all of them exhibited the same behavior reported by OP.
An Interesting Case
I also tested against an implementation of the backquote macro by Guy Steele and published in CLTL2. This case is more interesting. This backquote implementation in CLTL2 is meant for exploring the behavior of backquote expressions and has an optional code simplification phase. Here $ corresponds to a backquote, and %# corresponds to ,#. Without code simplification, the result of expanding the original expression is:
CL-USER> (setf *bq-simplify* nil)
NIL
CL-USER> (try '("$(1 2 %#3)"))
`(1 2 ,#3) = (APPEND (LIST '1) (LIST '2) 3 'NIL)
This corresponds to the expression which was expected above from reading the description in the HyperSpec. But note that this expression will not compile:
CL-USER> (append (list 1) (list 2) 3 nil)
The value
3
is not of type
LIST
[Condition of type TYPE-ERROR]
But, when code simplification is turned on:
CL-USER> (setf *bq-simplify* t)
T
CL-USER> (try '("$(1 2 %#3)"))
`(1 2 ,#3) = (LIST* '1 '2 3)
This "simplified" expression is valid, and evaluates to a dotted list:
CL-USER> (list* 1 2 3)
(1 2 . 3)
My conclusion is that the expression following ,# must be a list per the Common Lisp standard, but some common implementations either do some form of code simplification similar to what is shown in CLTL2 or otherwise expand the backquote form in such a way that it appears that a non-list form can follow ,#. Don't rely on this, as it is difficult to say when it won't work.
Proper lists
Other answers detail this part already, let me rephrase it quickly: lists are either proper lists or improper lists; improper lists are either ciruclar lists or dotted lists.
In particular, a dotted-list is a non-circular list whose last cons cell has a non-list cdr slot.
Some functions are expected to work only when given proper lists, other have no such restrictions, typically because checking for this property is not without costs.
Writing (cons 1 2) creates such a list. Now, if the splicing syntax expands as (cons x y) where y is a non-list, you'll have a dotted-list too.
Slicing
Chapter 2.4.6 Backquote states (emphasis mine):
If a comma is immediately followed by an at-sign, then the form following the at-sign is evaluated to produce a list of objects.
I see no other note indicating that splicing a non-list value might be part of a conforming program, even if it results in practice to an improper list when splicing occurs at the end of the list.
The section also states that:
`((,a b) ,c ,#d)
will be interpreted as if it were
(append (list (append (list a) (list 'b) 'nil)) (list c) d 'nil)
but it could also be legitimately interpreted to mean any of the following:
(append (list (append (list a) (list 'b))) (list c) d)
(append (list (append (list a) '(b))) (list c) d)
(list* (cons a '(b)) c d)
(list* (cons a (list 'b)) c d)
(append (list (cons a '(b))) (list c) d)
(list* (cons a '(b)) c (copy-list d))
Expanding as calls to standard functions like list* or append make that corner case naturally produce improper lists, but notice that the first and last example do not allow splicing for the last element. For instance:
(list* (cons a '(b)) c (copy-list d))
The above form complains when d is not a list, because COPY-LIST only works with lists (either proper or dotted, but here it would be given a non-list value, e.g. a number).
It is thus my understanding that this expression:
`(1 2 ,#3)
is in fact invalid in Common Lisp and happens to works only by accident. Writing it could expose you to portability problems.
Conclusion
Why is it possible to use unquote-splicing for non-lists at the end of a quasiquoted list?
It happens by chance in your implementation because backquotes, unquotes and splicing are rewritten/expanded as list building functions that may produce dotted lists in that corner case, without signaling an error.
Why is the result a dotted list?
Because the code probably is interpreted as a call to append where the last item is a non-list value, which results in a dotted list. It does not work when splicing in the middle of a form, because append expects proper lists for all but the last arguments.
Bonus
Today I learned about ,. which is like ,# except is may use nconc instead of append
To construct a list of 3 variables with quasiquoting, one could write
`(,x ,y ,z)
This can be desugared to
(cons x (cons y (cons z nil)))
What if z is a list, and we want to splice its contents into the resulting list? Then instead of creating a new cons for it, we simply put it at the tail. When we write
`(,x ,y ,#z)
this desugars to
(cons x (cons y z))
If z happens to not actually be a list, then the result is a legal value, although not a proper list. For example, (1 2 . 3), as you observed. You will see the same result if you write the desugared form explicitly, as (cons 1 (cons 2 3)). If this also troubles you, you may want to investigate the concept of improper lists in general, or dotted pair notation.
'(1 2 3) is actually (cons 1 (cons 2 (cons 3 nil))).
So, the last element in '(1 2 3) is not a 'non-list', but a (cons 3 nil) i.e. (list 3), and thus can be spliced.
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.
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)
I am trying to create a function which has a symbol/token and a list passed in
the function will then check to see if the list contains the symbol/token. if so return its corresponding key pair and if not return nul
I made a defparameter which was:
(defparameter *pairs*
'((apple green)
(banana yellow)
(grape purple)))
i then made a function to do the task:
(defun list-pairs (word list)
(if (eq word list)
(rest(list)))
nil))
the function when executed as such : (list-pairs '(banana) *pairs*) i keep getting nul
what i want to be returned is yellow i.e. the key pair
can someone please help me as to explaining whats wrong. I am very new to LISP
(second (assoc 'banana *pairs*))
gives
YELLOW
Edit: See Joshua Taylor's comment below for documentation about assoc and the concept of association lists (or short: alists) that it works with (very briefly: alists are a key-value data structure formed by lists of cons cells in which the car of each cons cell is the key and the cdr is the value).
About what's wrong with your own code:
In the condition of your if: (eq word list), you are testing whether your argument word is the same as your argument list. But you actually want to look for word within list, so that cannot be the right condition.
Also, even if the condition were true, what you're then evaluating is (rest(list)). That is the rest of a new empty list, created by calling the function list, not the rest of your argument list.
And finally, you're closing the if expression after that (rest(list)) with the third closing parenthesis there. Because of that, the final nil would be the return value of your function even if everything else would be correct. You need to move the nil into the parentheses of the if expression to make it the else-consequent, and the value of the if expression the return value of the function.
What you are looking for is
(defun list-pairs (word list)
(if list
(if (eq (caar list) word)
(cadar list)
(list-pairs word (cdr list))))))
or, using let as a small optimisation:
(defun list-pairs (word list)
(if list
(let ((c (car list)))
(if (eq (car c) word)
(cadr c)
(list-pairs word (cdr list))))))
which works well with a Common Lisp implementation that does tail-call optimisation; you should use it like
(list-pairs 'banana *pairs*)
but in Common Lisp the loop macro is generally preferred:
(defun list-pairs (word list)
(loop for c in list
when (eq (car c) word)
return (cadr c)))
As another alternative:
(defun list-pairs (word list)
(dolist (p list)
(if (eq word (first p))
(return (second p)))))
(defparameter *pairs*
'((apple green)
(banana yellow)
(grape purple)))
(list-pairs 'banana *pairs*)
YELLOW
Your task leaves some questions open to me.
1.) Your function should get a symbol. But why do you call it like this: (list-pairs '(banana) *pairs*)? Wouldn't be (list-pairs 'banana *pairs*) sufficient?
2.) Do you only want to get matches for the first element of your pairs? I.e. Do you want the same result for the symbol banana and yellow?
If you only want to search for the first element, Rörd already mentioned assoc, which would be perfect.
If you want to check both elements of the pair, you can give remove-if-not a try.
(defun list-pairs (word list)
(remove-if-not
#'(lambda (sublist) (member word sublist))
list))
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...