LISP - Modify string
First, proper indentation helps a lot. I do appreciate you not putting the closing parens on separate lines though. Thank you.

(defun string-to-list (string)
  (loop for char across string collect char))

(defun is-vowel (char) (find char "aeiou" :test #'char-equal))

(defun is-consonant (char) (find char "bcdfghjklmnpqrstvwxyz" :test #'char-equal))

(defun letter-type (char)
  (if (is-vowel char) "V"
      (if (is-consonant char) "C"
      "0")))

(defun analyze-word (word-string)
  (loop for char across word-string collect (letter-type char)))




We're talking about Common Lisp, so there's no call to go defining your own string-to-list, especially if you're doing it in terms of loop. You can do the same thing with a coerce call.

CL-USER&gt; (coerce "an example" 'list)
(#\a #\n #\  #\e #\x #\a #\m #\p #\l #\e)


Though now that I look at it, you don't call string-to-list anywhere anyway, so we may as well just remove the definition.

(defun is-vowel (char) (find char "aeiou" :test #'char-equal))

(defun is-consonant (char) (find char "bcdfghjklmnpqrstvwxyz" :test #'char-equal))

(defun letter-type (char)
  (if (is-vowel char) "V"
      (if (is-consonant char) "C"
      "0")))

(defun analyze-word (word-string)
  (loop for char across word-string collect (letter-type char)))




Common Lisp convention is to use a suffix of p or -p for predicates, rather than an is prefix.

(defun vowel-p (char) (find char "aeiou" :test #'char-equal))

(defun consonant-p (char) (find char "bcdfghjklmnpqrstvwxyz" :test #'char-equal))

(defun letter-type (char)
  (if (vowel-p char) "V"
      (if (consonant-p char) "C"
      "0")))




You can use cond rather than chaining ifs. Typically, this makes the intent clearer.

(defun letter-type (char)
  (cond ((vowel-p char) "V")
        ((consonant-p char) "C")
        (t "O")))




If you want to make the result of analyze-word a string, you'll need to return a character rather than a string from letter-type

(defun letter-type (char)
  (cond ((vowel-p char) #\V)
        ((consonant-p char) #\C)
        (t #\O)))


At that point, you can coerce its result.

CL-USER&gt; (coerce (analyze-word "supercalifragilistiwhateverthefuck") 'string)
"CVCVCCVCVCCVCVCVCCVCCVCVCVCCCVCVCC"




Since your function is taking a sequence of characters, and returning a sequence of characters in the same form, it's actually easier to define analyze-word in terms of map than in terms of loop (in my experience, this is atypical but still worth looking out for).

(defun analyze-word (word-string)
  (map 'string #'letter-type word-string))


That will take "the function named letter-type", apply it to each element of word-string and return the resulting string. This also saves you from having to coerce its result after the fact.

CL-USER&gt; (analyze-word "supercalifragilistiwhateverthefuck")
"CVCVCCVCVCCVCVCVCCVCCVCVCVCCCVCVCC"


Take this last point with a grain of salt though, because if you ever need to re-write analyze-word to return something like (list :consonants 21 :vowels 13), you'll be right back to loop.