Avatar of paulfioravanti

paulfioravanti's solution

to Pig Latin in the Elixir Track

Published at Aug 19 2019 · 0 comments
Instructions
Test suite
Solution

Implement a program that translates from English to Pig Latin.

Pig Latin is a made-up children's language that's intended to be confusing. It obeys a few simple rules (below), but when it's spoken quickly it's really difficult for non-children (and non-native speakers) to understand.

  • Rule 1: If a word begins with a vowel sound, add an "ay" sound to the end of the word. Please note that "xr" and "yt" at the beginning of a word make vowel sounds (e.g. "xray" -> "xrayay", "yttria" -> "yttriaay").
  • Rule 2: If a word begins with a consonant sound, move it to the end of the word and then add an "ay" sound to the end of the word. Consonant sounds can be made up of multiple consonants, a.k.a. a consonant cluster (e.g. "chair" -> "airchay").
  • Rule 3: If a word starts with a consonant sound followed by "qu", move it to the end of the word, and then add an "ay" sound to the end of the word (e.g. "square" -> "aresquay").
  • Rule 4: If a word contains a "y" after a consonant cluster or as the second letter in a two letter word it makes a vowel sound (e.g. "rhythm" -> "ythmrhay", "my" -> "ymay").

There are a few more rules for edge cases, and there are regional variants too.

See http://en.wikipedia.org/wiki/Pig_latin for more details.

Running tests

Execute the tests with:

$ mix test

Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant @tag :pending with a # symbol.

For example:

# @tag :pending
test "shouting" do
  assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end

Or, you can enable all the tests by commenting out the ExUnit.configure line in the test suite.

# ExUnit.configure exclude: :pending, trace: true

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

Source

The Pig Latin exercise at Test First Teaching by Ultrasaurus https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

pig_latin_test.exs

defmodule PigLatinTest do
  use ExUnit.Case

  describe "ay is added to words that start with vowels" do
    # @tag :pending
    test "word beginning with a" do
      assert PigLatin.translate("apple") == "appleay"
    end

    @tag :pending
    test "word beginning with e" do
      assert PigLatin.translate("ear") == "earay"
    end

    @tag :pending
    test "word beginning with i" do
      assert PigLatin.translate("igloo") == "iglooay"
    end

    @tag :pending
    test "word beginning with o" do
      assert PigLatin.translate("object") == "objectay"
    end

    @tag :pending
    test "word beginning with u" do
      assert PigLatin.translate("under") == "underay"
    end

    @tag :pending
    test "word beginning with a vowel and followed by a qu" do
      assert PigLatin.translate("equal") == "equalay"
    end
  end

  describe "first consonant letters and ay are moved to the end of words that start with consonants" do
    @tag :pending
    test "word beginning with p" do
      assert PigLatin.translate("pig") == "igpay"
    end

    @tag :pending
    test "word beginning with k" do
      assert PigLatin.translate("koala") == "oalakay"
    end

    @tag :pending
    test "word beginning with y" do
      assert PigLatin.translate("yellow") == "ellowyay"
    end

    @tag :pending
    test "word beginning with x" do
      assert PigLatin.translate("xenon") == "enonxay"
    end

    @tag :pending
    test "word beginning with q without a following u" do
      assert PigLatin.translate("qat") == "atqay"
    end

    @tag :pending
    test "word beginning with two consonants" do
      assert PigLatin.translate("pleasure") == "easureplay"
    end

    @tag :pending
    test "word beginning with three consonants" do
      assert PigLatin.translate("stringify") == "ingifystray"
    end

    @tag :pending
    test "word beginning with a serie of consonants : aliens speak Pig Latin too" do
      assert PigLatin.translate("zkrrkrkrkrzzzkewk") == "ewkzkrrkrkrkrzzzkay"
    end
  end

  describe "consecutive consonants are treated like a single consonant" do
    @tag :pending
    test "word beginning with ch" do
      assert PigLatin.translate("chair") == "airchay"
    end

    @tag :pending
    test "word beginning with qu" do
      assert PigLatin.translate("queen") == "eenquay"
    end

    @tag :pending
    test "word beginning with qu and a preceding consonant" do
      assert PigLatin.translate("square") == "aresquay"
    end

    @tag :pending
    test "word beginning with th" do
      assert PigLatin.translate("therapy") == "erapythay"
    end

    @tag :pending
    test "word beginning with thr" do
      assert PigLatin.translate("thrush") == "ushthray"
    end

    @tag :pending
    test "word beginning with sch" do
      assert PigLatin.translate("school") == "oolschay"
    end
  end

  describe "'x' and 'y', when followed by a consonant, are treated like a vowel" do
    @tag :pending
    test "word beginning with y, followed by a consonant" do
      assert PigLatin.translate("yttria") == "yttriaay"
    end

    @tag :pending
    test "word beginning with y, followed by another consonant" do
      assert PigLatin.translate("yddria") == "yddriaay"
    end

    @tag :pending
    test "word beginning with xr" do
      assert PigLatin.translate("xray") == "xrayay"
    end

    @tag :pending
    test "word beginning with xb" do
      assert PigLatin.translate("xbot") == "xbotay"
    end
  end

  describe "phrases are translated" do
    @tag :pending
    test "a whole phrase" do
      assert PigLatin.translate("quick fast run") == "ickquay astfay unray"
    end
  end
end

test_helper.exs

ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)
defmodule PigLatin do
  @suffix "ay"
  @vowels "aeiou"
  @vowel_chars String.codepoints(@vowels)
  @xy_vowel_sound ~r/\A(x|y)[^#{@vowels}]/
  @consonant_sound ~r/
    \A
    (?<consonant_sound>
      (?:.qu|qu|.)[^#{@vowels}y]*
    )
    (?<rest>
      .*
    )
  /x

  @doc """
  Given a `phrase`, translate it a word at a time to Pig Latin.

  Words beginning with consonants should have the consonant moved to the end of
  the word, followed by "ay".

  Words beginning with vowels (aeiou) should have "ay" added to the end of the
  word.

  Some groups of letters are treated like consonants, including "ch", "qu",
  "squ", "th", "thr", and "sch".

  Some groups are treated like vowels, including "yt" and "xr".
  """
  @spec translate(phrase :: String.t()) :: String.t()
  def translate(phrase) do
    phrase
    |> String.split(" ")
    |> Enum.map_join(" ", &translate_word/1)
  end

  defp translate_word(word) do
    if starts_with_vowel_sound?(word) do
      word <> @suffix
    else
      %{"consonant_sound" => consonant_sound, "rest" => rest} =
        Regex.named_captures(@consonant_sound, word)

      rest <> consonant_sound <> @suffix
    end
  end

  defp starts_with_vowel_sound?(word) do
    String.starts_with?(word, @vowel_chars) or
      Regex.match?(@xy_vowel_sound, word)
  end
end

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?