-
-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
275 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |