Announcement

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

  • Appending to structure vector

    Hi all,
    I am tip-toing into the Mata sea and have a question about working with a vector of structures that I hope to dynamically resize.

    My .mata file is below. When I un-comment the failing line, here is the error I get:

    Code:
    : myScale.scale_items = (myScale.scale_items,scale_item())   // does not work
    type mismatch:  ,:  struct scale_item found where transmorphic expected
    Stata tech support confirms that the join operator (,) cannot deal with this, but that creating an instance of the struct, (x in my code below) allows the join. Their advice was to pre-allocate the vector to the length I will need. I can probably make that happen if there is no other way, but I'd rather build it dynamically.

    So my real question is whether I am thinking about this wrong in some way?

    Thanks in advance for any advice on this not-so-well formed question.

    Cheers,
    Nick Winter

    Here is my .mata file:
    Code:
    clear *
    set matastrict on
    mata:
    struct scale_item {
       string              vector  stata_vars
       real                vector  num
       real                vector  denom
       real                vector  omit
    }
    
    struct scale {
       string              scalar name
       struct scale_item   vector scale_items  // vector of items
    }
    
    myScale = scale()
    myScale.name = "This is my scale"
    myScale.scale_items=scale_item()   // ititialize as 1 empty item
    
    length(myScale.scale_items)
    myScale.scale_items[1].stata_vars=("a1a")
    
    // now add second item to the scale
    // The following does not work:
    // myScale.scale_items = (myScale.scale_items,scale_item())
    
    // but this does:
    x = scale_item()
    myScale.scale_items = (myScale.scale_items,x)
    
    length(myScale.scale_items)
    myScale.scale_items[2].stata_vars=("a1b")
    liststruct(myScale)
    
    end

  • #2
    A little kludgy, but you can do something like this.
    Code:
    myScale.scale_items = myScale.scale_items, myScale.scale_items[1]
    
    myScale.scale_items[2].stata_vars = ("a1b")
    Otherwise, pointers.
    Code:
    version 18.0
    
    clear *
    
    mata:
    
    struct Element {
        real vector Values
    }
    
    struct Container {
        pointer(struct Element scalar) vector E
    }
    
    o = Container(1)
    
    o.E = &Element(1)
    
    o.E = o.E, &Element(1) // <= Here
    
    length(o.E)
    
    end
    
    exit
    Or classes, which is the way I'd go inasmuch as you're asking for stack (push-pop) behavior.

    Comment


    • #3
      You have to define a struct before adding it.
      This is not what is happening in
      Code:
      myScale.scale_items = (myScale.scale_items,scale_item())
      where "scale_item()" isn't set/defined. It is a struct definition.
      When you define x in
      Code:
      x = scale_item()
      you can use x in your code
      Last edited by Niels Henrik Bruun; 22 Jun 2023, 01:16.
      Kind regards

      nhb

      Comment


      • #4
        I don't catch your meaning on "define", "set/defined".

        For example, how does the following "set" or "define" a struct any differently from that which gave the problem in #1 above?

        .ÿ
        .ÿversionÿ18.0

        .ÿ
        .ÿclearÿ*

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

        .ÿsetÿlinesizeÿ80

        .ÿ
        .ÿmata:
        -------------------------------------------------ÿmataÿ(typeÿendÿtoÿexit)ÿ------
        :ÿ
        :ÿstructÿDummyÿ{}

        :ÿ
        :ÿeltype(Dummy())
        ÿÿstruct

        :ÿ
        :ÿorgtype(ÿ(ÿDummy(),ÿDummy()ÿ)ÿ)
        ÿÿrowvector

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

        .ÿ
        .ÿsetÿlinesizeÿ`line_size'

        .ÿ
        .ÿexit

        endÿofÿdo-file


        .


        I think that it's just a peculiarity of structs as member variables of structs.

        You can append numeric, string and pointer elements to vectors that are member variables of structs, just not struct elements.

        (Probably applies to class elements, as well.)

        Comment


        • #5
          Joseph Coveney I am by no means a coding expert.
          Your example only shows that you can get meta information on the struct. Sorry, I do not know the right words.
          Code:
          . mata mata clear
          
          . mata:
          : struct strct {
          >    string scalar strcontent
          > }
          
          : // You can do this
          : tst = strct()
          : tst.strcontent = "hi"
          : liststruct(tst)
          1  structure of 1 elements
          1.1  1 x 1 string = "hi"
          
          : // You can do this
          : liststruct(strct())
          1  structure of 1 elements
          1.1  1 x 1 string = ""
          
          : // You can't do this, ie working with content of the struct strct() directly
          : (strct()).strcontent
          type mismatch:  exp.exp:  transmorphic found where struct expected
          (0 lines skipped)
          r(3000);
          To me, it seems like the struct isn't truly applicable before it is assigned to a variable
          Last edited by Niels Henrik Bruun; 22 Jun 2023, 02:38.
          Kind regards

          nhb

          Comment


          • #6
            "just not struct elements" → "just not struct elements from the struct's generating function"

            That is, the peculiarity is that you cannot append (extend the array) using the automatically created generating function of the same name.

            But you can populate the vector originally using the generating function, for example, the following runs without any problem.
            Code:
            version 18.0
            
            clear *
            
            mata:
            
            struct Dummy {}
            
            struct Container {
                struct Dummy vector D
            }
            
            c1 = Container()
            c2 = Container()
            
            c1.D = Dummy()
            
            c2.D = Dummy(), Dummy()
            
            end
            
            exit
            Last edited by Joseph Coveney; 22 Jun 2023, 02:50.

            Comment


            • #7
              Joseph Coveney, I do not understand
              "just not struct elements" → "just not struct elements from the struct's generating function"
              .
              And do not think your example contradicts what I was trying to say.
              Again, I'm no expert in coding.
              This remains a fun fact of Mata.
              Kind regards

              nhb

              Comment


              • #8
                Originally posted by Niels Henrik Bruun View Post
                Code:
                : // You can't do this, ie working with content of the struct strct() directly
                : (strct()).strcontent
                Okay, I see what you mean now, thanks.

                But I don't think that that is what the OP's problem is.

                He isn't trying to work with the contents of the struct that he's trying to add.

                He's just trying to extend the length of the struct vector that's a member variable of another struct. And you can't do that with the generating function, like you can initially populate the member variable. That's the peculiarity that I was getting at.

                Originally posted by Niels Henrik Bruun View Post
                This remains a fun fact of Mata.
                Agreed.

                Comment


                • #9
                  Originally posted by Joseph Coveney View Post
                  He's just trying to extend the length of the struct vector that's a member variable of another struct. And you can't do that with the generating function,
                  FWIW: the complexity of structs being member variables of other structs is not neccessary to reproduce the behavior:

                  Code:
                  . mata
                  ------------------------------------------------- mata (type end to exit) -------------------------------------------------------------------------------------------------------------------------
                  : struct Foo { }
                  
                  : foo = Foo()
                  
                  : foo = (foo, Foo())
                  type mismatch:  ,:  struct Foo found where transmorphic expected
                  r(3000);
                  
                  : end
                  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

                  Comment


                  • #10
                    Originally posted by daniel klein View Post
                    FWIW: the complexity of structs being member variables of other structs is not neccessary to reproduce the behavior:
                    Interesting, thanks. I'm not very familiar with structs.

                    Comment


                    • #11
                      Well, at least in this respect, classes are no different:

                      Code:
                      . mata
                      ------------------------------------------------- mata (type end to exit) -------------------------------------------------------------------------------------------------------------------------
                      : class Foo {}
                      
                      : foo = Foo()
                      
                      : foo = (foo, Foo())
                      type mismatch:  ,:  class found where transmorphic expected
                      r(3000);
                      
                      : end
                      ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                      I have no idea about the underlying technical details, though.

                      Comment


                      • #12
                        Originally posted by daniel klein View Post
                        Well, at least in this respect, classes are no different:
                        . . . I have no idea about the underlying technical details, though.
                        Yeah, I was told years ago by Technical Services that Mata's classes are built upon structs, and so that's why I made the parenthetical comment at the end in #4 above.

                        Likewise, no idea about the technical details behind the scenes.

                        To paraphrase Niels Henrik, "Just another fun fact of Mata."

                        Comment


                        • #13
                          Thank you all! It looks like I'm not missing something obvious, and I can do what I want by appending myScale.scale_items[1] and then setting/resetting its elements.

                          Originally posted by Joseph Coveney View Post
                          Or classes, which is the way I'd go inasmuch as you're asking for stack (push-pop) behavior.
                          That said, any pointers (pun intended) on how classes might facilitate a cleaner solution?

                          Another thing that occurred to me is to use an AssociativeArray with integer indices; feels like overkill though.

                          Comment


                          • #14
                            It wouldn't be less verbose than the kludge, and it wouldn't make sense to go the extra effort if you are going to be working exclusively in the interactive mode. But it would be more convenient not to have to think about the nitty-gritty of adding elements each time, especially if you're going to be doing it a lot, if you converted the scale struct to a class with an add-element method that takes care of all of the details for you and even fills in the new element's contents while it's at it.

                            Comment

                            Working...
                            X