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\$

    1 Answer 1

    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\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.