Steven Marigliano
Steven Marigliano

Reputation: 21

Pig latin function in SML (Operator and Operand error)

I'm practicing sml using problems from Ullman(M97) second edition. The problem I am currently working on calls for a piglatin function that takes in a word, explodes it, and checks if the first character is a vowel (a, e, i, o u). If it is a vowel, it implodes the character list back into a string and adds "yay" at the end. If the first character is not a vowel, the function then checks the rest of the characters until it comes across the first vowel. When it does, it places all characters that came before the first vowel at the end of the character list, implodes the new character list back into a string and adds "ay" to it.

For example:

- pl "able";
val it = "ableyay" : string

- pl "stripe";
val it = "ipestray" : string

fun isVowel (c::cs) = 
    if c = #"a" then true
    else if c = #"e" then true
    else if c = #"i" then true
    else if c = #"o" then true
    else if c = #"u" then true
    else false

fun cycle nil = nil
  | cycle (h :: hs) = hs @ [h]

fun aL (h::hs) =
    if isVowel(h) = true
    then h :: hs
    else aL (cycle (h :: hs))

fun plx (x) =
    if isVowel x = true
    then (implode x) ^ "yay"
    else implode (aL (x)) ^ "ay"

fun pl (x) =  plx (explode x)

I have most of the problem done, but I am stuck on why my plx function gives me this:

Error: operator and operand don't agree [tycon mismatch]  
operator domain: char list list  
operand:         char list 
in expression: aL x uncaught exception Error

and I am not sure how to fix it.

Upvotes: 2

Views: 81

Answers (2)

sshine
sshine

Reputation: 16135

You may find Exercism's SML track enjoyable then. There's even a Pig Latin exercise. :-)

It is quite common to explode, analyse and implode, but it isn't very efficient, and in some cases it isn't easier either. As molbdnilo pointed out, isVowel should probably accept a char as input instead of a char list:

fun isVowel c =
    c = #"a" orelse
    c = #"e" orelse
    c = #"i" orelse
    c = #"o" orelse
    c = #"u"

For the function that converts a word into pig latin, you can do this entirely with string functions:

fun piglatin (word : string) =
    let val firstLetter = String.sub (word, 0)
    in if isVowel firstLetter
       then word ^ "yay"
       else String.extract (word, 1, NONE) ^ str firstLetter ^ "ay"
    end

Testing this:

- piglatin "pig";
> val it = "igpay" : string

- piglatin "ant";
> val it = "antyay" : string

Now, there are corner cases:

  • What if the word is the empty ""?

    - piglatin "";
    ! Uncaught exception:
    ! Subscript
    
  • What if the word is the uppercased "Ant"?

    - piglatin "Ant";
    > val it = "ntAay" : string
    

Those two problems will need to be addressed to make the string-based piglatin function robust and total.


Here is some feedback for the solution you posted:

  • Don't write if P then true else Q; write P orelse Q.
  • Don't write isVowel c = true; write isVowel c.
  • aL and plx aren't the best function names; I'm not sure exactly what they're supposed to except act as glue between pl and cycle, isVowel, explode and implode.

Upvotes: 0

molbdnilo
molbdnilo

Reputation: 66459

It's because the type of isVowel is char list -> bool.

If you look at aL:

fun aL (h::hs) = if isVowel(h) = true then h :: hs
                 else aL (cycle (h :: hs));

the isVowel(h) means that h must be a char list, and this in turn means that aL must have type char list list -> char list list, and implode (aL x) is an error.

To fix, change isVowel to char -> bool:

fun isVowel c = ...

and write isVowel (hd x) in plx.

Upvotes: 1

Related Questions