Back
Using Associative Arrays In Cymbal
/*
 *  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!
 */

Back