Numbers, time, dates, durations
Besides text, your chatbot can also handle other types of basic data, like numbers, date-time and time-spans. Here’s how to provide support for these features:
Numbers
The application engine will automatically convert every part of the input text that it can, into an integer or double. Furthermore, with the aid of the thesaurus, you are also able to pick up words that represent numbers and convert them into their corresponding number values. This allows us to easily work with and manipulate these numbers. Here are a few input pattern examples:
^n:number
^n:integer
^n:double
^n:noun.(language unit).numeral
The first 3 examples are the simplest: you can read a double (floating number), an integer or if you don’t care which, you can simply use ‘number’. The last one is a bit more interesting. Here we use the thesaurus to catch words like ‘one’, ‘two’,… Of course, that’s very limiting, cause this way, you’d have to declare a thesaurus-entry for every single number in the universe, which is, well…, impossible. So better to write the last pattern like so:
^n:noun.(language unit).numeral { [and] ^n:noun.(language unit).numeral}
The {} operator is used to find a repeating block in the input. So this catches things like: two thousand and five hundred. As you can see, we use the same variable name for the number in the front and repeating part. This means that most likely, the ‘n’ variable will contain a list of words instead of just 1. Also, we don’t collect the ‘and’ cause it would screw up the conversion procedure. A small warning about the {} operator though: The application can’t find duplicate patterns when the {} operator is used, so watch out for this. Also note, the thesaurus paths only work if the thesaurus contains data (can be loaded from xml file).
For completion, you could also include a pattern that handles mixed numbers and words. The conversion algorithm is perfectly capable of handling this mixed type of input.
Working with numbers
So, now that we can read in the numbers, lets look at how we can use them. let me just pick up where we ended: with word-numbers. Right now, the variable is still containing words (or a mix of words and numbers), but we actually need a single number, so this needs to be converted. Luckily, you don’t need to do this manually, but there’s already a function available for this. Here’s a usage example:
$value = $n:ToNumber;
:ToNumber will convert the content of the variable into a number (only integers are currently supported, a ‘.’ is not yet recognized). Each individual word is converted using the thesaurus and finally added/multiplied together to form a single number. Now, this conversion algorithm is language dependent and at the time of writing, there is only an implementation for English. If you need it for a different language, best to contact me, or if you feel up to adding it yourself, have a look at the chatbot’s module source file ‘convert.nnl’, which contains the definition for ‘ToNumber’.
Of course, the primary purpose for having real numbers, is so that we can do calculations with them. As you’ll soon see though, this is sometimes done a little bit different compared to a traditional programming language. That’s because the NNL works mostly with sets (like in 1 variable containing multiple values). So, if you want to add the contents of a single value, you need to use some special instructions. But, if you know that the variables contain 1 value and you want to do some regular calculations, that’s also possible. Here are some examples:
$result = Addition($n);
$result = $n – $w;
$result = $n – $w – $d – $f;
$result = subtract($n, $w,$d,$f + $h);
…
The basic principle is always the same: since a variable can contain a list of items (a set), it only needs 1 argument to perform the arithmetic operation, as long as it has enough values in the list.
For completion, here are the most important operators/functions (for a complete lists of available functions, see the instruction list):
| Function | Operator | Description | Nr of arguments |
| Addition | + | Adds all the numbers | any length |
| Minus | - | subtracts each item from the first | any length |
| Multiply | * | multiplies each item with the first | any length |
| Divide | / | divides each item with the first | any length |
| Modulus | % | returns the modulus of the 2 numbers | 2 items |
| Complete | completes the sequence of the 3 numbers | 3 items | |
| Count | counts the nr of items in the list | any length | |
| StDev | calculates the standard deviation of the values | any length | |
| Min | returns the smallest number in the list | any length | |
| Max | returns the biggest number in the list | any length | |
| Avg | gets the average of all the values. | any length | |
| Complement | removes all the items in the argument from the input variable and returns the result (the difference between the 2). | any length | |
| Reverse | Reversed the order of the items in the list (last becomes first) | any length | |
| Distinct | Removes all duplicates. If there are any arguments, these are first added to the end of the list before duplicates are removed. | any length | |
| Union | Adds the arguments to the end of the input items. | any length |
Date and time
Next on the menu are dates and times. These are very closely related to numbers. Internally, the system doesn’t see a difference between a date and a time, it’s always a DateTime. Depending how you use the value, you can regard it as a date, time or DateTime.
First of, there is a special variable to get to the current system date/time: $time. This returns a DateTime-cluster which contains a list of numbers that together form the date and time. The order is predetermined by the system to make it language independent. For ease of use, there are functions to get to each part of the date, so you don’t need to memorize each position in the format (though it helps for putting dates back together). Here’s a table of all the functions to get each part of the date list (+ the index position of the item in the list):
| Index | Function | Meaning |
| 0 | Year | Gets the year number. |
| 1 | Month | Gets the month of the year. |
| 2 | Day | Gets the day of the month. |
| 3 | Hour | Gets the hour (24 hours in a day) |
| 4 | Minute | gets the minutes (60 per hour) |
| 5 | Second | gets the nr of seconds (60 per minute) |
| 6 | DayOfWeek | gets the nr day number of the week (0=Sunday, 1= Monday,…) |
And here are some output patterns to show how you can use them:
$time:day/$time:month/$time:year
$time:hour : $time:minutes : $time:second
The current month is ^noun.month[$time:month-1]
Note that I deliberately put spaces between the variables and the time indicator (‘:’) to avoid using the escape character (\). Also, in the last example, we get the name of the month from the thesaurus through the index number. A thesaurus child at a specific index position can be accessed with the [] operator in the variable path. Because there’s no point of putting words between the brackets and this so often requires a small calculation (0 vs 1 based indexes), I have opted here for a more traditional calculus notation. Supported operators are: +-*/%.
Often it’s also useful to recompose a date, based on a series of numbers. For instance, to build the birthday of someone. As you probably already suspected, there are functions to do just that. Here are some examples:
$date = IToDT($list);
$time = IToDT(0, 0, 0, $list); //we set the first items to 0 cause we only want the time value.
$date = IToDT($year, $month,$day);
For building the date, the first value in the list has to be the year. This is the only required item, but more can follow: month, day, hour, minute, second. In that order. To build a time value, the first item has to be the hour. Minutes and seconds are optional, but have to be provided in the previously mentioned order. If you want to omit the minutes (or any other) part , use 0 instead.
Why not simply store the date values as individual numbers in memory fields yourself, you might wonder instead of going through the hassle of converting a number into another number (which these conversion functions basically do). Well, that will become apparent in the next section.
querying on date
The date and time values are stored in such a way that a year, month, day, minute, second or week in a date is always represented by the same neuron. This means that you can easily find all the dates (and therefor the attached assets) that meet certain criteria (all in the month June for instance). This is very useful in queries like “Who else is born in the same year and month as I am?”. Getting the date objects is real easy. Here’s an example:
var iAllOfYear = GetClustersWithMeaning( #user.birthday:year, ref(Time)); //get all dates for the specified year
$people= Chatbot.Search.GetAssetsFromValue(iAllOfYear, ‘birthday’); //get all assets that link to those dates
$people = Complement($people, #user); //remove the current user.
$people = #($people).name:Interleave(‘, ‘, ‘ and ‘); //get the names of all the remaining assets and format them nicely
–or, for year and month:–
var iAllOfDate = GetCommonParentsWithMeaning(ref(time), #user.birthday:year, (#user.birthday:Month);
$people= Chatbot.Search.GetAssetsFromValue(iAllOfDate , ‘birthday’);
$people = Complement($people, #user); //remove the current user.
$people = #($people).name;
Note: For a full example, check the Aici demo found at {documents}\nnd\demos\aiciOnline.dpl, alternatively for a smaller demo, see the Date-Time demo .
In the first line, we get the year section of the user’s birthday. With the ‘GetClustersWithMeaning’ instruction, we retrieve all the date objects that the system has stored for the same year as the user’s birthday. If you want to supply extra date info, you can use the ‘GetCommonParentsWithMeaning’, as is shown in the second example. With ‘Chatbot.Search.GetAssetsFromValue’ we retrieve all the assets that link to the date. The ‘birthday’ argument, specifies that we only want the assets that link to the date value with the ‘birthday’ attribute.
At this point, we have a list of assets that reference a date of the same year as the user’s birthday. There are still 2 things that need to be done: first we need to remove the ‘#user’ from the asset list, cause the ‘GetDates’ returns all objects, including the date object that represents the user’s birthday, but since the bot was asked ‘who else’, we need to remove the ‘#user’. This is done with the ‘complement’ instruction. This is a mathematical function (borrowed from set-theories) and will return a list that contains all the items from the input with all the arguments removed.
In the last line, we get the name value of all the assets that we found. This is again on a different line cause we need an asset path to get the ‘name’ of an asset. Regular variable paths don’t know how to do this. Hence the extra 3th line. And thus, we get the names of the known objects that have a birthday in the same year as the user.
Time ranges
Besides the Date-Time type, there is also a Time-Span type. This specifies a length of time instead of a specific moment. You get a Time-Span by building it with a function, or as the result of some Date-Time calculations (see later). To build a time range (or time-span), you can do something like:
$range = ChatBot.Convert.ToTimespan($list);
$range = ChatBot.Convert.ToTimespan(($days, $Hours)); //note the 2 (), this is required cause ToTimeSpan only expects 1 argument
Unlike a DateTime, a timespan contains the number of: days, hours, minutes and seconds (no year or month sections). so there are only 4 elements in the list. The first item in the list should always be the nr of days, possibly followed by the hours,… Missing data is set to 0. Like with IToDT, you can also specify missing data as arguments. The same order applies.
For retrieving values in a TimeSpan, you can use the same functions as for the DateTime. They operate in exactly the same way, but the index position of the actual items in the list is different compared to a DateTime. here’s an overview:
| Index | Function | Meaning |
| 1 | Day | Gets the total nr of days in the range. |
| 2 | Hour | Gets the remaining nr of hours. |
| 3 | Minute | gets the remaining nr of minutes. |
| 4 | Second | gets the remaining nr o seconds. |
At the time of writing, TimeSpans don’t yet support the same level of querying as the DateTime object does. If anyone has an urgent need for this, let me know, and I’ll see what I can do.
Calculating with dates and times
Like with regular numbers, you can also perform some calculations on dates, like subtracting one date from another. The syntax for arithmetic with dates/times is exactly the same as with numbers. Here’s an overview of what’s supported:
| Function | Operator | combination | results |
| Addition | + | Time + {range} | Time |
| range + {range} | Range | ||
| Minus | - | Time – {Time} | Range |
| Min | Time, Time,… | Time | |
| range, range,.. | Range | ||
| Max | Time, Time,… | Time | |
| range, range,.. | Range | ||
| Avg | Time, Time,… | Time | |
| range, range,.. | Range | ||
| StDev | Time, Time,… | Time | |
| range, range,.. | Range |
In general, the principle is always the same: the first argument of the instruction or the left side of the operator determines the type of the result, except for ‘minus’ which always returns a range and only works on Time values.
Boolean operations
As a final topic, perhaps a word about logical or Boolean operations on numbers, dates and ranges. Well, for the most part, these work as you would expect both for regular numbers and times/ranges. There’s just 1 small twist. The mathematical people out there will probably have noticed that set-theory is very present in this system: all variables can contain a list of items. This means that Boolean operators need to be prepared for this. Here’s an overview on how each operator handles lists:
- Equality (==): If the left part contains a list, the right side must have the exact same list: with the same nr of items, in the same order.
- Difference (!=) If the left part contains a list, the right side is different if it contains a different amount of items, the sequence is different or one or more of the items differ.
- Contains (contains): The right side of the operator should be part of the list found on the left side of the operation.
- doesn’t contain (!Contains): The right side of the operator should not be part of the list found on the left side of the operation.
- >, >=, <, <= (Bigger, bigger or equal, smaller, smaller or equal): If the left part contains a list, each item in the list will be evaluated to each item found on the right side. The operation succeeds if all items are compared and none failed.
There is plenty more to say about this topic. But my time-range has run out, a time to move on has been reached. All that’s left for me to say is: Stay tuned and CY later.