DMDX
Help.
Set Counter Keyword
<SetCounter
designator = expression>
<set
designator = expression>
counter designators:
counter.name.
c.name.
counterN
cN
N
deprecated usages:
<SetCounter N1,N2>
<set N1,N2>
<SetCounter N,text>
<set N,text>
Keyword to define and
manipulate either named counters (as of DMDX 6.3.0.0) or classic numbered counters. For a discussion on the uses of counters see the
Simple Counter and the
More Complex Counter sections in the built in
introduction, not to mention the
Indexed Branching and the following Dynamic Keyword section.
Preferred usage evaluates
expression
using a modified and significantly expanded version
of CALC.C that Bob Brodt wrote in 1985 and assigns the result to the specified
counter, either the named counter
.name. or the numbered
one
N (or
N1 depending on your usage).
Note that counter 1 is the same as counter
c1 and counter1
but distinct from the named counter .1.. If
a named counter has been
used a negative counter number will be automatically assigned (starting from -1)
for internal uses where the name can't be used, the DMDX user however should not need to ever use it.
First deprecated usage sets Counter N1
to value N2,
second deprecated usage sets counter N
to some value depending on the token string
text (see below for a list of tokens). Counters are used to
control branching in DMDX, more specifically for building looping mechanisms but
their use extends far beyond that.
Before a counter can be used it must be set up at least once with a
<SetCounter>
keyword, after that it can be incremented or decremented, conditionally incremented
or decremented depending on the subject's response, used as a parameter to
control other keywords, tested against with the
Branching keywords
or set to another value with <SetCounter>
again. Counter values are signed 32 bit integers, if you need
to represent real values (for positioning display
elements on the screen for instance) then
the
<macro fxpset> keyword can be used to simulate fixed point arithmetic.
Maximum name length (including dots) is 40 characters and the names are case
insensitive and counter namespace is independent from
macro namespace so you can quite
happily have both a counter and macro with the same name that don't interfere
with each other in any way. As of version 6.4.0.0 of DMDX counter
namespace includes UTF-8 characters as long as the
Unicode option is on.
A counter's value can be incremented or decremented with
<inc>
and
<dec>, conditionally incremented or decremented depending on
the subject's response with
<incic>,
<inciw>,
<incinr>,
<decic>,
<deciw>
or
<decinr> (see the Branching keywords
for a full list of possibilities), used as the time out,
output to other devices, used to
set macros up (particularly
handy for uses where DMDX's syntax doesn't otherwise allow the use of a
counter), or stored in the output data file with
<EmitCounter>.
Counters can be displayed with
<AppendCounter> (not recommended)
or the <sprintf>
keyword (recommended). A counter's value can also be used to set the frame
duration with
<CounterFrameDuration>
or used in looking time studies.
Possible tokens in
expression and values for
text
are:
Counter.name. |
The value of counter
.name.,
for example
<set counter.newcounter. =
counter.oldcounter.>
would set (and create if necessary) counter
.newcounter. to the value counter
.oldcounter. had when
this keyword is parsed.
|
c.name. |
Synonym for
Counter.name.
|
CounterN |
The value of counter
N,
for example <set counter3 = counter2>
would set (and create if necessary) counter 3 to the value counter 2 had when
this keyword is parsed.
|
cN |
Synonym for
CounterN
|
LastRT |
Absolute value of the
last Reaction Time gathered in whole milliseconds (rounded down).
Because expressions are evaluated as the item is parsed this RT is the
previous
item's RT, not the RT that might be gathered by the item the expression is in.
|
LastXT |
Last Reaction Time
(previous item) gathered in whole milliseconds (rounded down). This
will be negative if the response was incorrect.
|
ErrorRate |
The error rate at the
end of the previous item (useful for multiple tests against the error rate as
a
branch on the error rate
will reset it). There's an example of how to display the error rate in
the <apc>
documentation.
|
ClockOnTime |
The millisecond
time that the clock was last turned on. This has two possible values
depending on whether
<RecordClockOnTime>
is turned on. If <rcot>
is on then loading a counter with clockontime
will return the time the most recent clock on
occurred since the first clock on in the item file, otherwise it is the time since
the item file commenced execution.
|
MillisecTime |
The millisecond
time when the expression is evaluated (usually the time the item is parsed
but it can be later with
<AbortItemExpression>) since the item file commenced execution.
|
VideoTime |
The current video
retrace count when the expression is evaluated since the item file commenced
execution.
|
VideoModeX |
The current video
mode width in pixels.
|
VideoModeY |
The current video
mode height in pixels.
|
atoizilliontext |
The integer value of the
previous item's text string entered with
<ZillionTypedResponses>.
|
jobstatus |
The
value of the job status variable that tracks the job state at the time of
parsing. Only useful for the
<AbortItemExpression>.
|
Rules regarding evaluation of
expression
are as follows:
* Calculator program - 9 May 1985
* Bob Brodt
* 34 Mehrhof Rd.
* Little Ferry, NJ 07643
* (201)-641-9582
*
* This is a simple integer arithmetic calculator program. It uses infix
* notation, i.e. 1+2*3 as opposed to "reverse polish" notation: 1 2 3 * +.
*
* CONSTANTS:
* Numbers may be input as in C using 0x notation for hex,
and a
* leading zero for octal, everything else is assumed to
be decimal.
Single character constants can be
used in single quotes, reserved
DMDX ones
will throw syntax errors if used (for example '"', '>',
'$' and ';'), there are no escape sequences for non-printing
characters (' is legal as in ''').
* DMDX constants at time of evaluation:
lastrt
lastxt
errorrate
clockontime
millisectime
videotime
videomodex
videomodey
atoizilliontext
jobstatus
* VARIABLES:
* DMDX counters referenced by counter.name., c.name., counterN or cN
* OPERATORS:
* The following operators are supported (from highest precedence
to lowest):
*
* ( ) associativity
* ~ ! one's complement, logical NOT
* * / % multiply, divide and modulo
* + - unary and binary add & subtract (note unary operators have problems until
5.1.1.2, see
PROBLEMS below)
* .SHL. .SHR. shift left and right
* .LT. .GT. .LE. .GE. less than,
greater than, less than or equal to and
greater than or equal to
* .EQ. .NE. equal to and not equal to
* & bitwise AND
* ^ bitwise exclusive OR
* | bitwise inclusive OR
* .AND. logical AND
* .OR. logical OR
* = assignment
* , comma - separates function arguments
*
* All operators associate from left to right with the exception
of
* the assignment (=) operator. Logical operations
resolve to 1 if true and 0
* if false.
* FUNCTIONS:
* The calculator also has built-in function capabilities:
* max(a, b) - larger of a or b
* min(a, b) - smaller of a or b
* abs(a) - absolute value of a
* random(a) - random number
between 0 and a-1
* PROBLEMS:
* Precedence of functions is a little weird, currently random(a) + b
evaluates to random(a + b) because
* function call brackets are just brackets, so random a + b is valid
syntax with + having the higher
* precedence. So the safe use of functions is currently (random a)
+ b.
* Similar kind of thing for unary operators -5 + 15 evaluated to
-20, eek. Worse -5 .ge. 0 evaluates to
* -1 instead of 0.
So you had to have (-5) + 15 and (-5) .ge. 0. Brute forced a fix in
5.1.1.2
Typical use of a counter involves
setting it to some initial value and decrementing it and using one of the counter
branching keywords
<bicGT>
till the counter reaches 0. In the following item 10 will be displayed
five times:
0 "looping demo" <set c1 = 5>;
+10 * "display"
<dec 1>
<bicGT 1,0,-10>;
0 "end";
Using the named counters and
<BranchIf> that
looping example
might be written as:
0 "looping demo" <set c.loopcounter. = 5>;
+10 * "display"
<dec c.loopcounter.>
<bi -10, c.loopcounter. .gt. 0>;
0 "end";
Possible things to be aware of are
that expressions (both in <SetCounter>
and
<BranchIf> keywords), non conditional arithmetic
(<inc>
and
<dec>)
and most other actions in counter related keywords are performed as the item is
parsed before display so multiple uses of those keywords in the same item are possible.
Whereas conditional arithmetic keywords (<incic>,
<inciw>,
<incinr>,
<decic>,
<deciw>
or
<decinr>)
are evaluated after the item is displayed and the RT gathered (thus necessitating
special control structures within DMDX) and are limited to one operation per counter
per item, in the case of multiple uses only the last will have any effect.
Branches other than <BranchIf>
(<bicGT>
and
<bicLE>)
are evaluated at the beginning of the next item after everything else.
Another typical use of counters involves
tracking multiple error rates and branching if the error rates are too high.
In the following each class of test needs an 80% or higher success rate:
100 "start" <set c.test1correct.=0> <set c.test1total.=0>
<set
c.test2correct.=0> <set
c.test2total.=0>
<set
c.test3correct.=0> <set
c.test3total.=0>;
+1000 "class 1 test" * <incic
c.test1correct.> <inc
c.test1total.>;
+2000 "class
2
test" * <incic
c.test2correct.> <inc
c.test2total.>;
+3000 "class
3
test" * <incic
c.test3correct.> <inc
c.test3total.>;
!
repeat items 1000, 2000 and 3000 as needed;
~200 <emit
c.test1correct.> <emit
c.test1total.>
<emit
c.test2correct.> <emit
c.test2total.>
<emit
c.test3correct.> <emit
c.test3total.>
<bi
1000, (c.test1correct. * 100 / c.test1total. .lt. 80) .or.
(c.test2correct. * 100 /
c.test2total. .lt. 80) .or.
(c.test3correct. * 100 /
c.test3total. .lt. 80)>;
0 "end";
See the
<ZillionTypedResponses>
keyword for a fairly complex counter using example.
You can manipulate a series of counters as an array
using macro indexes. For example you could have 100 counters between 100
and 199 and access them as a two dimensional 10x10 array using two macros
X and Y to
access any given element with c1~X~Y for
example after using
<macro set X=2>
and <macro set Y=7> to access element (2,7)
(note we're accessing dimensions with indexes from 0 through 9). If you
require more than a dimension of 10 then you more or less have to calculate the
index yourself, say you want a one dimensional array and your counters start at
100 and you want to access the element in counter
.index. you would need to set a macro to 100 plus your index with
<macro set .offset. = c.index. + 100> and then
access the element itself with c~.offset. (with
the traditional caveat that you have to set your macro up in a different item
from the one that evaluates the array element because macros are expanded before
the item is parsed). Alternatively with a bit more gerfingerpoken
you can put a leading zero on the index (unless of course you used counters from
zero on up) with the
<macro fxpset>
option used to put leading zeros on macros to be used to position screen
elements and discarding the leading zero and decimal point. For example
say you had a one thousand element array stored in counters 1000 through 1999 to
access any given element with counter
.index. you would have to first set macro
X to the ASCII representation of
c.index.with
<macro fxpset 3 X = c.index.> (if
c.index. had the
value 7 macro X would wind up with the five
characters 0.007) and then discard the leading
0 and decimal point with
<macro pop X, c1> <macro pop X, c1>
(discarding the 0 and
. values into counter 1) allowing you finally access your element with
c1~X.
DMDX Index.