forked from ChatScript/ChatScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPracticum-ControlFlow.html
More file actions
133 lines (126 loc) · 7.41 KB
/
Copy pathPracticum-ControlFlow.html
File metadata and controls
133 lines (126 loc) · 7.41 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<!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>© Bruce Wilcox, mailto:gowilcox@gmail.com 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
## IF condition syntax
There are two possible syntaxes for the conditions of an if. The original syntax allowed operators and operands, with multiple
tests separated by `AND` or `OR` like this:</code></pre>
<pre><code>if ( $foo < 5 AND $_x ? ~myset)</code></pre>
<pre><code>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 `[]` construction.
The same conditions of above, in a pattern. look like this:</code></pre>
<p>u: ($foo<5 $_x?~myset)</p>
<pre><code>More recently ^if statements are allowed to use pattern notation, merely by saying `PATTERN` at the start of the conditions:</code></pre>
<pre><code>if (PATTERN $foo<5 $_x?~myset)</code></pre>
<pre><code>
So why use one notation over the other? In fact, the most versatile notation is the `PATTERN` 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.
## IF condition and function failures
The other interesting thing about the IF condition is that it automatically traps any rule failures, treating them as a false result.</code></pre>
<pre><code>if (^substitute(character $_value x y FAIL)) {}</code></pre>
<pre><code>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.
# Loop
Simple loops execute code over and over again like this:</code></pre>
<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 liit 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 next 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>