Collection Types
The Collection Types
There are five collection types. Four of them—the Set, OrderedSet, Bag, and Sequence types—are concrete types and can be used in expressions. The fifth, the Collection type, is the abstract supertype of the other four and is used to define the operations common to all collection types.
The four concrete collection types are defined as follows:
A Set is a collection that contains instances of a valid OCL type. A set does not contain duplicate elements; any instance can be present only once. Elements in a set are not ordered.
An OrderedSet is a set whose elements are ordered.
A Bag is a collection that may contain duplicate elements. A bag is typically the result of combining navigations. Elements in a bag are not ordered.
A Sequence is a bag whose elements are ordered.
(Note that a value of type Sequence or OrderedSet is ordered and not sorted.)
Set { 1 , 2 , 5 , 88 }
Set { 'apple' , 'orange', 'strawberry' }
OrderedSet { 'apple' , 'orange', 'strawberry', 'pear' }
Sequence { 1, 3, 45, 2, 3 }
Sequence { 'ape', 'nut' }
Bag {1 , 3 , 4, 3, 5 }
Sequence{ 1..(6 + 4) }
Sequence{ 1..10 }
-- are both identical to
Sequence{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
Collection Type Expressions
When the type of an element is a collection, this can be indicated using the words Set, OrderedSet, Bag, or Sequence, and the type of the elements of the collection between rounded brackets, as shown in the following examples:
Set(Customer)
Sequence(Set(ProgramPartners))
OrderedSet(ServiceLevel)
Bag(Burning)
Collection Operations
All operations on collections are denoted in OCL expressions using an arrow
The operation following the arrow is applied to the collection before the arrow
This practice makes it possible for the user to define operations in the model that have the same names as the standard operations
The user-defined operation is taken when it follows a dot
The standard operation is taken when it follows an arrow
Collection operations do not change a collection, but they may result in a new collectioncontext LoyaltyProgram
inv: self.participants->size()
Treating Instances as Collections
Because the OCL syntax for applying collection operations is different from that for user-defined type operations, you can use a single instance as a collection. This collection is considered to be a set with the instance as the only element.
As an instance: | As a collection: |
context Membership inv: account.isEmpty() |
context Membership inv: account->isEmpty() |
Collections of Collections
When a collection is inserted into another collection, the resulting collection is automatically flattened; the elements of the inserted collection are considered direct elements of the resulting collection.
Set {Set{1,2}, Set{3,4}, Set{5,6}}
Set { 1, 2, 3, 4, 5, 6 }
(flattened)
Operations on Collection Types
Standard operations on all collection types
In the invariant, you specify that the actual service level of a membership must be one of the service levels of the program to which the membership belongs:
context Membership
inv: programs.levels ->includes(currentLevel)
The following invariant specifies that the available services for a service level must be offered by a partner of the loyalty program to which the service level belongs:
context ServiceLevel
inv:program.partners->includesAll(self.availableServices.partner)
Operations with Variant Meaning
Collection operations with variant meaning
The equals and notEquals Operations
For sets, this means that all elements present in the first set must be present in the second set and vice versa
For ordered sets, an extra restriction specifies that the order in which the elements appear must also be the same
For two bags to be equal, not only must all elements be present in both, but the number of times an element is present must also be the same
For two sequences, the rules for bags apply, plus the extra restriction that the order of elements must be equal
The including and excluding Operations
The including operation results in a new collection with one element added to the original collection
For a bag, this description is completely true
If the collection is a set or ordered set, then the element is added only if it is not already present in the set
If the collection is a sequence or an ordered set, the element is added after all elements in the original collection
The excluding operation results in a new collection with an element removed from the original collection
From a set or ordered set, it removes only one element
From a bag or sequence, it removes all occurrences of the given object
The flatten Operation
The flatten operation changes a collection of collections into a collection of single objects
Set {Set{1,2}, Set{2,3}, Set{4,5,6}}
Set { 1, 2, 3, 4, 5, 6 }
(flatten)
Bag {Set{1,2}, Set{1,2}, Set{4,5,6}}
Bag { 1, 1, 2, 2, 4, 5, 6 }
(flatten)
When the flatten operation is applied to a sequence or ordered set, the result is a sequence or ordered set (respectively)
Sequence {Set{1,2}, Set{2,3}, Set{4,5,6}}
Sequence { 2, 1, 2, 3, 5, 6, 4 }
(flatten)
The asSet, asSequence, asBag, and asOrderedSet Operations
Instances of all four concrete collection types can be transformed into instances of another concrete collection type
Applying asSet on a bag or asOrderedSet on a sequence means that of any duplicate elements, only one remains in the result
Applying asBag on a sequence or asSet on an ordered set means that the ordering is lost
Applying asOrderedSet or asSequence on a set or bag means that the elements are placed randomly in some order in the result
Applying the operation on the same original twice does not guarantee that both results will be equal
The union Operation
Combines two collections into a new one
The union of a set with a set will result in a set; any duplicate elements are added to the result only once
Combining a set with a bag (and vice versa) results in a bag
A sequence or ordered set may not be combined with either a set or a bag, only with another ordered collection
The intersection Operation
Results in another collection containing the elements in both collections
Valid for combinations of two sets, a set and a bag, or two bags
Not for combinations involving a sequence or ordered set
The minus Operation
Results in a new set containing all the elements in the set on which the operation is called, but not in the parameter set
This operation is defined for sets and ordered sets
When applied to an ordered set, the ordering remains
Set{1,4,7,10} - Set{4,7} = Set{1,10}
OrderedSet{12,9,6,3} - Set{1,3,2} = OrderedSet{12,9,6}
The symmetricDifference Operation
Results in a set containing all elements in the set on which the operation is called, or in the parameter set, but not in both
This operation is defined on sets only
Set{1,4,7,10}.symmetricDifference(Set{4,5,7}) = Set{1,5,10}
Operations on OrderedSets and Sequences OnlyThe first and last operations result in the first and the last elements of the collection, respectively
Sequence{'a','b','c','c','d','e'}->first()= 'a'
OrderedSet{'a','b','c','d'}->last() = 'd'
The at operation results in the element at the given position
Sequence{'a','b','c','c','d','e'}->at(3)= 'c'
The indexOf operation results in an integer value that indicates the position of the element in the collection
When the element is present more than once in the collection, the result is the position of the first element
The index numbers start with one, not zero
Sequence{'a','b','c','c','d','e'}->indexOf( 'c' ) = 3
The insertAt operation results in a sequence or ordered set that has an extra element inserted at the given position
OrderedSet{'a','b','c','d'}->insertAt(3,'X’)= OrderedSet{'a','b','X','c','d'}
The subSequence operation may be applied to sequences only
Results in a sequence that contains the elements from the lower index to the upper index, inclusive, in the original order
Sequence{'a','b','c','c','d','e'}-> subSequence(3,5)= Sequence{'c','c','d'}
The subOrderedSet operation may be applied to ordered sets only
Its result is equal to the subSequence operation, although it results in an ordered set instead of a sequence
OrderedSet{'a','b','c','d'}->subOrderedSet( 2, 3 ) = OrderedSet{'b','c'}
The append and prepend operations add an element to a sequence as the last or first element, respectively
Sequence{'a','b','c','c','d','e'}-> append('X’)= Sequence{'a','b','c','c','d','e','X'}
Sequence{'a','b','c','c','d','e'}-> prepend('X’)= Sequence{'X','a','b','c','c','d','e'}
Loop Operations or IteratorsLoop operations on all collection types
Iterator VariablesAn iterator variable is a variable that is used within the body parameter to indicate the element of the collection for which the body parameter is being calculated
The type of this iterator variable is always the type of the elements in the collection
Because the type is known, it may be span omitted in the declaration of the iterator variable
context LoyaltyProgram
inv: self.Membership.account->isUnique(accacc.number)
context LoyaltyProgram
inv: self.Membership.account->isUnique(acc: LoyaltyAccountacc.number)
context LoyaltyProgram
inv: self.Membership.account->isUnique(number)
The sortedBy OperationWe can demand an ordering on the elements of any collection using the sortedBy operation
The parameter of this operation is a property of the type of the elements in the collection
For this property, the lesserThan operation (denoted by <) must be defined
The result is a sequence or ordered set, depending on the type of the original collection
Loop over all elements in the original collection and order all elements according to the value derived from calculating the parameter property
The first element in the result is the element for which the property is the lowest
context LoyaltyProgram
def: sortedAccounts:Sequence(LoyaltyAccount) = self.Membership.account->sortedBy(number)
The select OperationThe select operation enables us to specify a selection from the original collection
The result of the select operation is always a proper subset of the original collection
The parameter of the select operation is a boolean expression that specifies which elements we want to select from the collection
The result of select is the collection that contains all elements for which the boolean expression is true
context CustomerCard
inv: self.transactions->select(points>100)->notEmpty()
The result of select can be described by the following pseudocode:
element = collection.firstElement();
while(collection.notEmpty()) do
if()
then
result.add(element);
endif
element = collection.nextElement();
endwhile
return result;
The reject OperationThe reject operation is analogous to select, with the distinction that reject selects all elements from the collection for which the expression evaluates to false
The existence of reject is merely a convenience
The following two invariants are semantically equivalent:
context Customer
inv: Membership.account->select(points>0)
context Customer
inv: Membership.account-> reject(not(points>0))
The any OperationTo obtain any element from a collection for which a certain condition holds
The body parameter of this operation is a boolean expression
The result is a single element of the original collection
If the condition holds for more than one element, one of them is randomly chosen
If the condition does not hold for any element in the source collection, the result is undefined
self.Membership.account->any(number<10000)
The forAll OperationTo specify that a certain condition must hold for all elements of a collection
The result is a boolean value
It is true if the expression is true for all elements of the collection
If the expression is false for one or more elements in the collection, then forAll results in false
context LoyaltyProgram
inv: participants->forAll(age()<=70)
The forAll operation has an extended variant in which multiple iterator variables can be declared
context LoyaltyProgram
inv: self.participants->forAll(c1 ▏self.participants->forAll( c2 ▏c1 <> c2 implies c1.name <> c2.name ))
context LoyaltyProgram
inv: self.participants->forAll(c1, c2 ▏c1 <> c2 implies c1.name <> c2.name)
Although the number of iterators is unrestricted, more than two iterators are seldom used
The multiple iterators are allowed only with the forAll operation and not with any other operation that uses iteratorsThe exists OperationTo specify that there is at least one object in a collection for which a certain condition holds
The result is a boolean
It is true if the expression is true for at least one element of the collection
If the expression is false for all elements in the collection, then the exists operation results in false
context LoyaltyAccount
inv: points > 0 implies transactions-> exists(t ▏t.points > 0)
There is a relationship between the exists and the forAll operations
collection->exists(‹expression›)
not collection->forAll(not ‹expression›)
The one OperationThe one operation gives a boolean result stating whether there is exactly one element in the collection for which a condition holds
If there is exactly one such element, then the result is true
Otherwise, the result is false
context LoyaltyProgram
inv: self.Membership.account-> one(number<10000)
The difference between the any and one operations
The any operation can be seen as a variant of the select operation
Its result is an element selected from the source collection
The one operation is a variant of the exists operation
Its result is either true or false depending on whether or not a certain element exists in the source collectionThe collect OperationIterates over the collection, computes a value for each element of the collection, and gathers the evaluated values into a new collection
The type of the elements in the resulting collection is usually different from the type of the elements in the collection on which the operation is applied
The result of the collect operation on a set or bag is a bag and on an ordered set or sequence, the result is a sequence
The result of the collect is always a flattened collection
context LoyaltyAccount
inv: transactions->collect( points )-> exists( p : Integer ▏p = 500 )
context LoyaltyAccount
inv: transactions.points->exists(p : Integer ▏p = 500 )
The collectNested OperationIterates over the collection, like the collect operation
Whereas the result of the collect is always a flattened collection
The result of the collectNested operation maintains the nested structure of collections within collections
self.programs->collect(partners)-> collectNested( deliveredServices )
the type of this expression is Bag(Set(Service))The iterate OperationThe iterate operation is the most fundamental and complex of the loop operations
At the same time, it is the most generic loop operation
The syntax of the iterate operation is as follows:
collection->iterate( element : Type1; result:Type2 = ‹expression› ▏‹expression-with-element-and-result›)
The resulting value is accumulated in the variable result, which is also called the accumulator
The accumulator gets an initial value, given by the expression after the equal sign
None of the parameters is optional
Set{1,2,3}->iterate( i: Integer, sum: Integer = 0 ▏sum + i )
Suppose that the class ProgramPartner needs a query operation that returns all transactions on services of all partners that burn points
context ProgramPartner
def:getBurningTransactions():Set(Transaction) =
self.deliveredServices.transactions->iterate(
t: Transaction;
resultSet:Set(Transaction) = Set{}
if t.oclIsTypeOf(Burning) then
resultSet.including(t)
else
resultSet
endif
)
0 意見:
張貼留言