-
Notifications
You must be signed in to change notification settings - Fork 155
Expand file tree
/
Copy pathPracticum-ControlFlow.html
More file actions
119 lines (119 loc) · 7.55 KB
/
Practicum-ControlFlow.html
File metadata and controls
119 lines (119 loc) · 7.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<title></title>
<style type="text/css">code{white-space: pre;}</style>
</head>
<body>
<h1 id="chatscript-practicum-control-flow">ChatScript Practicum: Control Flow</h1>
<p>Copyright Bruce Wilcox, mailto:[email protected] www.brilligunderstanding.com <br>Revision 6/9/2018 cs8.3</p>
<p>'''There's more than one way to skin a cat'''. A problem often has more than one solution. This is certainly true with ChatScript. The purpose of the Practicum series is to show you how to think about features of ChatScript and what guidelines to follow in designing and coding your bot.</p>
<p>The backbone of any program is its control flow. The basic control flow of most computer languages is sequential flow, with options for conditional flow and loops. ChatScript defines a topic as a sequential flow of rules (either gambit or responder). And it defines outputmacros in the typical way as a sequential flow, with options for conditional flow (IF) and loops (LOOP).</p>
<h1 id="ifthenelse">IF/THEN/ELSE</h1>
<p>Technically ^if is a predefined special syntax. The compiler will accept <code>if</code> without the <code>^</code> as long as you provide parenthesized arguments (the if conditions) afterwards. The basic syntax allows for (but does not require) <code>elseif</code> and <code>else</code> clauses.</p>
<pre><code> if (...) {...}
else if (...) {...} # optional
else {} # optional</code></pre>
<h2 id="if-condition-syntax">IF condition syntax</h2>
<p>There are two possible syntaxes for the conditions of an <code>if</code>. The original syntax allowed operators and operands, with multiple tests separated by <code>AND</code> or <code>OR</code> like this:</p>
<pre><code> if ( $foo < 5 AND $_x ? ~myset)</code></pre>
<p>Wheneever there is a relational operator, you need to use spaces around it. This contrasts with pattern syntax where operators are embedded without spaces in a composite token. In patterns, the AND condition is represented as merely the next token, whereas the OR condition is represented using <code>[]</code> construction. The same conditions of above, in a pattern. look like this:</p>
<pre><code>u: ($foo<5 $_x?~myset)</code></pre>
<p>More recently ^if statements are allowed to use pattern notation, merely by saying <code>PATTERN</code> at the start of the conditions:</p>
<pre><code> if (PATTERN $foo<5 $_x?~myset)</code></pre>
<p>So why use one notation over the other? In fact, the most versatile notation is the <code>PATTERN</code> one. You can nest AND and OR tests to create hierarchies of precedence, you can do pattern matches in existing user input and you can memorize data from it. So perhaps you should always use PATTERN notation. The other notation is merely historical.</p>
<h2 id="if-condition-and-function-failures">IF condition and function failures</h2>
<p>The other interesting thing about the IF condition is that it automatically traps any rule failures, treating them as a false result.</p>
<pre><code> if (^substitute(character $_value x y FAIL)) {}</code></pre>
<p>So if in the above there is no x in $_value, then the fail request argument causes substitute to issue a fail rule, and the if merely doesn't execute its then part.</p>
<h1 id="loop">Loop</h1>
<p>Simple loops execute code over and over again like this:</p>
<pre><code> loop()
{
....
}</code></pre>
<p>But ChatScript doesn't want to risk an infinite loop, so in the absence of any explicit loop control, it defaults to a limit of 1000. You can change this default by setting a value on $cs_looplimit if you want to extend it.</p>
<pre><code> $cs_looplimit = 10000
loop()
{
....
}
$cs_looplimit = null # back to default of 1000</code></pre>
<p>You can more precisely control the loop by providing a value as argument to the LOOP</p>
<pre><code> loop($_mycount)
{
....
}</code></pre>
<p>or</p>
<pre><code> @0 = ^query(...)
loop(^length(@0))
{
....
}</code></pre>
<p>However, any such value will be forced to be no more than the max loop limit.</p>
<p>Other ways to end a loop involve the loop detecting a failure.</p>
<pre><code> @0 = ^query(...)
loop()
{
$_value = ^first(@0subject)
....
}</code></pre>
<p>The above loop will execute until the ^first function fails, which will end the loop (but not the rule or the topic).</p>
<p>The same would be true of ^last(<span class="citation">@0subject</span>) or ^next(FACT <span class="citation">@0subject</span>)</p>
<pre><code> @0 = ^query(...)
loop()
{
$_value = ^next(FACT @0subject)
....
}</code></pre>
<h2 id="query-idiosyncracies">QUERY idiosyncracies</h2>
<p>You have to be really careful retrieving values of a query in a loop, if you care about ordering of the values. You need to understand how facts are found by a query. When facts are created, they are added to lists associated with the field values.</p>
<pre><code> $_tmp = ^createfact( x y 1)
$_tmp = ^createfact( x y 2)</code></pre>
<p>The first fact is created and cross referenced on a subject list of x, a verb list of y, and an object list of 1. The next fact acts similarly, but adding to lists is the simple add to head of list. That means the subject list for x has the second fact first and the first fact second. ^query walks one of these lists (depending on the query) to get facts to consider. When you loop thru them, if you want them in order of creation, you need to use ^last to get the oldest fact. ^first and ^next will always get the most recent facts first.</p>
<p>Expecting a failure to terminate a loop DOES NOT WORK with JSON arrays, because JSON data accesses don't fail, they just return null when they run out. So below will execute the full loop limit as long as $_array is a JSON array, whether or not it has any values and whether or not those values are JSON objects with a name field.</p>
<pre><code> $_count = 0
loop()
{
$_value = $_array[$_count].name
$_count += 1
....
}</code></pre>
<p>You can handle this using ^length as follows:</p>
<pre><code> loop(^length($_array))
{
$_value = $_array[$_count].name
$_count += 1
....
}</code></pre>
<h2 id="json-idiosyncracies">JSON idiosyncracies</h2>
<p>One unusual side effect of how JSON data is represented is that if you know a unique name of a field in a JSON structure or a unique value, you can query directly into the structure to that level and use LOOP to find information, and even use the result to traverse a structure backwards. If you know 'c' is a unique field value:</p>
<pre><code>outputmacro: ^findarray($_value)
@0 = ^query(direct_o ? ? $_value)
^LOOP()
{
$_f = ^first(@0fact)
$_subject = ^field($_f subject)
if (!^jsonkind($_subject)) {^next(LOOP)}
^return ($_subject)
}
^fail(CALL)</code></pre>
<p>and you can similarly do a search for a unique field name to get its value:</p>
<pre><code>outputmacro: ^findarray($_field)
@0 = ^query(direct_v ? $_valu$_field ?)
^LOOP()
{
$_f = ^first(@0fact)
$_subject = ^field($_f subject)
if (^jsonkind($_subject))
{
$_value = ^field($f object )
^return ($_value)
}
}
^fail(CALL)</code></pre>
</body>
</html>