2
\$\begingroup\$

I've been recently fooling around with Go(lang) and ended up doing a (very) simple pig Latin parser. I'm pretty sure this is not the best solution out there (no punctuation and only lowercase letters) but it does its job.

However, I'd be delighted to learn how this could be improved, as I'm still pretty new to Go.

Here's the code and at the bottom some test cases.

package igpay

import "strings"

const pigTail = "ay"

func PigIt(input string) string {
    if len(input) == 0 {
        return ""
    }

    words := strings.Fields(strings.ToLower(input))
    result := make([]string, len(words))
    for _, word := range words {
        if len(word) < 3 {
            result = append(result, piggify(word, 1))
            continue
        }
        switch word[:3] {
        case "squ", "sch", "thr":
            result = append(result, piggify(word, 3))
            continue
        }
        switch word[:2] {
        case "ch", "qu", "th", "rh":
            result = append(result, piggify(word, 2))
            continue
        case "ye", "xe":
            result = append(result, piggify(word, 1))
            continue
        }
        switch word[:1] {
        case "a", "e", "o", "u", "i", "y", "x":
        result = append(result, word+pigTail)
        continue
        }
        result = append(result, piggify(word, 1))
    }
    return strings.TrimLeft(strings.Join(result, " "), " ")
}

func piggify(word string, index int) string {
    return word[index:]+word[:index]+pigTail
}

Test cases:

package igpay

var testCases = []struct {
    description string
    input       string
    expected    string
}{
    {
        description: "word beginning with a",
        input:       "apple",
        expected:    "appleay",
    },
    {
        description: "word beginning with e",
        input:       "ear",
        expected:    "earay",
    },
    {
        description: "word beginning with i",
        input:       "igloo",
        expected:    "iglooay",
    },
    {
        description: "word beginning with o",
        input:       "object",
        expected:    "objectay",
    },
    {
        description: "word beginning with u",
        input:       "under",
        expected:    "underay",
    },
    {
        description: "word beginning with a vowel and followed by a qu",
        input:       "equal",
        expected:    "equalay",
    },
    {
        description: "word beginning with p",
        input:       "pig",
        expected:    "igpay",
    },
    {
        description: "word beginning with k",
        input:       "koala",
        expected:    "oalakay",
    },
    {
        description: "word beginning with x",
        input:       "xenon",
        expected:    "enonxay",
    },
    {
        description: "word beginning with q without a following u",
        input:       "qat",
        expected:    "atqay",
    },
    {
        description: "word beginning with ch",
        input:       "chair",
        expected:    "airchay",
    },
    {
        description: "word beginning with qu",
        input:       "queen",
        expected:    "eenquay",
    },
    {
        description: "word beginning with qu and a preceding consonant",
        input:       "square",
        expected:    "aresquay",
    },
    {
        description: "word beginning with th",
        input:       "therapy",
        expected:    "erapythay",
    },
    {
        description: "word beginning with thr",
        input:       "thrush",
        expected:    "ushthray",
    },
    {
        description: "word beginning with sch",
        input:       "school",
        expected:    "oolschay",
    },
    {
        description: "word beginning with yt",
        input:       "yttria",
        expected:    "yttriaay",
    },
    {
        description: "word beginning with xr",
        input:       "xray",
        expected:    "xrayay",
    },
    {
        description: "y is treated like a consonant at the beginning of a word",
        input:       "yellow",
        expected:    "ellowyay",
    },
    {
        description: "y is treated like a vowel at the end of a consonant cluster",
        input:       "rhythm",
        expected:    "ythmrhay",
    },
    {
        description: "y as second letter in two letter word",
        input:       "my",
        expected:    "ymay",
    },
    {
        description: "a whole phrase",
        input:       "quick fast run",
        expected:    "ickquay astfay unray",
    },
}

Test:

package igpay

import (
    "fmt"
    "testing"
)

func TestPigLatin(t *testing.T) {
    for _, test := range test cases {
        if pl := PigIt(test.input); pl != test.expected {
            t.Fatalf("FAIL: Sentence(%q) = %q, want %q.", test.input, pl, test.expected)
        }
        fmt.Printf("PASS: %s\n", test.description)
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Your definition of consonant clusters is a bit ad-hoc and confusing. For instance, why is thr treated as a consonant cluster but not tr, str, chr, etc.? \$\endgroup\$ – trentcl Mar 4 '20 at 14:18
3
\$\begingroup\$

UPDATE: Second Review

In my first review I said that your code may not be correct because you did not provide a pig latin specification, the algorithm used looked peculiar. I asked the you to post your pig latin specification. You did not do so. I found your pig latin specification anyway. Your code is not correct.

The pig latin specification rules say that consonant sounds can be made up of a consonant cluster. Your code does not identify consonant clusters. Instead, you hard-code consonant clusters which occur in the test suite as string literals, for example, "thr", and so on.

For example, if you add this test to the test suite, it will fail.

{
    description: "spl consonant cluster",
    input:       "split",
    expected:    "itsplay",
},

First Review


This is a real-world code review: Code should be correct, maintainable, robust, reasonably efficient, and, most importantly, readable.


Your code fails the correctness test.

Pig (hog) latin is not well-defined. There are several variations. For example, Pig Latin - Wikipedia. Without a link to the pig latin rules that you used for your code, we have no way to verify that your interpretation and code are correct.

Post a link in your question to the pig latin rules that you used for your code.


Writing tests is pointless if you don't run them. Sentence is undefined.

if pl := Sentence(test.input); pl != test.expected {

igpay$ go test
./igpay_test.go: undefined: Sentence
igpay$ 

You write:

result := make([]string, len(words))

which is equivalent to

result := make([]string, len(words, len(words))

Then you execute statements like

result = append(result, piggify(word, 1))

Then you ignore the bug with strings.TrimLeft()

return strings.TrimLeft(strings.Join(result, " "), " ")

Don't ignore bugs; fix bugs.

Write

result := make([]string, 0, len(words))

and

return strings.Join(result, " ")

Read the relevant documentation:

The Go Programming Language Specification :

Making slices, maps and channels

Appending to and copying slices

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the review. A bit painful, but useful. Although, I know nothing about real-world programming I sure get the feeling now. I will work on the code more. \$\endgroup\$ – baduker Mar 5 '20 at 7:53

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.