Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Mata declarations and conditional (if/else) statements

    I could use some help with if/else statements and Mata declarations: I have a situation where my first inclination was to do something like this:

    Code:
    if (something) real scalar x
    else           real matrix x
    But when I do this I get the error "symbol x multiply defined". I am pretty sure this is something that is simply not allowed in Mata. But I'm not sure what the reason is, or how I would so something like this that is allowed.

    In practice, I'm doing something a little more complicated, along the lines of
    Code:
    class myclass1 {
      void new()
      real scalar x
    }
    class myclass2 extends myclass1 {
      void new()
      real scalar x
    }
    void myclass1::new() {
      "hello from 1"
    }
    void myclass2::new() {
      "hello from 2"
    }
    void testA() {
      if (0) class myclass1 scalar C
      else   class myclass2 scalar C
    }
    testA()
    This gives the same error message as before.

    I thought about using pointers as a workaround, as in the following example. You can see that the new() functions for both C1 and C2 are executed -- when the code is compiled -- even though I have these if/else statements in the code.
    Code:
    : void test2() {
    >   "start"
    >   if (0) class myclass1 scalar C1
    >   else   class myclass2 scalar C2
    >   pointer C
    >   "declarations done"
    >   if (0) C = &C1
    >   else   C = &C2
    >   "end"
    > }
    note: variable C set but not used
    
    : test2()
      hello from 1
      hello from 1
      hello from 2
      start
      declarations done
      end
    However, I can't figure out how to use this pointers approach (beyond using it to reveal when the new() functions are run) although maybe the it could work somehow. I have two concerns. First, I can't figure out the syntax for reading or writing members of a class instance when using the pointer. For example, all these things I tried inside test2() produced error messages:
    Code:
    *C.x = 2
    (*C).x = 2
    *C->x = 2
    C->x = 2
    The second concern is that the pointers approach would still run both new() functions instead of just running the one that is needed. That's not a problem for me, but it could be costly (computationally) if the new() function does something non-trivial.

    Any help or tips would be much appreciated. Thanks!

    - Keith
    Last edited by kkranker; 03 Apr 2018, 13:12.

  • #2
    Hi
    I think this is what you want:
    Code:
    cls
    capture mata mata clear
    mata
        class myclass1 {
          void new()
          string scalar x
        }
        class myclass2 extends myclass1 {
          void new()
          string scalar x
        }
        void myclass1::new() {
          x = "hello from 1"
        }
        void myclass2::new() {
          x = "hello from 2"
        }
        void myclass1::new() {
          x = "hello from 1 again"
        }
        void testA() {
          "C1 Defined"
          class myclass1 scalar C1
          "C2 Defined"
          class myclass2 scalar C2
          "End definition"
          if (0) C1.x
          else C2.x  
        }
        testA()
    end
    It returns
    Code:
      C1 Defined
      C2 Defined
      End definition
      hello from 2
    Kind regards

    nhb

    Comment


    • #3
      Thanks for your suggestion. I should have mentioned that I realize that I could have written if/else statements throughout the rest of the test() program. My question is really all about finding a way to avoid that. The reason is that I have a complex class definition and I want the class(es) to do all the work to control which functions are used when. That is, I want to insert a bunch of code that simply refers to C, rather than effectively doubling the size of my program by writing "if ...C1...; else ...C2...;" over and over. Make sense?

      Thanks again,
      Keith

      P.S. I think your solution is still calling both new() functions when the code is compiled--before the "C1 Defined" is printed to the screen--but it just so happens that your new() functions don't print anything so you can't tell that it's happening.

      Comment


      • #4
        Couldn't you use something like a conventional simple factory?

        .ÿ
        .ÿversionÿ15.1

        .ÿ
        .ÿclearÿ*

        .ÿ
        .ÿlocalÿline_sizeÿ`c(linesize)'

        .ÿsetÿlinesizeÿ72

        .ÿ
        .ÿmata:
        -------------------------------------------------ÿmataÿ(typeÿendÿtoÿexit
        >ÿ)ÿ--------------------------------------------------------------------
        :ÿmataÿsetÿmatastrictÿon

        :ÿ
        :ÿclassÿMyClassÿ{
        >ÿÿÿÿÿÿÿÿÿprotected:
        >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrealÿscalarÿx
        >ÿ}

        :ÿ
        :ÿclassÿMyClass1ÿextendsÿMyClassÿ{
        >ÿÿÿÿÿÿÿÿÿprivate:
        >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvoidÿnew()
        >ÿ}

        :ÿvoidÿfunctionÿMyClass1::new()ÿ{
        >ÿÿÿÿÿÿÿÿÿ"helloÿfromÿ1"
        >ÿ}

        :ÿ
        :ÿclassÿMyClass2ÿextendsÿMyClassÿ{
        >ÿÿÿÿÿÿÿÿÿprivate:
        >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿvoidÿnew()
        >ÿ}

        :ÿvoidÿfunctionÿMyClass2::new()ÿ{
        >ÿÿÿ"helloÿfromÿ2"
        >ÿ}

        :ÿ
        :ÿclassÿFactoryÿ{
        >ÿÿÿÿÿÿÿÿÿprivate:
        >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpointer(classÿMyClassÿscalar)ÿscalarÿget1()
        >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpointer(classÿMyClassÿscalar)ÿscalarÿget2()
        >ÿÿÿÿÿÿÿÿÿpublic:
        >ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpointer(classÿMyClassÿscalar)ÿscalarÿgetMyClass()
        >ÿ}

        :ÿpointer(classÿMyClassÿscalar)ÿscalarÿfunctionÿFactory::get1()ÿ{
        >ÿÿÿÿÿÿÿÿÿclassÿMyClass1ÿscalarÿmy_class
        >ÿÿÿÿÿÿÿÿÿreturn(&my_class)
        >ÿ}

        :ÿpointer(classÿMyClassÿscalar)ÿscalarÿfunctionÿFactory::get2()ÿ{
        >ÿÿÿÿÿÿÿÿÿclassÿMyClass2ÿscalarÿmy_class
        >ÿÿÿÿÿÿÿÿÿreturn(&my_class)
        >ÿ}

        :ÿpointer(classÿMyClassÿscalar)ÿscalarÿfunctionÿFactory::getMyClass(real
        >ÿÿscalarÿwhich)ÿ{
        >ÿÿÿÿÿÿÿÿÿifÿ(whichÿ==ÿ1)ÿreturn(get1())
        >ÿÿÿÿÿÿÿÿÿelseÿifÿ(whichÿ==ÿ2)ÿreturn(get2())
        >ÿÿÿÿÿÿÿÿÿelseÿÿ_error(3300,ÿ"Argumentÿneedsÿtoÿbeÿeitherÿ1ÿorÿ2")
        >ÿ}

        :ÿ
        :ÿ
        :ÿvoidÿfunctionÿtestA(realÿscalarÿwhatever)ÿ{
        >ÿ
        >ÿÿÿÿÿÿÿÿÿpointer(classÿMyClassÿscalar)ÿscalarÿm
        >ÿ
        >ÿÿÿÿÿÿÿÿÿclassÿFactoryÿscalarÿf
        >ÿ
        >ÿÿÿÿÿÿÿÿÿpragmaÿunsetÿm
        >ÿÿÿÿÿÿÿÿÿpragmaÿunusedÿm
        >ÿÿÿÿÿÿÿÿÿifÿ(!whatever)ÿmÿ=ÿf.getMyClass(1)
        >ÿÿÿÿÿÿÿÿÿelseÿmÿ=ÿf.getMyClass(2)
        >ÿ
        >ÿ}

        :ÿ
        :ÿtestA(0)
        ÿÿhelloÿfromÿ1

        :ÿ
        :ÿtestA(1)
        ÿÿhelloÿfromÿ2

        :ÿ
        :ÿend
        ------------------------------------------------------------------------

        .ÿ
        .ÿsetÿlinesizeÿ`line_size'

        .ÿ
        .ÿexit

        endÿofÿdo-file


        .

        Comment


        • #5
          Thanks Joseph! I had never seen this approach before, but it looks really promising. Thanks so much!

          Comment

          Working...
          X