Data Structures¶
One of the features that elevates Unicon from a high level language to a very high level language is the range of structured data types.
Unicon mutable data types¶
List (arrays)¶
Unicon lists are mutable, ordered collections of data. Each element can be
any datatype. Lists are created with square bracket syntax, or the
list function. list()
accepts an optional size parameter,
followed by an optional default initial value.
&null is the default initial value if not specified.
Appending to a list is supported with the ||| (list concatenation) operator, which can be augmented with assignment (as is the case for almost all Unicon operators).
List functions include stack and queue like operations, along with arbitrary positioning.
- push, pop (front of the list)
- put, pull (end of the list)
- insert(L, i, x) will insert x at position i
- delete (L, i,...) will delete the elements at position i and other given indexes.
Subscript indexing of a list is supported with square bracket syntax.
List[index]
. This is where Unicon lists, and arrays
look very similar. Lists are one-dimensional vectors. Subscripting will fail
if the requested element(s) are out of current list size range.
Create new list sections with square bracket and colon syntax,
L[start:end]
. This syntax creates new lists.
Subscript range access creates new lists. List sections are not variable;
the original list is not modifiable using sections. Where as Groceries[1]
:= "Milk"
modifies the Groceries
list, Groceries[1:3] := "Milk"
is
invalid; the Groceries[1:3]
section expression is not a variable and
assignment does not apply.
The get function will remove and return multiple elements from the head of a list.
list comprehension¶
While the normal list creation syntax is [v1, v2,...]
, Unicon also
features list comprehension. The syntax is [: expr :]
, all values from
the expression become values in the returned list.
#
# list-operations.icn, some simple List examples
#
link fullimag
procedure main()
local L1, L2
# L1 is 5 elements of 0
L1 := list(5, 0)
L2 := ["a", "b", "c", "c", 1, 1.0, ["sublist"]]
# lists are ordered, mutable structures
write(fullimage(L1), " size of L1: ", *L1)
write(fullimage(L2), " size of L2: ", *L2)
# Indexing
write("\nL2[3]: ", L2[3])
write("L2[1:3]: ", fullimage(L2[1:3]))
# Concatenation
write("\nL1 ||| L2")
write(fullimage(L1 ||| L2))
# Membership
if member(L1 ||| L2, "d") then write("\nd is in the list")
# Insertion
write("\nInsertion")
write(fullimage(insert(L2, 1, "first")))
# List comprehensions
write("\nComprehension with [: 1 to 10 :]")
write(fullimage([: 1 to 10 :]))
write("Comprehension with [: 1 to (1 to 5) :]")
write(fullimage([: 1 to (1 to 5) :]))
end
prompt$ unicon -s list-operations.icn -x
[0,0,0,0,0] size of L1: 5
["a","b","c","c",1,1.0,<2>["sublist"]] size of L2: 7
L2[3]: c
L2[1:3]: ["a","b"]
L1 ||| L2
[0,0,0,0,0,"a","b","c","c",1,1.0,<2>["sublist"]]
Insertion
["first","a","b","c","c",1,1.0,<2>["sublist"]]
Comprehension with [: 1 to 10 :]
[1,2,3,4,5,6,7,8,9,10]
Comprehension with [: 1 to (1 to 5) :]
[1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
Set¶
Unicon Set data is an unordered, mutable data structure that follows set theory. Sets are initialized with the Set function.
Union, intersection, difference, insertion and membership testing are all supported. Set elements are unique within a set.
Cset is an internally efficient form of character sets.
#
# set-operations.icn, some simple Set data examples
#
link fullimag
procedure main()
S1 := set("a", "b", "c", "c", "d")
S2 := set("d", "e", "f", "g", "h")
# sets have a uniqueness propery, "c" is only held once
write("Uniqueness")
write(fullimage(S1))
write(fullimage(S2))
# Union
write("\nUnion S1 ++ S2")
write(fullimage(S1 ++ S2))
# Intersection
write("\nIntersection S1 ** S2")
write(fullimage(S1 ** S2))
# Difference
write("\nDifference S1 -- S2")
write(fullimage(S1 -- S2))
write("Difference S2 -- S1")
write(fullimage(S2 -- S1))
# Membership
if member(S1 ** S2, "d") then write("\nd is in both sets")
# Insertion
write("\nInsertion")
write(fullimage(insert(S1, "i")))
end
prompt$ unicon -s set-operations.icn -x
Uniqueness
set("a","b","c","d")
set("d","e","f","g","h")
Union S1 ++ S2
set("a","b","c","d","e","f","g","h")
Intersection S1 ** S2
set("d")
Difference S1 -- S2
set("a","b","c")
Difference S2 -- S1
set("e","f","g","h")
d is in both sets
Insertion
set("a","b","c","d","i")
Table¶
Unicon tables are associative arrays, key-value pairs. Keys can be any type, values can be any type. Created with the Table function, access is by square bracket subscripting (where the subscript is a key specifier).
Structure operators such as * size and ? random element operators (returning a random table value, not a key) are also built to work with tables.
The generate elements operator, generates values not keys.[1] The function key(T) generates the keys of a table.
Subscripted access will return a default value if lookup does not find the key. The default value is the optional argument of the Table declaration function, or &null.
The member function succeeds if the given key exists in the table, or fails otherwise.
#
# table-operations.icn, some simple table() examples
#
link ximage
procedure main()
T1 := table() # the default default is &null
T2 := table(0) # default element is 0
# tables are mutable, key value pair structures
# accessing an unknown key returns the table default value
# setting an unknown key inserts the key-value pair
write("T1[\"unknown\"]: ", image(T1["unknown"]))
write("T2[\"unknown\"]: ", image(T2["unknown"]))
T1["unknown"] := "now known"
write("T1[\"unknown\"]: ", image(T1["unknown"]))
# T2 elements default to 0, math is ok for unknown elements
write("T2[\"unknown\"] + 42: ", image(T2["unknown"] + 42))
write("\nximages:")
write(ximage(T1))
write(ximage(T2))
# Access is by square bracket operator
# keys can be any type, values can be any type
T2[3] := "42"
T2["3"] := 42
write("\nElements:\nT2[3]: ", image(T2[3]))
write("T2[\"3\"]: ", image(T2["3"]))
# Size, note: T2["unknown"] was not assigned during the computation
write("size of T1: ", *T1)
write("size of T2: ", *T2)
# element generator gives values
write("\nT2 values")
every write(image(!T2))
# key() function, generates the keys
write("\nT2 keys")
every write(image(key(T2)))
write(ximage(T2))
# Membership
if member(T2, 3) then write("\ninteger 3 is a key in T2")
# removal
v := delete(T2, 3)
write("\ndelete T2[3], return value is: ", type(v))
# membership test will now fail, no output written
if member(T2, 3) then write("\ninteger 3 is a key in T2")
write("T2 keys after delete")
every write(image(key(T2)))
# table values can also be functions
fT := table()
fT["add"] := adder
operation := "add"
# add two values, invoked from table key
write("\noperation ", operation, " returns ", fT[operation](1,2))
write(ximage(fT))
end
#
# adder function
#
procedure adder(a,b)
return a + b
end
prompt$ unicon -s table-operations.icn -x
T1["unknown"]: &null
T2["unknown"]: 0
T1["unknown"]: "now known"
T2["unknown"] + 42: 42
ximages:
T1 := table(&null)
T1["unknown"] := "now known"
T2 := table(0)
Elements:
T2[3]: "42"
T2["3"]: 42
size of T1: 1
size of T2: 2
T2 values
"42"
42
T2 keys
3
"3"
T2 := table(0)
T2[3] := "42"
T2["3"] := 42
integer 3 is a key in T2
delete T2[3], return value is: table
T2 keys after delete
"3"
operation add returns 3
T6 := table(&null)
T6["add"] := procedure adder
Todo
more on tables
[1] | The fact that the ! operator generates values and not keys for table data was deemed an early Icon language design mistake. Before Icon v7, you needed to convert a table to a paired list to get at keys. The key function was added to get around this design flaw. Key value pair associative data tables were originally in SNOBOL. This extremely useful Unicon data structure has a long history, |
Record¶
Records are fixed length[2], named field structures, created with a globally named initializer defined with the record reserved word at compile time. Unicon record structures can also be defined at runtime with the constructor function.
Compile time record
definitions occur outside of procedures in source
code. The initializer is global to all procedures within a compile unit.
constructor definitions are contained within the same scope as the
variable used to store the return value from the function.
record
data plays a background role in the object oriented features of
Unicon, but programmers don’t need to worry about that, details managed by the
compiler.
#
# record-operations.icn, demonstrate records
#
link ximage
# record initializers are in the global name space
record sample(field1, field2, field3)
procedure main()
# records are created by the initializer
R1 := sample("value1", "value2", 3)
# record field access uses the dot operator
write("R1.field1: ", R1.field1)
write("R1.field2: ", R1.field2)
write("R1.field3: ", R1.field3)
# Unicon also allows runtime creation of record constructors
Rcon := constructor("runtime", "f1", "f2", "f3")
# The new "runtime" initializer is now available as Rcon
R2 := Rcon("v1", "v2", R1)
write("\nR2.f1: ", R2.f1)
write("R2.f2: ", R2.f2)
# like all Unicon structures, fields can be arbitrarily nested
write("R2.f3.field1: ", R2.f3.field1)
write("type of R2: ", type(R2))
write("type of R2.f3: ", type(R2.f3))
end
examples/record-operations.icn
prompt$ unicon -s record-operations.icn -x
R1.field1: value1
R1.field2: value2
R1.field3: 3
R2.f1: v1
R2.f2: v2
R2.f3.field1: value1
type of R2: runtime
type of R2.f3: sample
[2] | Records are fixed length in terms of always having the same number of named fields. The field elements themselves can be any data type, of variant sizes. This is not the same as a COBOL fixed length record, which always have the same number of bytes in each field and in each record hierarchy. |
Index | Previous: Datatypes | Next: Expressions