/* * This program demonstrates ICI's set type. ICI sets should be * thought of as "unordered collections" of objects. Sets may * contain any type and any number of objects. */ /* * The standard ICI string() function, which turns any object into a * string, uses %g format when it converts a float object. This has * the effect of making our 3.0 below turn into "3" upon output making * it unrecognisable as an integer. In this "application" this is * inacceptable. * * Here we demonstrate how to override existing functions in ICI. We * write a wrapper around the standard string() function that uses an * alternate format for floating point values. * * The technique is well-defined. We simply define a function with the * same name as the one we wish to wrap. This function may be an * intrinsic or it may be user-supplied, it doesn't matter. Within * the function we perform whatever actions are required and when we * wish to call the original function we use ICI's `$' operator to * obtain the current definition of the function. The unary `$' * operator means "evaluate at parse time" and when applied to a * function name it performs a name lookup and returns the function * object with that name. When used within the wrapper function it * returns the previous object with the name as the wrapper function * is not yet defined - it is still being parsed. * * Functions may be wrapped in this manner any number of times. Each * wrapper function calls the previous definition of the function * which in turn calls the previous definition when it was parsed. */ extern string(any) { if (typeof(any) == "float") return sprintf("%f", any); /* * Call previous string function. */ return ($string)(any); } /* * Output a set of objects prefixed with a caption. * * This function uses a default value for one of its * parameters. Function parameters are names within the functions * "auto" scope level. To give parameters default values we define, * initialised, auto variables with the same names. When a function is * called its auto variables are copied into its auto scope level * followed by the function's actual parameters. If an initialised * variable has the same name as a parameter and a value is passed for * that parameter then it will overwrite the value in the auto scope * level. If no parameter is passed then no overwriting will occur and * the parameter will have the value of its initialiser. Simple. */ static output_set(caption, s, separator) { auto separator = " "; printf("%s\n", caption); auto el; forall (el in s) printf("%s%s", separator, string(el)); printf("\n"); } /* * Now define a couple of sets and show the results of the some of the * set operators on them. ICI also has sub-/super-set and proper * sub-/super-set operators. */ auto a, b; a = set(1, "two", 3.0); b = set(1, 2, 3); output_set("set a", a); output_set("set b", b); output_set("union, a + b", a + b); output_set("difference, a - b", a - b); output_set("difference, b - a", b - a); output_set("intersection, a * b", a * b); /* * To test for set membership you index the set object with * the object you're testing. The result is zero if the object * is not in the set or one if it is int he set. */ if (a["two"]) printf("The object \"two\" is in the set a\n"); /* * To remove objects from sets you assign zero or NULL to them. */ a["two"] = 0; if (a["two"]) printf("The object \"two\" is in the set a\n"); else printf("The object \"two\" is NOT in the set a\n");