Avatar of davearonson

davearonson's solution

to Pig Latin in the Elixir Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

Note:

This solution was written on an old version of Exercism. The tests below might not correspond to the solution code, and the exercise may have changed since this code was written.

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

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:

$ elixir pig_latin_test.exs

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

For more detailed information about the Elixir track, please see the help page.

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

if !System.get_env("EXERCISM_TEST_EXAMPLES") do
  Code.load_file("pig_latin.exs", __DIR__)
end

ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)

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
defmodule PigLatin do
  @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(&String.graphemes/1)
    |> Enum.map(&do_translate(&1, []))
    |> Enum.join(" ")
  end

  defp do_translate(word=[vowel|rest], acc)
      when vowel in ~w(a e i o u) do
    "#{word}#{acc|>Enum.reverse}ay"
  end

  defp do_translate(word=[special,consonant|rest], [])
      when special in ~w(x y)
           and not consonant in ~w(a e i o u) do
    "#{word}ay"
  end

  defp do_translate(["q", "u"|rest], acc) do
    do_translate(rest, ["u", "q" | acc])
  end

  defp do_translate([first|rest], acc) do
    do_translate(rest, [first|acc])
  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?