Stata The Stata listserver
[Date Prev][Date Next][Thread Prev][Thread Next][Date index][Thread index]

Re: st: Temporary objects in Mata


From   [email protected] (William Gould, Stata)
To   [email protected]
Subject   Re: st: Temporary objects in Mata
Date   Wed, 02 Nov 2005 08:04:16 -0600

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/



© Copyright 1996–2024 StataCorp LLC   |   Terms of use   |   Privacy   |   Contact us   |   What's new   |   Site index