|Powered by QM on a Rpi server|
KnowledgeBase 00044: Multivalue Functions
This article was originally published as a Tip of the Week.
The QMBasic language includes a powerful set of functions that operate on multivalued data, repeating some operation for each dynamic array element. These functions allow developers to construct simple solutions to apparently complex problems.
The functions described here are of most use in dictionary I-type expressions but can also be used in programs.
A Simple Example
If we have two dynamic arrays, delimited by value marks
A 4 vm 6 vm 1 B 2 vm 3 vm 5QM and several other multivalue implementations allow us to perform arithmetic operations such as adding A and B
A+B 6 vm 9 vm 6where the addition operator has worked on each value pair in turn. Some systems provide this functionality with a function named ADDS()
The concatenation operator (: or CAT) does not work in this way. If we have
A P vm Q vm R B 1 vm 2 vm 3evaluating
C = A : Bdoes exactly what we have asked, concatenating the two strings, giving
P vm Q vm R1 vm 2 vm 3What we probably wanted was
P1 vm Q2 vm R3and we can achieve this with the CATS() function.
The SPLICE() function is similar but inserts a single valued separator between the items in each pair
SPLICE(A,'**',B) P**1 vm Q**2 vm R**3
There are many such functions and, with just a few exceptions, their names are formed by adding an S to the equivalent single valued operator or function name, effectively pluralising it. Some work on two dynamic arrays, combining corresponding elements (ADDS, SUBS, MULS, DIVS, CATS, etc). Others apply some operation to each element of a single dynamic array. For example, the OCONVS() function can be used to convert each element of a multivalued list of internal form dates to external form.
Multivalued Relational Operators
The multivalued relational operators (EQS, NES, LTS, LES, GTS, GES) perform a comparison of each element in a pair of dynamic arrays, returning a new dynamic array of the same structure with each element set to 1 or 0 according to the result of the comparison. For example, if we have
A 4 vm 6 vm 1 B 2 vm 3 vm 5we could use GTS(A, B) to determine which element is greater in each pair.
GTS(A,B) 1 vm 1 vm 0
The true power of these functions only becomes apparent when they are used in combination. We could, for example, use
SUM(GTS(A,B))to determine how many elements of A are greater than B.
Multivalued Conditional Operations
One of the most powerful multivalue functions is IFS() which takes a multivalued list of true/false values and two other dynamic arrays. It then builds a new dynamic array in which the true/false list is used to determine which of the other dynamic arrays is used to form the new value.
A 4 vm 6 vm 1 B 2 vm 3 vm 5 GTS(A,B) 1 vm 1 vm 0 IFS(GTS(A,B),A,B) 4 vm 6 vm 5This construct has returned a dynamic array formed from the greater value in each pair.
But What If...
Returning to the simple case of adding two dynamic arrays, what will happen if one has fewer values than the other?
A 4 vm 6 vm 1 B 2 vm 3The language defines that the missing item is treated as zero, resulting in
A+B 6 vm 9 vm 1except if it is the divisor in a division operator when it is treated as one.
There are times when what we want to do is to carry forward the last value to substitute for the missing value. We can do this with the REUSE() function.
A+REUSE(B) 6 vm 9 vm 4The REUSE() function simply keeps using the last value until the expression is completely resolved. If we do not know which of the two lists has fewest items, we can even do
C = REUSE(A) + REUSE(B)The most obvious case of needing REUSE() is when one of the items to be processed is single valued. We could, for example, add 20% tax to a multivalued list of prices with
VALUE = PRICE * REUSE(1.2)Simply writing
VALUE = PRICE * 1.2would not work because, as explained above, the right hand side of the multiplication would be taken as zero for the second and subsequent values in PRICE.
A More Complex Example
Imagine we have a sales processing system with an order history file in which field BRANCH contains the single valued branch number at which an order was placed. Field ITEMS contains a multivalued list of items, each formed from a product number and size code separated by an asterisk. A sample record might be
Branch Items 15986 6379*12 9824*14 2537*9 2732*3We want to display the part description alongside each item. These are stored in a separate stock file which is keyed by the branch and product number, separated by a hyphen. We can form the stock file id using
SPLICE(REUSE(BRANCH),'-',FIELDS(ITEMS,'*',1))This could then be used in a TRANS() function to retrieve the product descriptions.
Branch Items.. Prod...... Description 15986 6379*12 15986-6379 Coat, ladies 9824*14 15986-9824 Jacket, mens, black weatherproof 2537*9 15986-2537 Boots, mens, hiking 2732*3 15986-2732 Boots, child
00094: Dictionary I-Type Records