Car and Cdr in Scheme - list

So I've been learn Scheme for school, and have run into a situation using car and cdr series that doesn't quite make sense to me.
So given a list: (define x '(1 2 3 4 5))
How come (caddddr x) spits an error at me, while (cddddr x) returns (5) and (car (cddddr x)) returns 5.
Isn't (caddddr x) the same as (car (cddddr x))?

You can only put a few a's andd's in there :-) check the documentation, between the initial c and the final r there can be between 1 and 4 characters in any combination of a's and d's. If you need to access a specific element beyond that, consider using list-ref, which returns an element given its zero-based index on the list, for example:
(define x '(1 2 3 4 5))
(list-ref x 4)
=> 5

Because the scheme definition goes up to (cddddr pair)‌‌ but not beyond. In the words of the specification for car and cdr and friends: "Arbitrary compositions, up to four deep, are provided." See (for example):
http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_idx_620
And as has been noted elsewhere, list-ref is probably what you want in this case.

Related

How to insert an element in lisp?

I want to insert an element next to the last element of the list, but I only know how to insert an element next to the first element of the list, can someone help me further?
Example : (insert '5 '(1 3 2 7 8 9)) should output => (1 3 2 7 8 5 9)
(defun insert (item list)
(cons (first list)
(cons item
(rest list))))
//when I do (insert '5 '(1 3 2 7 8 9)) I get (1 5 3 2 7 8 9)
Lists are singly linked lists so only add to the front is possible without having to copy parts of the list. your attempt makes a copy of the first pair, the add your pair as the second element then share the rest of the original list with the argument.
In order to add as the last element you need to do this until list is empty and then return a list with the one element. None of the cons will be in common. So:
(insert 'x '()) ; ==> (list 'x)
(insert 'x '(1)) ; ==> (cons '1 (insert 'x (cdr '(1))))
Of course this can be done with append. Laziness is rewarded in programming:
(defun insert-last (item list)
(append list (list item)))
Know that if you have a recursive function or an iteration where you add like this for many elements depending on the arguments you are making a very bad algorithm. It is much better to either use loop to collect to the end or to make the list backwards and in the last step reverse it. Then you'll get n or 2n passes through the data instead of n^2.

LISP How to remove element at certain index from a list

I'm trying to learn LISP. I got my way around functions and I wanted to test myself with some.
I was trying to write a function that can remove an element from a list in a given index.
This seems pretty straightforward, but I can't manage to do it.
Example: I have the list (20 8 13 10) and I want to remove the number at index 2.
How would I go about something like this?
It's very easy. This is the base case:
(remove-nth 0 '(2 3)) ; => (3)
And the default case:
(remove-nth 1 '(1 2 3)) ; ==
(cons 1 (remove-nth 0 '(2 3)))
The only thing left for you to do is to actually implement it!
There is a third case. What if the list is nil? In the strictest sense you cannot do the job and you should signal an error or perhaps there isn't anything to do so it's ok to then have it as a base case that evaluates to '() so that (remove-nth 5 '(1 2)) ; ==> (1 2)

How to use loops in LISP

I have been trying to understand how to use loops in LISP and they still don't really seem to work correctly. I tried using the following code:
(loop for i from 0 to (list-length y)
(when (eq (values-list (nth i (car y))) 0)
(return-from checkZero t)))
Which should loop through my list checking if my value is equal to 0 or not. If it is equal then it should return from and exit the loop, otherwise it should run until it reaches the list length. Am I thinking about this wrong and if so how do I go about fixing this loop?
(I'm not sure if my actual code works or not yet since I am still dealing with the errors generated by the incorrectly used loop and I can't find many good resources for using loops online)
The main problem in the loop is the WHEN-expression. There are two ways you can write that:
Use the loop WHEN condition DO forms-clause:
(loop for...
when (eq ...) do (return-from ...))
Use the regular WHEN-macro inside a loop DO-clause:
(loop for...
do (when (eq ...)
(return-from ...)))
There are a few other things to fix in your code.
When naming things in Lisp, use dashes between words rather than camelCase (check-zero rather than checkZero).
Use = for general numeric comparison, or ZEROP to check that a number is zero. EQ is used to check if two objects are the same object.
You can return from a loop using RETURN
I'm not quite sure what you're trying to accomplish with the (VALUES-LIST (NTH ... (CAR ...))), but it's not going to work. If you're trying to simply loop over a flat list of values (such as (1 2 3 4 5 6)), you should be using the loop FOR item IN list-clause.
So now you should have something like:
(defun check-zero (list)
(loop for item in list
when (zerop item) do (return t)))
LOOP also has a THEREIS condition-clause that you could use:
(defun check-zero (list)
(loop for item in list
thereis (zerop item)))
This return as soon as it finds an item that satisfies ZEROP. However, there are easier ways to achieve the same. You could use MEMBER to check if the list contains a zero:
(defun check-zero (list)
(member 0 list :test #'=))
CL-USER> (check-zero '(1 3 4 3 5 7))
NIL
CL-USER> (check-zero '(1 3 4 3 0 5 7))
(0 5 7)
This returns a generalized boolean. That is, any value that is not NIL is considered true in Common Lisp.
Since there is a predicate function (ZEROP) to check if an object is a zero, you could also use SOME or MEMBER-IF for this:
(some #'zerop '(1 3 4 6 2 0 45 6 7)) ;=> T
(member-if #'zerop '(1 3 4 6 2 0 45 6 7)) ;=> (0 45 6 7)

Appending Elements to an Existing List in Scheme

I need some help understanding the syntax on how to append a number into a list, i'm doing this from user input via the console so this elements have to be entered in recursively. So for ever number that is entered, the list must grow for each element (only numbers) added.
Here is the code I am working with, the problem lies in the second conditional. Right now this works but only creates an empty list of each number I put in, so the results would be
>12
>202
>30
()()()
zero input: stopping list
(define (inputlist)
(let ((applist list))
(let ((inpt (read)))
(cond
((= inpt 0)(newline) (display "zero input: stopping list"))
;;OLD((number? inpt) (cons inpt applist) (display (applist))(inputlist))
((number? inpt) (append (applist)(list inpt)) (display (applist))(inputlist))
(else
display "Not a number")))))
I understand why cons is not doing what I need it to be doing, but is there a similar functionality to appending each read in element to a pre-existing list?
EDIT: I've gotten closer what i've needed to do but still with the same results, i am now appending upon my applist with a list i create via every input, though it is still resulting in as many empty lists as I input.
SECOND EDIT: I've realized why it's printing multiple ()'s is because it's being called off the stack when 0 is entered, so i'm sure it's not working because the appending isn't working as intended, i've displayed the applist on the 0 conditional and it returns one null list.
A simple way to append an element to the end of a list while looping would be to do call append and update the reference to the list afterwards:
(set! applist (append applist (list inpt)))
Notice that you have several misplaced parentheses - in your code some are missing, some are unnecessary. In Scheme () means function application, and you have to be careful where you put those brackets.
Also, be aware that append doesn't modify the initial list, it creates a new one, and if you need to refer to it, you have to store it somewhere (that's why I'm doing a set! above).
There are more serious errors with you logic. The conditions are in the wrong order (you have to test if the input is a number before asking if it's zero), and you forgot to loop if something other than a number is entered. Also, if we pass along the list as a parameter to the loop, we won't have to do an ugly set!. Try this instead, it's closer to what you were aiming for:
(define (inputlist)
(let loop ((applist '()))
(let ((inpt (read)))
(cond ((not (number? inpt))
(display "not a number")
(newline)
(loop applist))
((zero? inpt)
(display "zero input: stopping list"))
(else
(let ((new-applist (append applist (list inpt))))
(display new-applist)
(newline)
(loop new-applist)))))))
As mentioned in the comments, bear in mind that appending at the end of a list inside a loop in general is a bad idea. It's ok for learning purposes, but in real-life code, you'd cons at the head of the list and reverse it at the end - this is more efficient.
Note that (cons x xs) where x is an element and xs is a list produces a new list which has x as its first element.
Here is one way to use cons to add an element in the end of a list:
Example:
Add 4 to (1 2 3)
1. Reverse the list: (3 2 1)
2. Add 4 to the front: (4 3 2 1)
3. Reverse: (1 2 3 4)
> (reverse (cons 4 (reverse (list 1 2 3)))
(1 2 3 4)
A function that uses this principle:
(define (cons-to-back x xs)
(reverse (cons x (reverse xs))))
> (cons-to-back 4 (list 1 2 3))
(1 2 3 4)
An alternative is to use append which appends the elements of two lists:
> (append '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
All we need to do, is to the the element into a list before using append:
> (append '(1 2 3) (list 4))
'(1 2 3 4)
Alternative definition of cons-to-back:
(define (cons-to-back x xs)
(append xs (list x)))

Copy of a list (or something else) in Scheme

I'm newbie in Scheme and find out that if I change a list with set-car!/set-cdr! (even locally) the parent list is modified too. This is an example what I mean:
(define my-list '(1 2 3 4 5)) ; Original list
(define (function list) ; Some example function
(let ((copy list))
(set-car! copy 'new)
(display copy)
)
(function my-list); will display (new 2 3 4 5)
my-list; but the original is changed "forever" and will be also '(new 2 3 4 5)
My question is:
Is there a way to make a copy of the original list and work only on it, so at the end the original not to be changed?
Your code (let ((copy list)) allows list to be accessed through the name copy. So when you set-car! copy, you are actually set-car!'ing the original list.
The phenomenon of mutation in lisp like languages is a little confusing at first.
Mutation is to be avoided usually, for the reasons you have discovered. Parts of lists and things a floating around. This is because internally each node in a list has two parts - the car is it's value which may point to another list, and it's cdr is the part that follows it - usually this is a lit of things similar to the value in car.
This solves your problem using SRFI1's list-copy function
(let ((copy (list-copy list)))
Here is one way to copy a list (make a new list). This code is not really complete. When we look at it internally it adds pieces of a list in each recursive call. Where do the pieces of each new list-section come from? The cdr is generated with the call (list-copy (cdr list)), while the car is simply taken - not copied! - from the other list.
(define (list-copy list)
(if (null? list) '() (cons (car list) (list-copy (cdr list)))))
The result for you when experimenting, was that the car's of your list copy where borrowed from my-list.
Here is a more proper version:
(define (full-copy list)
(if (null? list)
'()
(if (list? list)
(cons (full-copy (car list)) (full-copy (cdr list)))
list)))
Only the code for the car has changed. Now, the car is reconstructed to. The only parts that are borrowed are the numbers (so this copy is not okay when the numbers are instead something more special). I'm not sure how the srfi1 version works.
The let statement only ever binds one identifier to an object - the object is not copied, you simply have another way to access it. So any changes (use of mutation, as in functions that end in "!") will affect both.
I suggest that when you're learning Scheme or Racket, you avoid all the ! functions such as set-car!, set-cdr!, and set!.
Instead of mutating things, try to think in terms of creating a new thing from the old thing(s).
For instance if you have a list and want something new at the start:
;; Return a new list with `x` replacing the head of the list `xs`.
(define (replace-head xs x) ; list? any? -> list?
(cons x (cdr xs)))
;; Using it:
(replace-head '(1 2 3 4 5) 100)
; => '(100 2 3 4 5)
p.s. A version of replace-head that can handle xs being empty could be:
(define (replace-head xs x) ; list? any? -> list?
(cond [(empty? xs) (list x)]
[else (cons x (cdr xs))]))
(replace-head '(1 2 3 4 5) 100)
; => '(100 2 3 4 5)
(replace-head '() 100)
; => '(100)
You also might find it helpful to look at How to Design Programs, for examples of good ways to design and think in Scheme or Racket.