1

I'm creating some kind of a "personal assistant" application which is basically a web service that receives a message and then does something according to it.

For example I send "what time is it?" and I get the current time as answer. Other examples:

  • "tell me a joke" -> queries and sends some joke from an public api
  • "I have to [X] -> sets [X] to todo list
  • "What do I have to do?" -> sends todo list
  • "Start my pc" -> Starts PC via Wake On Lan

or what ever else comes to my mind.

My first prototype just uses an if else statment which looks something like

if message.contains("foo"):
  # do foo stuff
elif message.contains("bar"):
  # do bar stuff

Of course this will be a total mess after adding several commands so I'm thinking about what would be a good concept to structure such a huge conditional statement.

(I'm using Python with the web.py framework)

One idea is to use some list / map to create a mapping between key words and associated functions and split functionality in different classes (like a todo list module and so on).

There are applications like WolframAlpha or Siri which have just a single input method but several hundred or thousand different functions in the brackground. Of course those are on a totally different level but in general, how do you create a nice and clean branching from a single input to a big number of different functions?

das Keks
  • 203
  • 1
  • 3
  • 6
  • Possible duplicate of [Approaches to checking multiple conditions?](http://programmers.stackexchange.com/questions/191208/approaches-to-checking-multiple-conditions) – gnat Mar 02 '16 at 15:21
  • The linked question is more general and all answers are like "it depends". In my case I gave a more precise description of the problem so I'd not say it's a dublicate. – das Keks Mar 02 '16 at 15:24
  • 1
    @gnat: That's not a dupe, but I swear I've seen this question before. – Robert Harvey Mar 02 '16 at 15:27
  • 1
    You are delving into the realm of NLP. You are either going to have to have a bajillion branching conditions and a hand coded parser of enormous complexity (and there's no way around that or making it look pretty), or go down the path of graduate level research in natural language programming. –  Mar 02 '16 at 15:29
  • 1
    Related: [Language Parsing to Find Important Words](http://programmers.stackexchange.com/questions/179791) and [How do Personal Assistants typically Generate Sentences?](http://programmers.stackexchange.com/questions/270481) – Robert Harvey Mar 02 '16 at 15:30
  • @RobertHarvey possibly [here](http://programmers.stackexchange.com/questions/linked/191208?lq=1) or [here](http://programmers.stackexchange.com/questions/linked/148849?lq=1) – gnat Mar 02 '16 at 15:31
  • 3
    @gnat: None of those are duplicates. The "huge conditional statement" thing is a red herring; large `if` statements are not how personal assistants work. – Robert Harvey Mar 02 '16 at 15:32
  • 1
    If you're going to go up against a graduate-level problem like this, I think your best bet is to combine a voice recognition system like [Sphinx](http://cmusphinx.sourceforge.net/) with a search engine like Apache Lucene. – Robert Harvey Mar 02 '16 at 15:36
  • I'm not planning to make a Siri competitor (at least not yet :P) and think I'm okay with some kind of `if else` statement but I'm looking for some way to make it as clean as possible and easy to add new methods/commands. – das Keks Mar 02 '16 at 15:45
  • @dasKeks then *either* gnat's dup initially proposed is likely the correct one (and you should mark it as such), or you are going to go down the path of writing a parser generator (or [using one](https://wiki.python.org/moin/LanguageParsing) - and the [Wikipedia comparison post](https://en.wikipedia.org/wiki/Comparison_of_parser_generators)). –  Mar 02 '16 at 15:52

1 Answers1

1

The usual approach to mapping strings to functions is to use a dictionary or hashtable. Here is what it would look like in C#:

var commands = new Dictionary<string, Action<string>>();

This creates a hash table with a string key and a method delegate taking one parameter (in this case a string. You can use the parameter however you want).

You can create the commands at compile time:

var commands = new Dictionary<string, Action<string>>
{
    { "Tell me a joke", TellJoke }, 
    { "I have to",  IHaveTo }
};

or at runtime:

commands.add("Tell me a joke", TellJoke);

Where TellJoke is some method that takes one parameter and returns nothing. After a bit of pre-processing of the search string, the mapped function would then be called thusly:

Commands[searchKey](searchString);

or more robustly:

bool ExecuteCommand(string key, string search)
{
    if (commands.Contains(key))
    {
        Commands[searchKey](search);
        return true;
    }
    return false;
}

Of course, a real personal assistant wouldn't work this way. You would have some sort of engine that can be modified at runtime, not compile time, so that it can learn.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • If the key is parameterized (such as with `"I have to {0}"` - you're likely going to be walking all the indexes if you don't find an exact match anyways. This is not likely to be efficient. A [Trie](https://en.wikipedia.org/wiki/Trie) based system with wildcards might be a better choice for the underlying data structure. –  Mar 02 '16 at 16:01
  • @MichaelT: Fixed. This arrangement would likely pass an arbitrary string parameter to every method anyway (i.e. the original search term). As I already hopefully made clear, the approach is naive. – Robert Harvey Mar 02 '16 at 16:02
  • Of course naive dictionary lookup wouldn't be compatible with place holders. I'd probably use a dictionary fixed prefixes and pattern matching/regexes to disambiguate if necessary. – CodesInChaos Mar 02 '16 at 16:02
  • @CodesInChaos I don't disagree with you at all on that, though `{"prefix" => {"validation regex1" => fp1, "validation regex2" => fp2, "default" => fp3}, "prefix", ...}` is going to get hairy too once you start trying to handle it all. And I dread trying to test the data structure. –  Mar 02 '16 at 16:12
  • @MichaelT I'd produce both the prefix and the regex programmatically from the original pattern (which might either be a regex or a simpler language). I'd use some kind of dictionary for the prefix and a flat list for the regex+action. In the first version I'd start with a simple linear search through a list, only adding the prefix dictionary as an optimization later on. – CodesInChaos Mar 02 '16 at 16:32
  • @CodesInChaos I'd be looking at a prefix tree with each node having a list of regexes or other grammars (eww... BNF or xpath) which are associated with function pointers that take the argument list extracted from the regex/bnf/xpath to handle that - assuming that one of them matches. And as this got suitably complex in writing that data structure (and it feels that way already), I'd write a DSL to store the structure (or stick it in some ugly xml). However, this feels like its going to go over the head of the OP who is trying to figure out how to organize if or switch statements optimally. –  Mar 02 '16 at 16:36