Arrays

An array is a value type used to group a set of values under one variable name. The individual elements of the array are sequentially numbered, normally starting at zero unless otherwise configured.

Every element in the array can be used as a separate location with its own independent value that may have its own type. To refer to an element in the array, use square brackets enclosing the index or "subscript" value. If A is an array with five elements then A[0] refers to the first element, A[4] is the fifth.

You may use a variable, an expression, or a function as the subscript in your code, to manipulate an array or dynamically access different elements as operating conditions change.

Arrays may be declared either as static or dynamic. If a function specifies one type or the other as a parameter, it is essential that you use only that type.
Use a dynamic array in all cases where a static array is not specifically required.

In data processing, the WhileLoop() and DoLoop() functions certainly have their place, use the array functions when you can, because they are simpler and faster.

Dynamic Arrays

A dynamic array is created by first declaring the name, then using a script block to define the array size using the New() function:

[
  NameArray     { to be used as a one-dimensional array };
]

Init [  
  If 1 Main;
  [
     NameArray = New(10);
  ]
]

If you need to initialize the array after creating it, use an ArrayOp() function where possible.

Static Arrays

Static arrays have a fixed size. They are created, and optionally initialized when they are declared:

[
  AStaticArray[10] = 0;
]

Pointers and Arrays

A significant difference between static and dynamic arrays comes from the fact that the New() function returns a pointer. For example:

ArrayPtr = New(10);

Technically, to use an element from "ArrayPtr", you should dereference the element:

(*ArrayPtr)[0] = 5;

Fortunately, dereferencing is done automatically. The array index operator [ ] performs a pointer dereference operation if the variable before the operator has a pointer value. Thus, the expression above can also be written as follows:

ArrayPtr[0] = 5;

The preferred method is to allow the array index operator to perform the dereferencing of the pointer, because it improves the readability of the statement and executes faster.

Many functions specify whether an array parameter should be a static or a dynamic array - use care to provide the correct type.

You can pass a static array into a called module by reference. If you pass a static array into a launched module or subroutine, then only the first value will be passed in, because static arrays cannot be copied. For example, in the case of X = StaticArray; the value gets padded with [0] such that only the first element of the static array is passed in.

Whether or not a variable is passed by value or reference is a factor of the way the module is being called or launched, not a factor of the values in the variables.

For example, if you have the following code:

Main [
  MyCall(StaticArray);
  MyCall(DynamicArray);
  If Watch(1);
  [
    MyLaunch(StaticArray);
    MyLaunch(DynamicArray);
  ]
]

Assume that MyLaunch and MyCall both have one parameter called ArrayParm.

In MyCall, you can use ArrayParm to read and write all values in StaticArray and DynamicArray without issue. If it executes ArrayParm = 3, then DynamicArray would become a simple 3 (the array would be discarded), while StaticArray would remain an array, with only its first element set to 3 (this is the array padding mentioned earlier).

In MyLaunch, you can use ArrayParm to read and write all values of DynamicArray without issue, but not StaticArray. This is because MyLaunch takes its values by value, which means parameter values get copied into the local parameter values in the MyLaunch object. That works for DynamicArray, since DynamicArray contains a pointer value and pointer values can be copied. It doesn't work for StaticArray because static arrays don't get copied. Instead, it only copies the first element of the array into ArrayParm, so ArrayParm is neither an array nor a pointer to an array. If it executes ArrayParm = 3, then this does nothing but set its local ArrayParm parameter to 3; the original DynamicArray and StaticArray that was passed into the parameter are not modified by that assignment.

When you pass values by value (as happens when you pass values into a launched module), they are copied. You cannot pass a static array by value, but you can pass (a pointer to) a dynamic array. If you passed the dynamic array itself (i.e. *DynamicArray), then you would get the same behavior as when you pass a static array.

Multidimensional Arrays

An array that contains an array in each of its elements is defined as follows:

Data = New(5, 10);

This declaration may be read as "Data is an array of 5 elements each of which is an array of 10 values." This is a multidimensional array. Each dimension must appear as a number enclosed in square brackets [ ] that follows the variable name.

The left-most subscript is the "first" or "lowest" dimension of the array. The right-most subscript is the "last", "leading", or "highest" dimension.

It is possible but rather uncommon, to define starting and ending elements for a multidimensional array. To do so, use a static declaration. For example, the following declaration is for a two-dimensional array whose valid elements range from Data[10][-1] to Data[20][6]:

Data[10, 20][-1, 6];

Mismatched Array Dimensions

When using an array in your code, if the number of defined array dimensions does not match what your code attempts to address, the evaluation proceeds anyway, following certain rules. Specified array dimensions are matched to defined array dimensions starting from the right and working towards the left. If more array dimensions were specified than were defined, that array expression is called "over specified". (You are unlikely to encounter this.) If fewer array dimensions were specified than were defined, that array expression is called "under specified".

For under-specified arrays, missing leading (right-most) dimensions will default to zero. Consider the statement:

Data = New(2, 3);
Q = Data[1];

This expression would use Data[1][0]. Do not write code that relies on this - doing so is regarded as poor practice.

The ArrayOp Functions

When working with array values, many programmers will tend to use looping functions to step through the array items. While possible in VTScada, a better option exists for many common array tasks in the form of the ArrayOp functions; ArrayOp1 and ArrayOp2.

The parameters to ArrayOp1 include an array, a scalar value, and a numeric code designating a function to be applied. For example, to initialize an array:

(
  A     { an array };
  S = 2 { a value  };
)


Init [ 
  If 1 Main;

  [
    A = New(10); { A is a 10-element array}
    ArrayOp1(A[0]  { starting at element 0 of A },
             10    { for all 10 elements        },
              S    { take the value of S        },
              0    { set each element of A to S });
  ]
]

Other function codes could add the value of S to the current value of each element of A, or multiply, apply an AND, OR or exclusive OR (XOR), etc.

ArrayOP2 is similar, but in this case S is another array. The function code directs how elements of S should be applied to matching elements of A.

Many other array handling functions such as ArrayMax(), Sum(), Mean(), etc. are also available.

Practice with arrays

Create a drop list.

Working in a new script application, edit the Graphics module of AppRoot as follows. (Note that the droplist title will be black. You will need to change the window color to read it.)

Hint: You can save time by copying the droplist example from the documentation, then editing parameters to match what's shown here.

Graphics
[
  Options                         { an array of options                };
  Choice                          { user selection from options        };
  Index = 0                       { offset into options                };
  Msg = ""                        { display selection                  };
]


Init [ 
  If 1 Main;
  [
    Options = New(5);
    Options[0] = "Apple";
    Options[1] = "Cherry";
    Options[2] = "Mango";
    Options[3] = "Peach";
    Options[4] = "Pear";
  ]
]
Main [
  System.DropList(40, 60, 140, 40 { Boundaries of list }, 
                  Options { Data displayed }, 
                  "Choose one" { Title }, 
                  Index { Highlighted index },  
                  1, 1 { Focus ID, trigger }, 
                  FALSE { Editable field }, 
                  "Apple" { Starting value }, 
                  Choice { Variable to set }, 
                  FALSE { No bevel }, 
                  0 { Align top of list }); 
   Msg = Concat("Selection is ", Choice, " at index ", Index);
   ZText(150, 75, Msg, "<FFFFFF00>", 0 );
]
{ End of System\Graphics }
>

Create a grid list.

Create a new script application or replace the code in the last with the following. Again, you can save time by copying the sample GridList from the documentation then editing the parameters.

Graphics
[
  Titles                       { column titles               };
  Data                         { data array to fill grid     };
  DataForm                     { formatting code             };
]


Init [ 
  If 1 Main;
  [   
    { initialize the data array }
   Titles = New(5);
   Data   = New(5, 5);
 
   Titles[0] = "Col 0";
   Titles[1] = "Col 1";
   Titles[2] = "Col 2";
   Titles[3] = "Col 3";
   Titles[4] = "Col 4";
  
   Data[0][0] = "0";
   Data[1][0] = "1";
   Data[2][0] = "2";
   Data[3][0] = "3";
   Data[4][0] = "4";

   DataForm  = "%s";
  ]
]


Main [
  GUITransform(50, 200, 450, 50  { Reference rectangle         },
               1, 1, 1, 1, 1     { Scale Left, Bottom, Right, Top, Overall },
               0, 0, 1, 0        { No movement; visible; res   },
               0, 0, 0           { Not selectable              },
               System.GridList(Titles  { Titles array         },
                               Data   { Data array           },
                               DataForm { Cell format array   },
                               60     { Column widths array   },
                               3      { # Data rows           },
                               5      { # Data cols           },
                               15, 8  { Grid BGnd, Grid color },
                               1, 1 { Grid line width, line style},
                               30, 30 { Row/Title height      },
                               0, 0 { Horiz/Vert cell padding },
                               0, 0 { Horiz/Vert Scroll position },
                               1, 1 { Disable V/H scroll bars },
                               1    { Disable column sizing   },
                               1    { Disable Sorting         },
                               1    { Disable selected cell   },
                               0, 0 { Enable V/H grid lines  },
                               1    { LockFirstColumn         },
                               Invalid  { Sort                },
                               0       { SelectedRow         },
                               0      { SelectedColumn      },
                               System.DefFont { GridFontParm    }));
]
{ End of System\Graphics }

Experiment with ArrayOp1

Working with the same file as the last example, add a WinEdit control for data entry of a number and a droplist to select an operations code for use with the ArrayOp1 function. Each time that either changes, the second row of the array will be reset to match the first, then the scalar will be applied according to the selected operation.

Graphics
[
  Titles                        { an array of column titles          };
  Data                          { data to display in grid            };
  DataForm                      { formatting of data                 };
  Scalar = 0                    { scalar value                       };
  OpCodes                       { Array of OpCodes                   };
  OpCode = 0                    { Selected OpCode for ArrayOp fcn.   };
  OpCodeDesc                    { Array of OpCode descriptions       };
  nada                          { Text choice of OpCode. Not used    };
]


Init [ If 1 Main;
  [

   Titles = New(5);
   Data = New(5,5);
   OpCodes = New(8);
   OpCodeDesc = New(8);
 
   Titles[0] = "Col 0";
   Titles[1] = "Col 1";
   Titles[2] = "Col 2";
   Titles[3] = "Col 3";
   Titles[4] = "Col 4";
  
   Data[0][0] = "0";
   Data[0][1] = "1";
   Data[0][2] = "2";
   Data[0][3] = "3";
   Data[0][4] = "4";

   DataForm  = "%s";

   OpCodes[0] = 0;
   OpCodeDesc[0] = "A = S";
   OpCodes[1] = 1;
   OpCodeDesc[1] = "A + S";
   OpCodes[2] = 2;
   OpCodeDesc[2] = "A - S";
   OpCodes[3] = 3;
   OpCodeDesc[3] = "S - A";
   OpCodes[4] = 4;
   OpCodeDesc[4] = "A * S";
   OpCodes[5] = 5;
   OpCodeDesc[5] = "A / S";
   OpCodes[6] = 6;
   OpCodeDesc[6] = "S / A";
   OpCodes[7] = 7;
   OpCodeDesc[7] = "A % S";
   
   ArrayOp2(Data[1][0],Data[0][0],5, 0);
  ]
]


Main [
  GUITransform(50, 200, 450, 50   { Reference rectangle         },
               1, 1, 1, 1, 1       { Scale Left, Bottom, Right, Top, Overall },
               0, 0, 1, 0        { No movement; visible; res   },
               0, 0, 0           { Not selectable              },
               System.GridList(Titles  { Titles array         },
                                Data   { Data array           },
                                DataForm { Cell format array   },
                                80     { Column widths array   },
                                2      { # Data rows           },
                                5      { # Data cols           },
                                15, 8  { Grid BGnd, Grid color },
                                1, 1 { Grid line width, line style},
                                30, 30 { Row/Title height      },
                                0, 0 { Horiz/Vert cell padding },
                                0, 0 { Horiz/Vert Scroll position },
                                1, 1 { Disable V/H scroll bars },
                                1    { Disable column sizing   },
                                1    { Disable Sorting         },
                                1    { Disable selected cell   },
                                0, 0 { Enable V/H grid lines  },
                                1    { LockFirstColumn         },
                                Invalid  { Sort                },
                                0       { SelectedRow         },
                                0      { SelectedColumn      },
                                System.DefFont { GridFontParm    }));
    
  WinEditCtrl(170, 290, 220, 270, 0, Scalar, 1);      
    
  System.DropList(50, 265, 165, 250 { Boundaries of list }, 
                  OpCodeDesc { Data displayed }, 
                  "Choose an OpCode" { Title }, 
                  OpCode { Highlighted index }, 
                  1, 1 { Focus ID, trigger }, 
                  0 { Editable field }, 
                  OpCodeDesc[0] { Starting value }, 
                  nada { Variable to set }, 
                  0 { Bevel }); 
  If Watch(0, Scalar, OpCode);
  [
    ArrayOp2(Data[1][0], Data[0][0], 5, 0);
    ArrayOp1(Data[1][0], 5, Scalar, OpCodes[OpCode]);
  ]
]
{ End of System\Graphics }
>