ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Shift an array to the left

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Shift an array to the left

    I have an array of character elements.

    Code:
    dcl-s  MyArray    char(32)   dim(SomeNumber);
    I wast to copy all the elements, beginning with element 2, one position to the left. That is, copy element 2 to element 1, element 3 to element 2, etc.

    Speed is critical!!! It needs to run as fast as possible, or even faster.

    How would you do it?

  • #2
    Unless you know where the array resides in memory (via pointers) your best bet (for speed) will probably be a tight loop ;-)

    Comment


    • #3
      I think %subarr is what you are looking for.

      Comment


      • #4
        Originally posted by roverbyte View Post
        I think %subarr is what you are looking for.
        Thanks, Rover, but I'm not comfortable using %SUBARR. This is from the RPG manual:

        CAUTION:
        It is valid to use %SUBARR to assign part of an array to another part of the same array. However, if the source part of the array overlaps the target part of the array, unpredictable results can occur.

        %SUBARR (Set/Get Portion of an Array), partial arrays, array operations, %SUBARR (Set/Get Portion of an Array), built-in functions, %SUBARR(Set/Get Portion of an Array), array, Using partial arrays, run-time array, %SUBARR (Set/Get Portion of an Array), dynamic array

        Comment


        • #5
          For safety's sake I would prefer to code a loop.
          Code:
          for i = 2 to %elem(MyArray) by 1;
            MyArray[i-1]=MyArray[i];
          endfor;
          But if speed is as super essential as you say, I would use pointers to overlay a character variable and use substr

          Code:
          dcl-s MyArrayOverlay char(%size(MyArray)) based(@MyArray);
          dcl-s @MyArray pointer;
          
          @MyArray = %addr(MyArray);
          
          MyArrayOverlay = %substr(MyArrayOverlay:%len(MyArray(1))+1);  // Get substring starting with where element 2 should start and reset to that, so effectively shifting left by 1 element
          Disclaimer - I have not tested this code, I am sure it will not work first time

          Comment


          • #6
            I suspect the fastest would be to use the C function memcpy. You could also achieve the same thing by placing the array in a DS so that you could overlay the relevant portions. e.g.
            Code:
            dcl-ds  *N;
               MyArray   char(32)   Dim(10);
               First9  char(288)  Pos(1);
               Last9  char(288)  Pos(33);
            end-ds;
            
            First9 = Last9;
            Come to think of it you could use %Subst against the DS name (or use pointers of course)
            Code:
            dcl-ds  ArrayAsChar;
               MyArray   char(32)   Dim(10);
            end-ds;
            %Subst(ArrayAsChar: 1) = %Subst(ArrayAsChar:33);
            The most "RPG" way (assuming it works) would be:
            Code:
            %subarr(array:1:9) = %subarr(array:2);
            Sorry I don't have time to test if that does what is needed or some funky RPG thing. If it works it is possible the optimizer would see that as a memcpy candidate but I doubt it. In which case it would be faster than coding your own loop but not as fast as the earlier options. I see from your manual quote that the compiler doesn't guarantee it will work which seems odd because in general operations involving overlapping operands work just fine - much to my annoyance on occasion. Barbara would know why this is an issue.
            Last edited by JonBoy; September 20, 2019, 08:59 AM.

            Comment


            • #7
              Additional note. Just checked the memcpy definition and it too says that overlaps are unpredictable. If that is indeed the case then all of the techniques proposed (other than a one-by-one loop) would also have potential issues. There is hope however as memmove can be used instead and handles overlaps. See this page: https://www.ibm.com/support/knowledg...85.htm#memmove

              Comment


              • #8
                Originally posted by TedHolt View Post

                Thanks, Rover, but I'm not comfortable using %SUBARR. This is from the RPG manual:

                CAUTION:
                It is valid to use %SUBARR to assign part of an array to another part of the same array. However, if the source part of the array overlaps the target part of the array, unpredictable results can occur.

                https://www.ibm.com/support/knowledg...r.htm#bbsubarr
                How about:
                Code:
                MyArray1 = %subarr(MyArray :2);
                MyArray = MyArray1;
                I can't speak to the performance of this, since I've never had to do it.

                Comment


                • #9
                  Thanks for the ideas, everybody. At present I'm using a loop. Looking forward to trying memmove. Hoping Barbara M. gives us her input.

                  Comment


                  • #10
                    Here's the code to shift everything to the right (I use it when inserting into a sorted array).


                    memmove( %Addr(@FLD003($I+1))
                    : %Addr(@FLD003($I ))
                    : (@FLD003$ - $I + 1)*%Len(FLD003)
                    );


                    to shift to the left (untested)


                    memmove( %Addr(@FLD003(1))
                    : %Addr(@FLD003(2 ))
                    : (@FLD003$ - $I)*%Len(FLD003)
                    );


                    You will want to clear the last array element Clear @FLD003($I)

                    Comment


                    • #11
                      Originally posted by TedHolt View Post
                      I have an array of character elements.

                      Code:
                      dcl-s MyArray char(32) dim(SomeNumber);
                      I wast to copy all the elements, beginning with element 2, one position to the left. That is, copy element 2 to element 1, element 3 to element 2, etc.

                      Speed is critical!!! It needs to run as fast as possible, or even faster.

                      How would you do it?
                      What do you want to do with the 1st element? It just goes away?

                      Comment


                      • #12
                        Code:
                        Dcl-s MyArray Char(32) dim(100);                    
                        Dcl-S MyArray2 Char(32) Dim(99) based(MyArray2_Ptr);
                        Dcl-s Myarray2_Ptr Pointer Inz(%Addr(MyArray(2)));
                        Myarray2 is a 99 element array starting with the 2nd element of MyArray - essentially Myarray moved one spot to the left... can't beat the performance of that.

                        Comment


                        • #13
                          Originally posted by Rocky View Post

                          What do you want to do with the 1st element? It just goes away?
                          The last element will still be there, but the contents will be ignored until something comes along and modifies the last element with a new value. Think of it as a sort of queue, with values pulled off the front and new values added to the back. (That's not exactly the way it works, but it's similar enough.) When the first value is discarded, the others have to move left.

                          Comment


                          • #14
                            Are you wanting a queue?

                            Comment


                            • #15
                              Originally posted by Rocky View Post
                              Are you wanting a queue?
                              No.

                              Comment

                              Working...
                              X