the list inside a list of lisp tutorial - lisp

I am reading Programming in Emacs Lisp
Here is another list, this time with a list inside of it:
'(this list has (a list inside of it))
I am confused with the nested list, why it has not a prefix quoting as
'(this list has '(a list inside of it))
if not has a prefix `, why it not parse the a as a function?

's-expression is an abbreviation for (quote s-expression): anything inside the s-expression is considered a datum and it is not evaluated.
So,
'(this list has (a list inside of it))
is an abbreviation of:
(quote (this list has (a list inside of it)))
that contains the following list:
(this list has (a list inside of it))
which is the value of the entire quote form since it is not evaluated.
It is easy to verify this by writing:
'(this list has '(a list inside of it))
This, if evaluated, will produce as value the following list:
(this list has (quote (a list inside of it)))

That's one of the slight difficulties in Lisp: a list is data and can also be a program. If you want a list to be data in a Lisp program, you need to quote it.
Lists as such: one can read them with READ
(this list has (a list inside of it))
(this list has no list inside of it)
(+ 1 2)
(1 2 +)
(1 + 2)
(quote (this list has (a list inside of it)))
(quote (this list has (quote (a quote list inside of it))))
(quote quote)
Valid Lisp forms: one can evaluate them with EVAL
(+ 1 2)
Evaluates to -> 3
(quote (+ 1 2))
Evaluates to -> (+ 1 2)
(quote (this list has (a list inside of it)))
Evaluates to -> (this list has (a list inside of it))
(quote quote)
Evaluates to -> quote
This is also a valid Lisp form:
(quote (this list has (quote (a quoted list inside of it))))
It evaluates to:
(this list has (quote (a quoted list inside of it)))

Related

Quoting a function name then using it

I'm writing a program to log to various places. In it I have some log functions:
(define (write-to-file destination content)
(with-output-to-file destination
(λ ()
(displayln
content))
#:mode 'text #:exists 'append))
(define (write-to-port destination content)
(displayln content destination))
I want to use these functions at run-time. So I have made a list of lists to hold my configuration:
(define log-destinations
'((write-to-file "my.log")
(write-to-port (current-error-port))))
So I have a function that recursively consumes the list log-destinations:
(define (send-log-content content destinations)
(unless (null? destinations)
(let ([ destination (car destinations)])
(#%app (car destination) (cadr destination) content))
(send-log-content content (cdr destinations))))
However at runtime I'm getting:
application: not a procedure;
expected a procedure that can be applied to arguments
given: 'write-to-file
arguments...:
"my.log"
"[info] web: 2021/03/21 16:26:35 +11:00 /\n"
context...:
Which suggests I've not quoted the function name properly. So how do I quote the function name appropriately in log-destinations so it can be called correctly at runtime?
With the list literal here:
(define log-destinations
'((write-to-file "my.log")
(write-to-port (current-error-port))))
write-to-file and write-to-port are symbols, and (current-error-port) is a quoted list. You need the sublists in log-destinations to contain the procedures write-to-file and write-to-port. Further, you need to actually complete the evaluation of (current-error-port) to get the error port, which is to say that you need to evaluate the arguments within your lists.
Quoting a list to create a list literal means that the arguments will not be evaluated when the list is created. But, you can use the list procedure to create lists while evaluating the arguments:
(define log-destinations
(list (list write-to-file "my.log")
(list write-to-port (current-error-port))))
Alternatively, you could use the backquote syntax, i.e., quasiquotation. Quasiquotation allows you to create a list template with some control over how the list members are evaluated. Use a backquote to quasiquote a list, then place a comma before arguments which should be evaluated:
(define log-destinations
`((,write-to-file "my.log")
(,write-to-port ,(current-error-port))))
It isn't clear to me why you are using #%app; my guess is that you were thinking of (current-error-port) as a list in the procedure application. With the above changes you should be able to change the procedure call to:
((car destination) (cadr destination) content)

How can I check if a list is a list of lists?

I'm trying to check if a parameter before use it in a function with racket. This parameter must be a list of lists.
I have tried:
(andmap (lambda (x) (not (and (list? x) (not (pair? x))))) lst)
with:
(define lst '('(a) '(b) '(c)))
But it fails because (pair? '(a)) is true. With pair? I'm trying to avoid (a . 1) dotted pairs because (list? (a . 1)) is also true.
How can I check if a list is a list of lists and doesn't contains dotter pairs?
Three things:
To check if a list is a list of lists, you can simply write
(define (list-of-lists? v)
(and (list? v) (andmap list? v)))
which first checks if the value is a list, then checks if all its elements are lists.
If you are doing this because a function should only accept a list of lists, and other values are illegal, then you should make things easier for yourself by using Racket’s contract system instead of doing the validation yourself:
(require racket/contract)
(define/contract (f lst-of-lsts)
(-> (listof list?) any/c)
#| do something here |#)
You, like many people exposed to it for the first time, seem to have some confusion about how quote works. You probably mean to write '((a) (b) (c)), not '('(a) '(b) '(c)). Please read What is the difference between quote and list?, or just use the list function.

LISP appending item to array from a for loop

I'm trying to add an item to an array if this item is not already in the array. Essentially what I want to achieve is to iterate through a list remove any duplicates by checking if they already exist.
Here's how I've started:
(defun reduce(mylist)
(setq newList (list ()) )
(loop for item in mylist
do (
(if (not (member item newList))
)
))
)
(reduce '(3 4 5 4 5))
The error I'm getting is (IF (NOT (MEMBER 'ITEM 'NEWLIST))) should be a lambda expression. I know this is something to do with how item and newList are being accessed and can't figure out how to correct it.
The error is caused by wrapping the body of the do form with parentheses. Parens have meaning in lisp, so 'extra' wrapping like this will break the code.
There are some other mistakes. setq is used to assign to an unbound variable. You should use let to establish a binding instead. The initial value of that variable is a one-length list containing (), while it should probably just be ().
reduce is already a Common Lisp function, so a difference choice of name would be a good idea.
Finally, the formatting isn't either idiomatic or consistent - you have mylist and newList. A lisp programmer would spell these names my-list and new-list.
Here's how it might look when cleaned up a bit. I've left some important parts out for you to fill in.
(defun unique-elements (list)
(let ((result ()))
(loop for item in list
do (when (not (member item result))
...))
...))
(It would be cleaner to use loop's collection machinery for this job, but I decided an example of how to use do would be more helpful.)

'cdadr' on nested data list in lisp

While studying cons, cdr and car to handle lists I tried following :
(cadr '('(1) '(2)))
'(2)
which gives the second item in the list as expected.
Whereas following gives :
(cdadr '('(1) '(2)))
((2))
How is data being concerted to code and still not giving error?
How was this evaluated?
cdr on '(2) should give nil, which is does. Why not above?
[I am new to both clisp and stackoverflow so pardon me.]
quote is a special operator which returns its single unevaluated argument. The form (quote ...) can be abbreviated using ' as '.... Since the ' is handled by the reader, the form
'('(1) '(2)))
is actually read the same as
(quote ((quote (1)) (quote (2)))
The outermost application quote to the the argument((quote (1)) (quote (2))) returns that argument. The cadr of that argument is the list
(quote (2))
whose first element is the symbol quote, and whose second element is a list of a single element 2.
Because of quotes. You should write (cadr '((1) (2))).
With your list, (caadr '('(1) '(2))) yields QUOTE.
Actually, your list '('(1) '(2)) is really '((quote (1)) (quote (2))), which may show better why you get this result.

Emacs Lisp: how to avoid inserting a duplicate list item?

How do I check if a string is already in a list in Emacs Lisp? I need to check if a certain path string is already in exec-path, and then add it to that list if it's not.
The function add-to-list will check automatically before adding
(setq a '(1 2 3))
(add-to-list 'a 4)
(add-to-list 'a 3)
will result in a equal to (4 1 2 3)
From Emacs 26 C-h f add-to-list:
(add-to-list LIST-VAR ELEMENT &optional APPEND COMPARE-FN)
Add ELEMENT to the value of LIST-VAR if it isn’t there yet.
The test for presence of ELEMENT is done with ‘equal’, or with
COMPARE-FN if that’s non-nil.
If ELEMENT is added, it is added at the beginning of the list,
unless the optional argument APPEND is non-nil, in which case
ELEMENT is added at the end.
The return value is the new value of LIST-VAR.
This is handy to add some elements to configuration variables,
but please do not abuse it in Elisp code, where you are usually
better off using ‘push’ or ‘cl-pushnew’.
If you want to use ‘add-to-list’ on a variable that is not
defined until a certain package is loaded, you should put the
call to ‘add-to-list’ into a hook function that will be run only
after loading the package. ‘eval-after-load’ provides one way to
do this. In some cases other hooks, such as major mode hooks,
can do the job.
In addition to cobbal's answer about add-to-list, there's also cl-pushnew:
(require 'cl-lib)
(setq list-of-strings '("one" "two" "three"))
(cl-pushnew "two" list-of-strings :test #'string=)
⇒ ("one" "two" "three")
(cl-pushnew "zero" list-of-strings :test #'string=)
⇒ ("zero" "one" "two" "three")
The :test #'string= argument is needed in your case because, cl-pushnew uses eql to compare by default, and it doesn't treat two strings with the same content as equal. (equal) would work too.
(eql "some-string" "some-string")
⇒ nil
(string= "some-string" "some-string")
⇒ t
(equal "some-string" "some-string")
⇒ t
From Emacs 26 C-h f cl-pushnew:
cl-pushnew is a Lisp macro in ‘cl-lib.el’.
(cl-pushnew X PLACE [KEYWORD VALUE]...)
(cl-pushnew X PLACE): insert X at the head of the list if not already there.
Like (push X PLACE), except that the list is unmodified if X is ‘eql’ to
an element already on the list.
Keywords supported: :test :test-not :key