-
Notifications
You must be signed in to change notification settings - Fork 154
Expand file tree
/
Copy pathPracticum-ControlFlow.html
More file actions
345 lines (345 loc) · 11 KB
/
Practicum-ControlFlow.html
File metadata and controls
345 lines (345 loc) · 11 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Practicum-ControlFlow</title>
<style>
html {
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 12px;
}
h1 {
font-size: 1.8em;
}
}
@media print {
html {
background-color: white;
}
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
p {
margin: 1em 0;
}
a {
color: #1a1a1a;
}
a:visited {
color: #1a1a1a;
}
img {
max-width: 100%;
}
svg {
height; auto;
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
background-color: #1a1a1a;
border: none;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
/* The extra [class] is a hack that increases specificity enough to
override a similar rule in reveal.js */
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<h1 id="chatscript-practicum-control-flow">ChatScript Practicum: Control
Flow</h1>
<p>Copyright 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</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"
data-cites="0subject">@0subject</span>) or ^next(FACT <span
class="citation" data-cites="0subject">@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>