Skip to content

Commit

Permalink
Add pangram exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
snahor committed Aug 24, 2017
1 parent 8e0fbe4 commit d4824d6
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@

]
},
{
"uuid": "42adb6ed-09f0-6980-2d4f-75ac90cc2bf1f59fb3a",
"slug": "pangram",
"core": false,
"unlocked_by": null,
"difficulty": 1,
"topics": [

]
},
{
"uuid": "225cfd7d-81a3-4a58-b1aa-1de2e40e7a93",
"slug": "binary",
Expand Down
45 changes: 45 additions & 0 deletions exercises/pangram/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Pangram

Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma,
"every letter") is a sentence using every letter of the alphabet at least once.
The best known English pangram is:
> The quick brown fox jumps over the lazy dog.
The alphabet used consists of ASCII letters `a` to `z`, inclusive, and is case
insensitive. Input will not contain non-ASCII symbols.

## Loading your exercise implementation in PolyML

```
$ poly --use {exercise}.sml
```

Or:

```
$ poly
> use "{exercise}.sml";
```

**Note:** You have to replace {exercise}.

## Running the tests

```
$ poly -q --use test.sml
```

## Feedback, Issues, Pull Requests

The [exercism/sml](https://github.com/exercism/sml) repository on
GitHub is the home for all of the Standard ML exercises.

If you have feedback about an exercise, or want to help implementing a new
one, head over there and create an issue. We'll do our best to help you!

## Source

Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram)

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
18 changes: 18 additions & 0 deletions exercises/pangram/example.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
fun isPangram (input: string): bool =
let
val counter = Array.tabulate (26, fn _ => 0)
val chars = map Char.toLower (String.explode input)
val aCode = ord #"a"

fun updateCounter c =
let
val index = ord c - aCode
in
if index < 0
then ()
else Array.update (counter, index, (Array.sub (counter, index)) + 1)
end
in
List.app updateCounter chars;
Array.all (fn x => x > 0) counter
end
2 changes: 2 additions & 0 deletions exercises/pangram/pangram.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fun isPangram (input: string): bool =
raise Fail "'isPangram' is not implemented"
41 changes: 41 additions & 0 deletions exercises/pangram/test.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
(* version 1.1.0 *)

use "pangram.sml";
use "testlib.sml";

infixr |>
fun x |> f = f x

val testsuite =
describe "pangram" [
describe "Check if the given string is an pangram" [
test "sentence empty"
(fn _ => isPangram ("") |> Expect.falsy),

test "pangram with only lower case"
(fn _ => isPangram ("the quick brown fox jumps over the lazy dog") |> Expect.truthy),

test "missing character 'x'"
(fn _ => isPangram ("a quick movement of the enemy will jeopardize five gunboats") |> Expect.falsy),

test "another missing character 'x'"
(fn _ => isPangram ("the quick brown fish jumps over the lazy dog") |> Expect.falsy),

test "pangram with underscores"
(fn _ => isPangram ("the_quick_brown_fox_jumps_over_the_lazy_dog") |> Expect.truthy),

test "pangram with numbers"
(fn _ => isPangram ("the 1 quick brown fox jumps over the 2 lazy dogs") |> Expect.truthy),

test "missing letters replaced by numbers"
(fn _ => isPangram ("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog") |> Expect.falsy),

test "pangram with mixed case and punctuation"
(fn _ => isPangram ("\"Five quacking Zephyrs jolt my wax bed.\"") |> Expect.truthy),

test "upper and lower case versions of the same character should not be counted separately"
(fn _ => isPangram ("the quick brown fox jumps over with lazy FX") |> Expect.falsy)
]
]

val _ = Test.run testsuite
159 changes: 159 additions & 0 deletions exercises/pangram/testlib.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
structure Expect =
struct
datatype expectation = Pass | Fail of string * string

local
fun failEq b a =
Fail ("Expected: " ^ b, "Got: " ^ a)

fun failExn b a =
Fail ("Expected: " ^ b, "Raised: " ^ a)

fun exnName (e: exn): string = General.exnName e
in
fun truthy a =
if a
then Pass
else failEq "true" "false"

fun falsy a =
if a
then failEq "false" "true"
else Pass

fun equalTo b a =
if a = b
then Pass
else failEq (PolyML.makestring b) (PolyML.makestring a)

fun nearTo b a =
if Real.== (a, b)
then Pass
else failEq (Real.toString b) (Real.toString a)

fun anyError f =
(
f ();
failExn "an exception" "Nothing"
) handle _ => Pass

fun error e f =
(
f ();
failExn (exnName e) "Nothing"
) handle e' => if exnMessage e' = exnMessage e
then Pass
else failExn (exnMessage e) (exnMessage e')
end
end

structure TermColor =
struct
datatype color = Red | Green | Yellow | Normal

fun f Red = "\027[31m"
| f Green = "\027[32m"
| f Yellow = "\027[33m"
| f Normal = "\027[0m"

fun colorize color s = (f color) ^ s ^ (f Normal)

val redit = colorize Red

val greenit = colorize Green

val yellowit = colorize Yellow
end

structure Test =
struct
datatype testnode = TestGroup of string * testnode list
| Test of string * (unit -> Expect.expectation)

local
datatype evaluation = Success of string
| Failure of string * string * string
| Error of string * string

fun indent n s = (implode (List.tabulate (n, fn _ => #" "))) ^ s

fun fmt indentlvl ev =
let
val check = TermColor.greenit "\226\156\148 " (**)
val cross = TermColor.redit "\226\156\150 " (**)
val indentlvl = indentlvl * 2
in
case ev of
Success descr => indent indentlvl (check ^ descr)
| Failure (descr, exp, got) =>
String.concatWith "\n" [indent indentlvl (cross ^ descr),
indent (indentlvl + 2) exp,
indent (indentlvl + 2) got]
| Error (descr, reason) =>
String.concatWith "\n" [indent indentlvl (cross ^ descr),
indent (indentlvl + 2) (TermColor.redit reason)]
end

fun eval (TestGroup _) = raise Fail "Only a 'Test' can be evaluated"
| eval (Test (descr, thunk)) =
(
case thunk () of
Expect.Pass => ((1, 0, 0), Success descr)
| Expect.Fail (s, s') => ((0, 1, 0), Failure (descr, s, s'))
)
handle e => ((0, 0, 1), Error (descr, "Unexpected error: " ^ exnMessage e))

fun flatten depth testnode =
let
fun sum (x, y, z) (a, b, c) = (x + a, y + b, z + c)

fun aux (t, (counter, acc)) =
let
val (counter', texts) = flatten (depth + 1) t
in
(sum counter' counter, texts :: acc)
end
in
case testnode of
TestGroup (descr, ts) =>
let
val (counter, texts) = foldr aux ((0, 0, 0), []) ts
in
(counter, (indent (depth * 2) descr) :: List.concat texts)
end
| Test _ =>
let
val (counter, evaluation) = eval testnode
in
(counter, [fmt depth evaluation])
end
end

fun println s = print (s ^ "\n")
in
fun run suite =
let
val ((succeeded, failed, errored), texts) = flatten 0 suite

val summary = String.concatWith ", " [
TermColor.greenit ((Int.toString succeeded) ^ " passed"),
TermColor.redit ((Int.toString failed) ^ " failed"),
TermColor.redit ((Int.toString errored) ^ " errored"),
(Int.toString (succeeded + failed + errored)) ^ " total"
]

val status = if failed = 0 andalso errored = 0
then OS.Process.success
else OS.Process.failure

in
List.app println texts;
println "";
println ("Tests: " ^ summary);
OS.Process.exit status
end
end
end

fun describe description tests = Test.TestGroup (description, tests)
fun test description thunk = Test.Test (description, thunk)

0 comments on commit d4824d6

Please sign in to comment.