/*
* Associative arrays must be explicitly declared using the likes of:
*/
local:
INT .ara1[ IP ]
export:
FLT .ara2[ STR, INT, DATE ] = { ? => 0.0 }
do Write_Line( "This is dynara.demo.Q" );
/*
* Note that any type that can be printed by a Write call can be used
* to index an associative array -- and that these arrays can have
* multiple dimensions.
* The initialization specification { ? => 0.0 } implies that any reference
* to an array element for specified indices that does not previously exist
* in the array will cause that element to be created with the indicated value of 0.0
* The utility of this will be seen shortly when array elements are updated.
*/
/*
* Creating new elements of an array:
*/
set .ara1[ ^12.1.135.16^IP ] = 15;
set .ara1[ ^135.27.111.4^IP ] = 38;
set .ara2[ "Tom", 23, ^1998-3-14^ ] = 45.6;
set .ara2[ "Harry", 47, ^1998-08-08^ ] = 45.6;
/*
* Printing out elements of an array:
*/
for_each_time [ .x1, .x2, .x3, .y1 ]
is_such_that( .ara2[ .x1, .x2, .x3 ] = .y1 )
{
do Write_Words( .x1, .x2, .x3, .y1 );
}
/*
* Computing the domain of an array = { .x : .ara1[ .x ] = ? }
*/
for_each_time [ .x ]
is_such_that( .ara1[ .x ] = ? )
{
do Write_Words( .x );
}
/*
* Computing the range of an array = { .y : .ara1[ ? ] = .y }
*/
for_each_time [ .y ]
is_such_that( .ara1[ ? ] = .y )
{
do Write_Words( .y );
}
/*
* Deleting an element of an array:
*/
set .ara1[ ^135.27.111.4^IP ] = ~;
/*
* Printing the total number of elements in an array:
*/
do Write_Words( ".ara1.Elt_Count =", .ara1.Elt_Count );
/*
* Testing for the existence of an array element for given indices:
*/
when( .ara1[ ^135.27.111.4^IP ] = ? )
{
do Exclaim_Words( "surprise: I thought this guy was deleted" );
}
when( .ara1[ ^135.27.111.4^IP ] = ~ )
{
do Write_Words( "yes, .ara1[ ^135.27.111.4^IP ] has been deleted" );
}
/*
* Deleting every element of an array:
*/
set .ara1 = {};
do Write_Words( ".ara1.Elt_Count =", .ara1.Elt_Count );
/*
Updating an array element that is known to exist:
*/
set .ara2[ "Harry", 47, ^1998-08-08^ ] += 100;
/*
* By default, it is an error to refer to an array element that doesn't exist.
* Consequently, in that case updating requires an explicit existence test
* if you can't guarantee that the element exists:
*/
set .qq = ^135.207.26.21^IP ;
when( .ara1[ .qq ] = ? )
{
set .ara1[ .qq ] += 100;
} else {
set .ara1[ .qq ] = 100;
}
/*
* However, if you specify an initializer like { ? => 0.0 }, then
* you can just say the following regardless of whether the element
* previously exists or not (PERL operates like this).
*/
set .ara2[ "Dick", 37, ^1998-09-01^ ] += 400.0 ;
/*
* This results in the array element getting the value 400.0 + 0.0
*/
/*
* TUPLES as values of associative array elements
*
* For each part compute the total number of orders, total quantity ordered and earliest order date
*/
{ local: TUPLE[ INT .cnt, FLT, DATE ] .part_stats[ STR .part ] = { ? => [ 0, 0, ^9999-01-01^ ] }
for_each_time [ .part, .qty, .dr ]
is_such_that(
there_is_an ORDER where(
Part_Nbr = .pno and
Quantity = .qty and
Date_Recd = .dr )
and there_is_a PART where(
Number = .pno and
Name = .part
)
){
when( .qty % 50 = 0 ) set .vbl_tracing = _true_;
else when( .vbl_tracing = _true_ ) set .vbl_tracing = _false_;
/* the following two cases are equivalent, although the one using $ is faster.
The $ is a macro expanding into the left-hand-side of the assignment.
.x#3 is the third component of TUPLE .x
*/
when( .qty % 2 = 0 )
{
set .part_stats[ .part ] = [ $#1+1, $#2+.qty, min($#3,.dr) ];
} else {
set [ .ord_cnt, .tot_qty, .min_dr ] = .part_stats[ .part ];
set .part_stats[ .part ] = [ .ord_cnt+1, .tot_qty+.qty, min(.min_dr,.dr) ];
}
}
for_each_time [ .part, .order_cnt, .tot_qty, .min_date_received ]
is_such_that(
.part_stats[ .part ] = [ .order_cnt, .tot_qty, .min_date_received ]
){
when( .tot_qty % 20 = 0 ) set .vbl_tracing = _true_;
else when( .vbl_tracing = _true_ ) set .vbl_tracing = _false_;
do Write_Words( .part, .order_cnt, .tot_qty, .min_date_received );
}
do Write_Line( 25 *"=" );
/* which parts were ordered exactly 4 times? (and list them in order) */
for_each_time [ .part ] is_such_that(
.part Is_The_Next_Where( .part_stats[ .part ] = [ 4, ?, ? ] ) in_lexico_order
){ do Write_Line( .part ); }
do Write_Line( 25 *"=" );
/* list all the part statistics in the alphabetic order of the part names */
for_each_time [ .part ] is_such_that(
.part Is_The_Next_Where( .part_stats[ .part ] = [ ?, ?, ? ] ) in_lexico_order
){
do Write_Words( .part_stats[.part]#1, .part_stats[.part]#2, .part_stats[.part]#3 );
}
do Write_Line( 25 *"=" );
/* as before, list all the part statistics in the alphabetic order of the part names
but more efficient due to the use of a VBL VBL
*/
for_each_time [ .part, ..ps ] is_such_that(
.part Is_The_Next_Where( .part_stats[ .part ] = [ ?, ?, ? ] ) in_lexico_order
and .ps = part_stats[.part]
){
do Write_Words( .part, ..ps#1, ..ps#2, ..ps#3 );
}
do Write_Line( 25 *"=" );
do Write_Words( .part_stats[ "chisel" ]#1, .part_stats[ "chisel" ]#2 );
set .part_stats[ "chisel" ]#1 = 0;
set .part_stats[ "chisel" ]#2++ ;
/* Using VBL VBLs */
set .psv = part_stats[ "chisel" ];
do Write_Words( ..psv#1, ..psv#2, ..psv#3 );
/* Skolem assignments are ignored */
set .part_stats[ "chisel" ] = [ ?, 47, ? ];
do Write_Words( "1985-04-07 =", .part_stats[ "chisel" ]#3 );
do Write_Words( "47.0 =", .part_stats[ "chisel" ]#2 );
do Write_Words( "0 =", .part_stats[ "chisel" ]#1 );
}
/*
* Finally, arrays can be imported/exported and passed as arguments
* to functions and procedures:
*/
set .vbl_tracing = _true_;
do Print_Array( .ara1 );
do Print_Ara2;
define PROC( INT .ara0[ IP ] ) Print_Array
{
do Write_Line( "Here is an array" );
for_each_time [ .x, .y ]
is_such_that( .ara0[ .x ] = .y )
{
do Write_Words( .x, .y );
}
}
global_defs:
define PROC task: Print_Ara2
{
import: FLT .ara2[ STR, INT, DATE ] = { ? => 0.0 }
do Write_Line( "Here is ara2" );
for_each_time [ .x1, .x2, .x3, .y1 ]
is_such_that( .ara2[ .x1, .x2, .x3 ] = .y1 )
{
do Write_Words( .x1, .x2, .x3, .y1 );
}
}
/*
* The body of this email is a valid Cymbal program.
* Try executing it and see what happens!
*/