INPUT /  OUTPUT /  LANGUAGE





Logic and Calculation

So far, whenever something needed working out, your program did it: the console sent a line of input, your program did the thinking, and printed the result. That's often the right approach. But the console can do simple calculations and make simple decisions by itself, using transient logic tags. That's useful when you want the screen to react straight away, without a round trip to your program, or when the logic belongs to the interface rather than to your program.


Arithmetic

Each arithmetic tag takes a list of numbers in its primary field, and generates the result:

TagDoesExampleGenerates
addadds them all up<add 1,2,3>6
subsubtracts the rest from the first<sub 10,3,2>5
mulmultiplies them all<mul 2,3,4>24
divdivides the first by the rest<div 7,2>3.5
modthe remainder after dividing<mod 17,5>2
negchanges the sign<neg 5>-5
absremoves the sign<abs -7>7
intdrops anything after the point<int 3.7>3

Dividing by zero generates inf.

Calculating with lists

add, sub, mul and div have a with field, for doing the same calculation on every element of a list:

<add 1,10,-2 with=2>         <! adds 2 to each: generates 3, 12, 0 !>
<mul 2,3 with=10,100>        <! pairs them up: generates 20, 300 !>

With one value in with, it's applied to every element. With a list of the same length, the elements are paired up.

Text that holds numbers

Whatever the user types in an input box is text, even if it looks like a number. {12} is not the same as 12, and adding text doesn't do what you might expect: add joins text together, so <add {5}, 1> generates 51.

The num tag converts text into a number:

<num {12}>         <! generates the number 12 !>
<num { 3.5 }>      <! generates 3.5; spaces around the number are ignored !>
<num {abc}>        <! generates invalid: there's no number in it !>

Always convert the contents of an input box with num before calculating or comparing with it.

Comparing

These tags compare values, and generate true or false:

TagGenerates true if...Example
eqall the values are equal<eq 5,5,5>
neany of the values are different<ne 1,2>
ltthe first is less than the second<lt 1,2>
leless than or equal<le 2,2>
gtgreater than<gt 3,2>
gegreater than or equal<ge 3,3>

eq compares values by what they mean, using the rules from the chapter on values for what counts as true and false. So <eq 0, false> and <eq 2, true> are both true. Text is compared letter by letter, so <lt {apple}, {banana}> is true.

Exact tests

Sometimes you need to know if a value is exactly null (say), not just something that counts as false. The tags true, false, null, inf and invalid do this when you give them a value:

<null x>          <! true only if x is exactly null !>
<invalid <num answer>>   <! true if what was typed isn't a number !>

Without a value, they just generate themselves: <null> generates null.

Combining conditions

TagGenerates true if...
andall of its values count as true
orany of its values count as true
notits value counts as false
xorexactly one of its two values counts as true
<and <ge age, 13>, <le age, 19>>    <! true for a teenager !>

Making decisions: the if tag

The if tag checks the condition in its primary field. If the condition counts as true, it carries out what's in its then field; otherwise, what's in its else field. Whatever the chosen branch generates, the if tag generates too:

<n:scalar 7>
<box <if <eq <mod n, 2>, 0> then={even} else={odd}>>    <! the box says "odd" !>

then and else are code fields, so they can hold actions as well as values, and only the chosen branch is carried out:

<if <gt score, best> then=<best score>, <putLn {new record}>>

You can leave out else if there's nothing to do otherwise, and you can put an if inside a branch of another one.

Joining and splitting text

join turns a list into a single piece of text. Its delim field says what to put between the elements:

<names:vector {Ada}, {Grace}, {Alan}>
<join names delim={, }>      <! generates "Ada, Grace, Alan" !>

This is the easiest way to display a list, since the elements would otherwise run together.

split does the opposite, breaking text into a list. By default it splits wherever there are spaces; use delim to split somewhere else:

<split {red green blue}>         <! generates three elements: red, green, blue !>
<split {a,b,c} delim={,}>        <! generates a, b, c !>

Tag-compounding

Logic code can end up with a lot of nested angle brackets. Tag-compounding is a shorthand that helps. Instead of putting one tag inside the end of another, put a | between them:

<putLn <add 1, 2>>       <! ordinary nesting !>
<putLn | add 1, 2>       <! the same thing, compounded !>

Everything after the | becomes a tag inside the end of the tag before it. You can compound several tags in a row, and the tag before each | can have fields of its own:

<putLn | join delim={-} | add 1,2 with=10>     <! sends "11-12" !>

Compounding is especially neat for changing a value based on itself. This adds one to count:

<count | add count, 1>      <! the same as <count <add count, 1>> !>

Putting it together

This program contains three small tools that all work inside the console. Apart from waiting at the end, the program does nothing at all:

print('''
<count:scalar 0>
<button {Click me} onClick=<count | add count, 1>, <shown count>>
Clicks: <shown:span bold=true {0}></p>

<a:input width=4,em {0}> and <b:input width=4,em {0}></n>
<button {+} onClick=<result | add <num a>, <num b>>>
<button {-} onClick=<result | sub <num a>, <num b>>>
<button {x} onClick=<result | mul <num a>, <num b>>>
<button {/} onClick=<result | div <num a>, <num b>>>
= <result:span bold=true {0}></p>

What is 6 x 7? <answer:input width=4,em>
<button {Check} onClick=<feedback | if <eq <num answer>, 42>
                                      then={Correct!} else={Try again.}>>
<feedback:span {}>
''')
input()
Click me Clicks: 3

12 and 30
+ - x / = 360

What is 6 x 7? 42 Check Correct!

Console or program?

Now that the console can do logic, where should your logic go? A good rule:


Try it

  1. Add a "Reset" button to the click counter.
  2. Make the calculator show "Can't divide by zero" instead of inf. (Hint: test the result with <inf ...>.)
  3. Make a temperature converter: type degrees Celsius, click a button, and see degrees Fahrenheit (F = C × 9 ÷ 5 + 32).
  4. Change the quiz so it also says "That's not a number" if the answer isn't a number.