Created
May 30, 2017 07:51
-
-
Save dbousamra/d47c315e3101e42be995c3e6e50b9340 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
case class Gen[A](g: () => A) { | |
/* | |
* Evaluates a Gen, yielding a value | |
*/ | |
def sample: A = { | |
g() | |
} | |
/* | |
* Transforms a Gen | |
*/ | |
def map[B](f: A => B): Gen[B] = { | |
Gen.unit(f(g())) | |
} | |
/* | |
* Sequences a Gen with another Gen | |
*/ | |
def flatMap[B](f: A => Gen[B]): Gen[B] = { | |
map(f).map(_.sample) | |
} | |
} | |
object Gen { | |
/* | |
* Lazily lift an expression into the Gen context. The monadic point/unit/pure | |
*/ | |
def unit[A](a: => A): Gen[A] = { | |
Gen(() => a) | |
} | |
/* | |
* A generator that yields a char | |
*/ | |
def char: Gen[Char] = { | |
Gen.unit(Random.nextPrintableChar()) | |
} | |
/* | |
* A generator that yields an alpha char | |
*/ | |
def alphaChar: Gen[Char] = { | |
Gen.chooseInt(0, 25).map(n => ('A' to 'Z').toList(n)) | |
} | |
/* | |
* A generator that yields an alphanumeric char | |
*/ | |
def alphaNumericChar: Gen[Char] = { | |
Gen.unit(Random.alphanumeric.head) | |
} | |
/* | |
* A generator that yields a list of A's given a Gen[A] | |
*/ | |
def listOfN[A](n: Int, gen: Gen[A]): Gen[List[A]] = { | |
Gen.unit(List.fill(n)(gen.sample)) | |
} | |
/* | |
* A generator that yields an int between min and max | |
*/ | |
def chooseInt(min: Int, max: Int): Gen[Int] = { | |
Gen.unit(min + Random.nextInt(max - min)) | |
} | |
/* | |
* A generator that yields a string with max length 24 (hardcoded, meh) | |
*/ | |
def string: Gen[String] = { | |
Gen.chooseInt(1, 24).flatMap { lengthOfString => | |
Gen.listOfN(lengthOfString, Gen.char).map(_.mkString) | |
} | |
} | |
/* | |
* Choose a gen randomly from a list of gens | |
*/ | |
def elements[A](gs: List[Gen[A]]): Gen[A] = { | |
Gen.chooseInt(0, gs.length).flatMap(n => gs(n)) | |
} | |
/* | |
* A generator of A randomly from a list of A's | |
*/ | |
def oneOf[A](g: A, gs: A*): Gen[A] = { | |
val items = (g +: gs) | |
Gen.chooseInt(0, items.length).map(n => items(n)) | |
} | |
/* | |
* Choose a Gen randomly from a list of Gens | |
*/ | |
def oneOf[A](g: Gen[A], gs: Gen[A]*): Gen[A] = { | |
elements((g +: gs).toList) | |
} | |
} | |
object Example { | |
val genSingularArticle: Gen[String] = Gen.oneOf( | |
"the", | |
"a" | |
) | |
val genAnimals: Gen[String] = Gen.oneOf( | |
"vulture", | |
"howler", | |
"wallaby", | |
"dalmatian", | |
"dolphin" | |
) | |
val genVegetables: Gen[String] = Gen.oneOf( | |
"carrot", | |
"spinach", | |
"squash", | |
"pumpkin" | |
) | |
val genNoun = Gen.oneOf( | |
genAnimals, | |
genVegetables | |
) | |
val genPastTenseVerb: Gen[String] = Gen.oneOf( | |
"howled", | |
"ate", | |
"pooed", | |
"attacked", | |
"walked" | |
) | |
val genAdverb: Gen[String] = Gen.oneOf( | |
"accidentally", | |
"always", | |
"angrily", | |
"anxiously", | |
"awkwardly", | |
"badly" | |
) | |
val genStory: Gen[String] = for { | |
article <- genSingularArticle | |
noun <- genNoun | |
adverb <- genAdverb | |
verb <- genPastTenseVerb | |
} yield s"$article $noun $adverb $verb" | |
val genAnimalAndVegetableStory = for { | |
article1 <- genSingularArticle | |
article2 <- genSingularArticle | |
animal <- genAnimals | |
vegetable <- genVegetables | |
adverb <- genAdverb | |
} yield s"$article1 $animal $adverb ate $article2 $vegetable" | |
def genNStories(n: Int): Gen[List[String]] = | |
Gen.listOfN(n, Gen.oneOf(genStory, genAnimalAndVegetableStory)) | |
def main(args: Array[String]): Unit = { | |
println(genNStories(3).sample.mkString("\n")) | |
// the dolphin accidentally ate the squash | |
// the wallaby badly ate the carrot | |
// a squash awkwardly attacked | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment