Created
November 7, 2017 16:26
-
-
Save benhardy/3355f9a5278a3dd5952013cc09178334 to your computer and use it in GitHub Desktop.
A little experiment in parsing English and answering questions
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
/** kinds of things that can make up a sentence */ | |
sealed trait Term { | |
def show: String | |
def complexity: Int = 1 | |
def root: Term = this | |
} | |
case class Word(show: String) extends Term | |
trait NounTerm extends Term | |
trait VerbTerm extends Term | |
trait AdjectiveTerm extends Term | |
trait AdverbTerm extends Term | |
trait InterrogativeTerm extends Term | |
case class Noun(show: String) extends NounTerm | |
case class Adjective(show: String) extends AdjectiveTerm | |
case class Verb(show: String) extends VerbTerm | |
case class Adverb(show: String) extends AdverbTerm | |
case class Interrogative(show: String) extends InterrogativeTerm | |
case class NounDescribed(adj:AdjectiveTerm, noun: NounTerm) extends NounTerm { | |
override def show = adj.show + " " + noun.show | |
override def complexity = adj.complexity + noun.complexity | |
override def root = noun | |
} | |
case class VerbDescribed(adverb: AdverbTerm, verb: VerbTerm) extends VerbTerm { | |
override def show = adverb.show + " " + verb.show | |
override def complexity = adverb.complexity + verb.complexity | |
override def root = verb | |
} | |
case class Statement(subject:NounTerm, verb:VerbTerm, obj:NounTerm) extends Term { | |
override def show = subject.show + " " + verb.show + " " + obj.show | |
override def complexity = subject.complexity + verb.complexity + obj.complexity | |
} | |
case class Question(interrogative: Interrogative, fact: Statement) extends Term { | |
override def show = interrogative.show + " " + fact.show | |
override def complexity = interrogative.complexity + fact.complexity | |
} | |
object Vocabulary { | |
val noun = "cat,dog,chicken,horse,monkey".split(",").toSet | |
val verb = "like,dislike,is,turn".split(",").toSet | |
val adjs = "blue,delicious,hungry,big,dead".split(",").toSet | |
val advs = "quickly,slowly,really,totally".split(",").toSet | |
val interrogative = "who,does,which,what".split(",").toSet | |
} | |
object Parser { | |
def parse(sentence: String): Term = { | |
val words = classifyWords(sentence) | |
val collapsed = words.foldRight(List.empty[Term])(assembleStructure) | |
collapsed.maxBy(_.complexity) | |
} | |
private def classifyWords(sentence: String): List[Term] = { | |
sentence.split(" ").toList.map { | |
case word if Vocabulary.noun.contains(word) => Noun(word) | |
case word if Vocabulary.adjs.contains(word) => Adjective(word) | |
case word if Vocabulary.advs.contains(word) => Adverb(word) | |
case word if Vocabulary.verb.contains(word) => Verb(word) | |
case word if Vocabulary.interrogative.contains(word) => Interrogative(word) | |
case word => Word(word) | |
} | |
} | |
private def assembleStructure(head: Term, tail: List[Term]) = { | |
(head, tail) match { | |
case (adj: AdjectiveTerm, (noun: NounTerm) :: rest) => NounDescribed(adj, noun) :: rest | |
case (adv: AdverbTerm, (verb: VerbTerm) :: rest) => VerbDescribed(adv, verb) :: rest | |
case (subject: NounTerm, (verb: VerbTerm) :: (obj: NounTerm) :: rest) => { | |
Statement(subject, verb, obj) :: rest | |
} | |
case (q: Interrogative, (statement: Statement) :: rest) => { | |
Question(q, statement) :: rest | |
} | |
case (Interrogative("what"), Interrogative("does") :: (subject: NounTerm) :: (verb: VerbTerm) :: rest) => { | |
Question(Interrogative("what"), Statement(subject, verb, Noun("?"))) :: rest | |
} | |
case (Interrogative("who"), (verb: VerbTerm) :: (obj: NounTerm) :: rest) => { | |
Question(Interrogative("who"), Statement(Noun("?"), verb, obj)) :: rest | |
} | |
case _ => head :: tail | |
} | |
} | |
} | |
object Knowledge { | |
val facts = List( | |
"cat really like blue chicken", | |
"dog really like blue chicken" | |
).map(Parser.parse) | |
def query(questionFull: Question): List[String] = { | |
facts flatMap { known => | |
(questionFull, known) match { | |
// exact match of question and fact | |
case (Question(Interrogative("does"), questionFact), fact) | |
if questionFact equals fact | |
=> List("YES") | |
// the question object noun is more general than the fact's, so display the fact's object noun | |
case (Question(Interrogative("does"), question), fact: Statement) | |
if (question.subject == fact.subject) && | |
(question.verb == fact.verb) && | |
(question.obj == fact.obj.root) | |
=> List(s"Yes, if it's ${fact.obj.show}") | |
// the question verb is more general than the fact one, so answer with the fact's specific verb | |
case (Question(Interrogative("does"), question), fact: Statement) | |
if (question.subject == fact.subject) && | |
(question.verb == fact.verb.root) && | |
(question.obj == fact.obj) | |
=> List(s"Yes, ${fact.verb.show}") | |
// value query | |
case (Question(Interrogative("what"), Statement(qSubject, qVerb, Noun("?"))), fact: Statement) | |
if (qSubject == fact.subject) && | |
(qVerb == fact.verb) | |
=> List(fact.obj.show) | |
// subject query | |
case (Question(Interrogative("who"), Statement(Noun("?"), qVerb, qObject)), fact: Statement) | |
if (qObject == fact.obj) && | |
(qVerb == fact.verb) | |
=> List(fact.subject.show) | |
case _ => Nil | |
} | |
} | |
} | |
} | |
object TheOracle { | |
val questions = List( | |
"does cat really like blue chicken", | |
"does cat really like chicken", | |
"does cat like chicken", | |
"does cat like blue chicken", | |
"what does cat really like", | |
"who really like blue chicken" | |
).map(Parser.parse) | |
def main(args: Array[String]) = { | |
val responses = questions.map { | |
case (question: Question) => question.show + "?\n" + Knowledge.query(question).map(" * " + _).mkString("\n") | |
case _ => "" | |
} | |
println(responses.mkString("\n\n")) | |
} | |
} | |
/* | |
This produces the following output: | |
does cat really like blue chicken? | |
* YES | |
does cat really like chicken? | |
* Yes, if it's blue chicken | |
does cat like chicken? | |
does cat like blue chicken? | |
* Yes, really like | |
what cat really like ?? | |
* blue chicken | |
who ? really like blue chicken? | |
* cat | |
* dog | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment