Unexpected behavior of the `find` function in Common-Lisp - search

Searching for nil in a sequence using find always returns NIL.
(find nil #(a b nil c)) -> NIL
(find nil #(a b c)) -> NIL
Same thing if the sequence is a list.
However, member operates as I would expect:
(member nil '(a b nil c)) -> (NIL C)
Why was find designed to operate this way?
Note that position works as I would expect:
(position nil #(a b nil c)) -> 2
(position nil #(a b c)) -> NIL

FIND simply isn't useful if the target element is NIL. It's returning the found element, but it's indistinguishable from the not-found case. If you want to find out of a sequence has a nil, POSITION is a better option. There are many other options.

It returns nil because the result of find if the element is not in the sequence is nil. But even with member, if it is not in the sequence, it will return nil:
CL-USER> (member nil '(a b c))
NIL
CL-USER>
So technically searching for nil in a sequence will return nil with member and position if it isn't there which can be confusing of course.
CL-USER> (position nil '(a b c))
NIL
CL-USER>
The difference with position is that if nil occurs, it will return the index location of course, as you noted. Find was designed that way because it is NOT searching for an index location, but is essentially searching to match the pointer for that symbol, or return nil. That's why using position makes more sense, as you've noted.

Related

Check are there lowercase characters in string

I need return True or False
True if at least one lowercase character
False no lowercase characters
I tried do it with loop and lambda function
Something like this
(defun check-lower-word (word)
(loop
for ch across word
((lambda (c) (if (lower-case-p c) return T) ch)
)
)
I need False if never worked "if"
With a predefined function, you could use some (manual):
CL-USER> (some #'lower-case-p "AbC")
T
CL-USER> (some #'lower-case-p "ABC")
NIL
There is a similar operation for the loop syntax (manual):
CL-USER> (loop for x across "AbC" thereis (lower-case-p x))
T
CL-USER> (loop for x across "ABC" thereis (lower-case-p x))
NIL
Finally, note that loop always returns nil when the iteration terminates without producing a result, so a less concise use of loop could be:
CL-USER> (loop for x across "AbC" if (lower-case-p x) do (return t))
T
CL-USER> (loop for x across "ABC" if (lower-case-p x) do (return t))
NIL
Code errors
You code is not balanced with respect to parentheses, there is a missing closing parenthesis at the end:
(defun check-lower-word (word)
(loop
for ch across word
((lambda (c) (if (lower-case-p c) return T) ch)
)
) ; <-- closes "(loop"
The syntax error in your loop should have raised an error, it does not make sense to write an expression EXPR directly in (loop for c across w EXPR), there should be a preceding do.
The literal ((lambda (c) E) ch) can be directly written as E where every occurence of the variable c is substituted by ch, namely:
(if (lower-case-p ch) return T)
The use of an intermediate literal lambda brings nothing here.
Also, the above reads as: if ch is lowercase, the value of the if is the value bound to the return variable, otherwise it is T. You are indeed missing parens around (return T). A "one-armed" (if T X) is best written as (when T X).
Another approach
You already have an example with some and loop, you can also use a short-circuiting map:
(defun check-lower-word (word)
(block nil
(map ()
(lambda (c)
(when (lower-case-p c)
(return t)))
word)))
A call to MAP with nil as a first argument means the sequence is iterated for effects and returns nil. For each character in the sequence (list or vector), when the character is lower-case, return T. The return exits the iteration early up to the NIL block.
I did it as
(defun check-lower-word (word)
(block outer
(loop
for ch across word do
(if (lower-case-p ch) (return-from outer T))
)
)
)

Iterating over strings

I have two strings of the same length which differ in exactly one character and I want a string of all the characters which are equal. So basically something like this which evaluates to a string instead of a list of characters:
(loop for a across "abcd"
for b across "abce"
when (char= a b) collect a)
Although performance isn't an issue here, I found it cumbersome to have a (coerce ... 'string) around it.
So I came up with something like
(loop with result = ""
for a across "abcd"
for b across "abce"
when (char= a b)
do (setf result (concatenate 'string result (string a)))
finally (return result))
which does the job but looks not very elegant to me.
(map 'string (lambda (a b) (when (char= a b) a)) "abcd" "abce")
looks nicer but is not working because NIL is not a character when aand bare not equal.
Is there a more elegant idiom to iterate over a string and get a string back?
(loop with result = ""
for a across "abcd"
for b across "abce"
when (char= a b)
do (setf result (concatenate 'string result (string a)))
finally (return result))
Repeated concatenate are not a that good idea for longer strings.
Alternatives:
Loop into a list and coercing to a string
CL-USER 3 > (loop for a across "abcd"
and b across "abce"
when (char= a b) collect a into list
finally (return (coerce list 'string)))
"abc"
Using stream and converting it to a string
CL-USER 4 > (with-output-to-string (*standard-output*)
(loop for a across "abcd"
and b across "abce"
when (char= a b) do (write-char a)))
"abc"
Using an adjustable string
CL-USER 5 > (loop with string = (make-array 0
:element-type 'character
:adjustable t
:fill-pointer 0)
for a across "abcd"
for b across "abce"
when (char= a b) do (vector-push-extend a string)
finally (return string))
"abc"
Another possibility is to use mismatch as in the comment of David Hodge:
CL-USER> (defun f(a b)
(let ((pos (mismatch a b)))
(concatenate 'string (subseq a 0 pos) (subseq a (1+ pos)))))
F
CL-USER> (f "abcdefg" "abcxefg")
"abcefg"
Use map to loop over multiple lists simultaneously
(map 'string #'(lambda (a b) (if (char= a b) a #\Rubout)) "abce" "abcd")
'string coerces resulting list into a string. #\Rubout get's coerced to a zero-length string. #\Backspace would even delete the last character.
Another way (it does not assume anything about elements position)
(defun only-matching (seq1 seq2)
(remove-if-not (lambda (c) (find c seq1)) seq2))
CL-USER> (only-matching "abcd" "abce")
"abc"
CL-USER> (only-matching "abdc" "abec")
"abc"`
or
(coerce
(intersection (coerce "abdc" 'list)
(coerce "abec" 'list))
'string)
which does not preserve order also
Note: remove-if-not is deprecated 'officially'.

extracting a n element from a list with dolist on lisp

I am trying to create a function where it takes a list of letters as parameter and a single letter as a parameter. I want to remove the single letter from the list.
(defun extract-all (lett li)
(let ((new-list nil))
(dolist (letter li new-list)
(if (eql lett letter)
(setf new-list (cons nil new-list))
(setf new-list (cons letter new-list))))))
so if I call the function with (extract-all 'n '(i n t e l l)), I want it to return i t e l l with the n removed.
First of all, you are not removing letters
(characters), but rather
symbols:
(type-of 'n)
==> SYMBOL
Second, there is a standard function remove to do just that:
(remove 'n '(i n t e l l))
==> (I T E L L)
Third, if you remove your "then" clause and reverse the result, you will get what you want:
(defun my-remove (object list)
(let ((new-list nil))
(dolist (element list (nreverse new-list))
(unless (eql object element)
(push element new-list)))))
(my-remove 'n '(i n t e l l))
==> (I T E L L)
Note that there are more ways to skin the cat, e.g.,
(defun my-remove (object list)
(loop for element in list
unless (eql object element)
collect element))
However, it is always best to use the library.
See also Where to learn how to practically use Common Lisp.

How to get the last element of a list and return nil if the element isn't in the list

I would like to get the position of any element in the list and get nil if the element isn't in the list. I did:
(defun myposition (letter list)
(cond
((atom list) nil)
((equal (car list) letter) 0)
((null (car list)) (myposition letter))
(t (1+ (myposition letter (cdr list)))) ) )
(myposition 'k '(g h i j k l)
4
(myposition 'p '(g h i j k l)
nil is not a number
When I replace ((atom list) nil) par ((atom list) 0), I get 6 instead nil
(myposition 'p '(g h i j k l)
6
In your first example, your function will recursively compute 1+1+1+1+0 = 4 to find the correct result.
In your second example, it will run through the whole list, add 1 per (non-matching) element, and finally add nil. So it actually computes 1+1+1+1+1+1+nil, which is incorrect since nil is not a number, hence the error message. If you replace nil by zero, it computes 1+1+1+1+1+1+0 which is wrong.
So your basic problem is that you recursively add 1 and, reaching the end of the list, you would like to throw away what you computed until then. But you have an addition pending which you cannot escape.
The easiest way is to change from a recursive to a tail-recursive solution, which is technically a plain goto. Here the addition is done by incrementing a variable, not by unwinding the call stack, which makes it easy to throw away the result from the previous additions and just return nil because there is no addition pending.
A (tail-)recursive solution could be:
(defun myposition (letter lst)
(labels ((sub (lst pos)
(cond
((null lst) nil)
((equal (car lst) letter) pos)
(t (sub (cdr lst) (1+ pos))))))
(if (atom lst) nil (sub lst 0))))
This will work in Common Lisp, but technically, if your implementation does no tail call optimisation, it might still blow the stack for large lists. That's why Common Lisp prefers iterative solutions, such as using the loop macro:
(defun myposition (letter lst)
(when (consp lst)
(loop for c in lst for i from 0
when (equal c letter) return i)))

Iterative version of Binary Tree Search

Basic Tree-search algorithm for searching a node (with value k) in a binary search tree.
'x' denotes the node of the binary search tree.
TREE-SEARCH (x, k)
if x= NIL or k = key[x]
then return x
if k < key[x]
then return TREE-SEARCH(left[x], k)
else return TREE-SEARCH(right[x], k)
Iterative Version:
ITERATIVE-TREE-SEARCH(x, k)
while x ≠ NIL and k ≠ key[x]
do if k < key[x]
then x ← left[x]
else x ← right[x]
return x
Shouldn't the first line (of the iterative algorithm) actually be while (x ≠ NIL OR k ≠ key[x]) instead of (while x ≠ NIL and k ≠ key[x]) ?
By the way, if you were wondering, this is from one of the famous books of Algorithm Analysis.
No, it needs to be and because otherwise you'll dereference NIL if k isn't found. Remember that while executes as long as the expression evaluates to true.
while x ≠ NIL and k ≠ key[x]
If x is NIL, then the expression x ≠ NIL and k ≠ key[x] is false, because x ≠ NIL is false. Either side of an and being false makes the whole expression false.
If or were used instead, x ≠ NIL would still be false, but you'd need to evaluate the other side — both sides of an or must be false for the or to be false. Unfortunately, evaluating the other side dereferences NIL. Ooops. Even if it weren't for that problem, k ≠ key[x] is true (because we're considering the case where k isn't found, so no key in the tree x can be k). Since one (or more) sides of the or is true, the or evaluates to true, and the loop continues forever.
In English, that while can read: while there is still tree left (x ≠ NIL) and we haven't yet found what we're looking for (k ≠ key[x]).

Resources