First, as you might find explained in Chapter 4 of the User's Guide
that came with E-Prime, and have instead discovered the hard way,
attributes propogate only downward through logging levels, they do
not persist as you pop back up to higher levels, and as a result they
do not persist as you pop up a level and then back down to the same
level, as happens at the end of a Procedure run by a List. IOW,
despite some appearance, "attributes" are *not* "variables", and they
will not fulfill the role of variables. Variables and attributes
each have their own roles.
So, as explained on the Forum, in short you need to use a global
variable. As explained in Chapter 4 of the User's Guide, you do that
in the User tab of the Script view. Define a variable such as
TRemaining, e.g.,
Dim TRemaining as Long
Then use that global variable to keep track of the value from trial
to trial, as in ... well, when I tried to write out the code it
didn't seem quite right, and I have to run off and catch a bus
now. Maybe you or someone else can now sort this out based on the
clues given so far.
-- David McFarlane, Professional Faultfinder
First, I will restate the specifications:
(1) A block of trials will last for either some
specified time (e.g., 10 min) or for 12 trials, whichever comes first.
(2) The stimulus on each trial will last for
either some specified time (2 min), or for the
remainder of the block duration, or until the
subject responds, whichever comes
first. Unpacking this, in effect it means that
all trials except the final one last for the
usual trial duration or until a response
(whichever comes first), and the final trial
lasts for the remainder of the block duration or
until a response (whichever comes first).
I already know some functions for working
directly with the clock times of events, so for
this problem I find it handier to think in terms
of the clock times for when the blocks and trials
will end, instead of the remaining block
duration. So here is my solution (following good
programming practices, such as using constants in place of "magic numbers").
I would first define a global variable on the
User tab of the Script window. This will hold
the clock time of when the block must end. (I
like to use a "g_" prefix to remind me that it's
a global and not a local variable):
Dim g_BlockTargetOffsetTime as Long
Then I would set up a structure with some InLines something like the following:
BlockInitCode
TrialList
TrialProc
TrialInitCode
TrialStimSlide
TrialStimSlide is the stimulus, with Duration set to [TrialDur].
BlockInitCode is an InLine object that simply
stores the target offset time of the block for
use later (it assumes that the first event in
TrialProc is our stimulus, and uses the much
overlooked GetNextTargetOnsetTime method, see
that topic in the E-Basic Help facility):
Const BlockDur as Long = 10 * 60 * 1000
g_BlockTargetOffsetTime = GetNextTargetOnsetTime + BlockDur
And TrialInitCode is another InLine object that
sets the TrialDur attribute according to specifications (1) and (2) above:
Const TrialDur as Long = 2 * 60 * 1000
If ((GetNextTargetOnsetTime + TrialDur) <=
g_BlockTargetOffsetTime) Then ' all but the final trial
c.SetAttrib "TrialDur", TrialDur
Else ' final trial
c.SetAttrib "TrialDur", _
(g_BlockTargetOffsetTime - GetNextTargetOnsetTime)
End If
And if you don't mind using one of the less
common coding conveniences of E-Basic/VBA, then you could shorten that to
Const TrialDur as Long = 2 * 60 * 1000
c.SetAttrib "TrialDur", _
Iif( ((GetNextTargetOnsetTime +
TrialDur) <= g_BlockTargetOffsetTime ), _
TrialDur, (g_BlockTargetOffsetTime - GetNextTargetOnsetTime) )
Pros to this solution:
- It does not use any array, nor require the
attending complications of computing array indexes.
- It does not require any "If
c.GetAttrib("Trial") > 1" test on each trial --
seems wasteful to run such a test on each trial
when we know ahead of time that it will succeed
on all trials except the first. Usually we can find a better way.
- It works directly in terms of the target offset
time for the block, which gets computed only
once. It is thus more accurate with time
(without care, accumulated computations of block
time remaining might not take into account delays
in actual stimulus duration, etc.).
Criticisms of this solution:
- Some users would find it more natural to work
in terms of the remaining block duration instead
of the target block offset time (but, without
much trouble, this solution could be adapted to
compute and use remaining block duration instead).
- It uses the GetNextTargetOnsetTime method -- a
very powerful concept in E-Prime, but kept well
hidden in the documentation (I stumbled upon it
myself only while searching the Forum for other things).
- Instead of having all the code compactly
gathered into one InLine (plus a bit of global
User code), it is spread across two InLines (plus
a bit of global User code). This may make it
slightly harder for some to follow (I think
Michiel Spapé has made well-put comments on this
sort of tradeoff either on the Group or in his E-Primer).
- You already have a perfectly good working
solution that makes sense to you, so I'm a little late to the game, aren't I?
-- David McFarlane, Professional Faultfinder