2
\$\begingroup\$

I have to write a program that changes a string's vowels, consonants and other symbols into C, V respectively 0. I've done this but I wonder if there is a more efficient and elegant way to do it. Would appreciate input.

(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)))

Moreover, I would like to make it a string, how could I do that? Should I define a function that would iterate through the list and make it a string or is it an easier way to do it?

I've got it, for the ones interested, the code is below. Used map to do it.

(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 analyze_word (word-string)
(loop for char across word-string collect (letter-type char)))

(defun letter-type (char)
  (cond ((find char "aeiou" :test #'char-equal) #\V)
        ((alpha-char-p char) #\C)
        (t #\0)))

(defun change_string (string)
(map 'string #'letter-type "analyze-word")
)
\$\endgroup\$
2
\$\begingroup\$

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> (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> (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> (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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.