In an ado-file, Ben Jann <[email protected]> wants to hold on to
values between calls to Mata. He offers the following (artificial) example:
. program define test
1. mata: test1(x=., y=.)
2. mata: test2(x, y)
3. end
. mata:
: void test1(x, y)
> {
> x = "one"
> y = "two"
> }
: void test2(x, y) display(x + y)
: end
. test
onetwo
Ben observes that this works, but that Mata objects x and y are left
behind after execution, and are left behind even if he changes x and y
to tempnames. He looks for a solution.
It will be convenient in what follows if I recast Ben's example to read
----------------------------------------- mytest.ado ---
program define mytest
mata: test1(x=., y=.)
mata: test2(x, y)
end
mata:
void test1(x, y)
{
x = "one"
y = "two"
}
void test2(x, y) display(x + y)
end
----------------------------------------- mytest.ado ---
I haven't changed anything; I've just put everything inside an ado-file
because I suspect that is what Ben is doing, too.
Solution
--------
All solutions share a common structure: A program is put in front of the
desired sewquence, and it is the responsibility of that program to clean up
after execution.
In the imporoved version of mytest.ado that follows, Ben's original
program mytest is renamed mytest_u, and I have written a new routine
mytest:
----------------------------------------- mytest.ado ---
program define mytest
capture noisily mytest_u `0'
local rc = _rc
capture mata: mata drop x
capture mata: mata drop y
exit `rc'
end
program define mytest_u
mata: test1(x=., y=.)
mata: test2(x, y)
end
mata:
void test1(x, y)
{
x = "one"
y = "two"
}
void test2(x, y) display(x + y)
end
----------------------------------------- mytest.ado ---
New program mytest immediately calls mytest_u -- Ben's original program.
It calls mytest_u with capture noisily, which prevents mytest from
aborting even if Ben's program fails or the user presses Break. The
noisily part ensures that we see the output.
Once Ben's original program returns, we save the return code, clean up,
and exit with the return code of Ben's original. Note that in the cleanup,
I did not code
capture mata: mata drop x y
but instead coded it as two statements
capture mata: mata drop x
capture mata: mata drop y
The reason is the usual one: Even if x does not exist, I still want to drop
y.
The names x and y are in the global space, and the names might conflict with
user-defined Mata objects, so Ben might want to change the code to read
----------------------------------------- mytest.ado ---
program define mytest
capture noisily mytest_u `0'
local rc = _rc
capture mata: mata drop mytest_x
capture mata: mata drop mytest_y
exit `rc'
end
program define mytest_u
mata: test1(mytest_x=., mytest_y=.)
mata: test2(mytest_x, mytest_y)
end
mata:
void test1(x, y)
{
x = "one"
y = "two"
}
void test2(x, y) display(x + y)
end
----------------------------------------- mytest.ado ---
Ben could instead use temporary variables, but then we would have to
deal with passing those names into mytest_u and really, the names
mytest_x and mytest_y are good enough.
Here is another verison of mytest.ado that I rather like:
----------------------------------------- mytest.ado ---
program define mytest
capture noisily mytest_u `0'
local rc = _rc
capture mata: cleanup()
exit `rc'
end
program define mytest_u
mata: test1(mytest_x=., mytest_y=.)
mata: test2(mytest_x, mytest_y)
end
mata:
void test1(x, y)
{
x = "one"
y = "two"
}
void test2(x, y) display(x + y)
void cleanup()
{
rmexternal("mytest_x")
rmexternal("mytest_y")
}
end
----------------------------------------- mytest.ado ---
If Ben likes that form of coding, too, he could think of substituting
the following code for cleanup():
void cleanup()
{
string colvector names
names = direxternal("mytest_*")
for (i=1; i<=cols(names); i++) rmexternal(name[i])
}
This way, as Ben adss other mytest_<name> globals to his system, they
will automatically be added to the cleanup() routine.
I'm of a mixed mind concerning this improvement to cleanup() because I
like to be explicit about my globals. I find that it is too easy to make
a code mistake. I intend to have three globals, mytest_x, mytest_y, and
mytest_z, but at one place in my code, I mistakenly typed mytest_c.
One thing that might help me spot the error would be when I spotted a
leftover mytest_c and wondered what it is.
-- Bill
[email protected]
*
* For searches and help try:
* http://www.stata.com/support/faqs/res/findit.html
* http://www.stata.com/support/statalist/faq
* http://www.ats.ucla.edu/stat/stata/