Trying to get better in Lisp, I came across the following problem:
(defun countVowels (string)
(setf vowels (list 'a 0 'e 0 'i 0 'o 0 'u 0))
(loop for ch across string
when (member ch vowels)
do (incf (getf vowels ch)))
(format t "~{~a~^, ~}" vowels))
In my opinion, this counts every vowel by incrementing the plist. However, when I invoke the function with e.g.
(countVowels "test")
the output is
A, 0, E, 0, I, 0, O, 0, U, 0
The code is incorrect. You're searching for characters in a list of symbols. To get it minimally working, you'll need to initialize vowels
as
(setf vowels (list #\a 0 #\e 0 #\i 0 #\o 0 #\u 0))
However, it still has some issues. setf
is used to assign to existing variables, not create new ones. While it will fall back to creating variables, SBCL issues a warning for this. We should be using let
instead, or simply a with
block inside the loop
macro.
Additionally, the naming convention is just flat lying to you. Common Lisp is case-insensitive, so there's no difference between countVowels
and COUNTVOWELS
, or even CoUnTvOwElS
. We usually tend to stick to one case in Common Lisp, just to keep things consistent.
Finally, there's no compelling reason to print inside the function. That just takes power away from the caller. Return the plist instead and let the caller decide what to do with it. If you don't want to rely heavily on the loop
macro, you can write
(defun count-vowels (string)
(let ((vowels (list #\a 0 #\e 0 #\i 0 #\o 0 #\u 0)))
(loop for ch across string
when (getf vowels ch)
do (incf (getf vowels ch)))
vowels))
(format t "~{~a~^, ~}" (count-vowels "test"))
or you can go all-in on loop
and write
(defun count-vowels (string)
(loop with vowels = (list #\a 0 #\e 0 #\i 0 #\o 0 #\u 0)
for ch across string
when (getf vowels ch)
do (incf (getf vowels ch))
finally (return vowels)))
(format t "~{~a~^, ~}" (count-vowels "test"))