Skip to content

Commit

Permalink
Implement pig-latin in Elixir
Browse files Browse the repository at this point in the history
  • Loading branch information
Sgoettschkes committed Nov 4, 2023
1 parent 922bf0a commit 5977117
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 0 deletions.
4 changes: 4 additions & 0 deletions elixir/pig-latin/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
24 changes: 24 additions & 0 deletions elixir/pig-latin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
pig_latin-*.tar

75 changes: 75 additions & 0 deletions elixir/pig-latin/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Help

## Running the tests

From the terminal, change to the base directory of the exercise then execute the tests with:

```bash
$ mix test
```

This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`

Documentation:

* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)

## Pending tests

In test suites of practice exercises, all but the first test have been tagged to be 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:

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

If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:

```bash
$ mix test --include pending
```

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.

```elixir
# ExUnit.configure(exclude: :pending, trace: true)
```

## Useful `mix test` options

* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
* `--failed` - runs only tests that failed the last time they ran
* `--max-failures` - the suite stops evaluating tests when this number of test failures
is reached
* `--seed 0` - disables randomization so the tests in a single file will always be ran
in the same order they were defined in

## Submitting your solution

You can submit your solution using the `exercism submit lib/pig_latin.ex` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
- The [Elixir track's programming category on the forum](https://forum.exercism.org/c/programming/elixir)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
45 changes: 45 additions & 0 deletions elixir/pig-latin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Pig Latin

Welcome to Pig Latin on Exercism's Elixir Track.
If you need help running the tests or submitting your code, check out `HELP.md`.

## Instructions

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, such as the "ch" in "chair" or "st" in "stand" (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.
Check the tests for all the details.

Read more about [Pig Latin on Wikipedia][pig-latin].

[pig-latin]: https://en.wikipedia.org/wiki/Pig_latin

## Source

### Created by

- @DoggettCK

### Contributed to by

- @alarregoity
- @angelikatyborska
- @Cohen-Carlisle
- @devonestes
- @moxley
- @neenjaw
- @sotojuan

### Based on

The Pig Latin exercise at Test First Teaching by Ultrasaurus - https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/
55 changes: 55 additions & 0 deletions elixir/pig-latin/lib/pig_latin.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
defmodule PigLatin do
@doc """
Given a `phrase`, translate it a word at a time to Pig Latin.
"""
@spec translate(phrase :: String.t()) :: String.t()
def translate(phrase) do
phrase
|> String.split(" ")
|> Enum.map(&translate_word/1)
|> Enum.join(" ")
end

defp translate_word(phrase) do
cond do
String.starts_with?(phrase, ["a", "e", "i", "o", "u"]) ->
phrase <> "ay"

String.starts_with?(phrase, "qu") ->
String.slice(phrase, 2..String.length(phrase)) <> "quay"

String.match?(phrase, ~r/^.qu/) ->
String.slice(phrase, 3..String.length(phrase)) <> String.first(phrase) <> "quay"

String.match?(phrase, ~r/^[xy][^aeiou].+$/) ->
phrase <> "ay"

String.match?(phrase, ~r/^\wy$/) ->
"y" <> String.first(phrase) <> "ay"

String.match?(phrase, ~r/^[^aeiou]+y/) ->
first_consonants =
phrase
|> String.codepoints()
|> Enum.take_while(fn char ->
char != "y"
end)
|> to_string()

String.slice(phrase, String.length(first_consonants)..String.length(phrase)) <>
first_consonants <> "ay"

true ->
first_consonants =
phrase
|> String.codepoints()
|> Enum.take_while(fn char ->
!(char in ["a", "e", "i", "o", "u"])
end)
|> to_string()

String.slice(phrase, String.length(first_consonants)..String.length(phrase)) <>
first_consonants <> "ay"
end
end
end
28 changes: 28 additions & 0 deletions elixir/pig-latin/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule PigLatin.MixProject do
use Mix.Project

def project do
[
app: :pig_latin,
version: "0.1.0",
# elixir: "~> 1.8",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end
123 changes: 123 additions & 0 deletions elixir/pig-latin/test/pig_latin_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
defmodule PigLatinTest do
use ExUnit.Case

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

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

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

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

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

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
test "word beginning with p" do
assert PigLatin.translate("pig") == "igpay"
end

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

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

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

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

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

test "word beginning with a series 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
test "word beginning with ch" do
assert PigLatin.translate("chair") == "airchay"
end

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

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

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

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

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
test "word beginning with y, followed by a consonant" do
assert PigLatin.translate("yttria") == "yttriaay"
end

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

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

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

describe "position of y in a word determines if it is a consonant or a vowel" do
test "y is treated like a consonant at the beginning of a word" do
assert PigLatin.translate("yellow") == "ellowyay"
end

test "y is treated like a vowel at the end of a consonant cluster" do
assert PigLatin.translate("rhythm") == "ythmrhay"
end

test "y as second letter in two letter word" do
assert PigLatin.translate("my") == "ymay"
end
end

describe "phrases are translated" do
test "a whole phrase" do
assert PigLatin.translate("quick fast run") == "ickquay astfay unray"
end
end
end
2 changes: 2 additions & 0 deletions elixir/pig-latin/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)

0 comments on commit 5977117

Please sign in to comment.