mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
747 lines
No EOL
28 KiB
Text
747 lines
No EOL
28 KiB
Text
[ $$0,0#00000000000000000000000000000000:Default]
|
|
[a83;*R6 $$1,3#31310162474203024125188417583966:caption]
|
|
[H4;b83;*4 $$2,3#07864147445237544204411237157677:title]
|
|
[b42;a42;ph2 $$3,3#45413000475342174754091244180557:text]
|
|
[l321;C@5;1 $$4,4#20902679421464641399138805415013:code]
|
|
[l321;b83;a83;*C$7;2 $$5,5#07531550463529505371228428965313:result`-line]
|
|
[l321;*C$7;2 $$6,6#03451589433145915344929335295360:result]
|
|
[{_}%EN-US
|
|
[s1; U`+`+ Containers Tutorial&]
|
|
[s2; 1. Vector basics&]
|
|
[s3; Let us start with a simple [* Vector] of ints&]
|
|
[s4; -|[* Vector<int>] v;&]
|
|
[s3; You can add elements to the Vector as parameters of the [* Add]
|
|
method&]
|
|
[s4; -|v.[* Add](1);&]
|
|
[s4; -|v.Add(2);&]
|
|
[s4; -|v.Add(3);&]
|
|
[s3; To iterate Vector you can use indices&]
|
|
[s4; -|for(int i `= 0; i < v.[* GetCount](); i`+`+)&]
|
|
[s4; -|-|LOG(v[* `[]i[* `]]);&]
|
|
[s3; Alternative, U`+`+ also provides standard C`+`+ begin/end interface&]
|
|
[s4; -|for(auto q `= v.[* begin](), e `= v.[* end](); q !`= e; q`+`+)&]
|
|
[s4; -|-|LOG([* `*]q);&]
|
|
[s3; or with C`+`+11 range`-for syntax:&]
|
|
[s4; -|for(const auto`& q : v)&]
|
|
[s4; -|-|LOG(q);&]
|
|
[s3;/ &]
|
|
[s3; [/ Note: LOG is diagnostics macro that outputs its argument to
|
|
the .log file in debug mode. Another similar macros we will use
|
|
in this tutorial are DUMP (similar to the LOG, but dumps the
|
|
source expression too) and DUMPC (dumps the content of the container).]&]
|
|
[s3;/ &]
|
|
[s2; 2. Vector operations&]
|
|
[s3; You can [* Insert] or [* Remove] elements at random positions of
|
|
Vector&]
|
|
[s4; -|Vector<int> v;&]
|
|
[s4; -|v.Add(1);&]
|
|
[s4; -|v.Add(2);&]
|
|
[s4; -|&]
|
|
[s4; -|v.[* Insert](1, 10);&]
|
|
[s5; v `= `{ 1, 10, 2 `}&]
|
|
[s4; -|v.[* Remove](0);&]
|
|
[s5; v `= `{ 10, 2 `}&]
|
|
[s3; [* At] method returns element at specified position ensuring that
|
|
such a position exists. If there is not enough elements in Vector,
|
|
required number of elements is added. If second parameter of
|
|
At is present, newly added elements are initialized to this value.
|
|
As an example, we will create distribution of RandomValue with
|
|
unknown maximal value&]
|
|
[s4; &]
|
|
[s4; -|v.Clear();&]
|
|
[s4; -|for(int i `= 0; i < 10000; i`+`+)&]
|
|
[s4; -|-|v.[* At](RandomValue(), 0)`+`+;&]
|
|
[s5; v `= `{ 958, 998, 983, 1012, 1013, 1050, 989, 998, 1007, 992
|
|
`}&]
|
|
[s3; You can apply algorithms on containers, e.g. [* Sort]&]
|
|
[s4; -|[* Sort](v);&]
|
|
[s5; v `= `{ 958, 983, 989, 992, 998, 998, 1007, 1012, 1013, 1050
|
|
`}&]
|
|
[s3; &]
|
|
[s2; 3. Transfer issues&]
|
|
[s3; Often you need to pass content of one container to another of
|
|
the same type. NTL containers always support [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ p
|
|
ick semantics] (equivalent of std`::move), and, depending on type
|
|
stored, also might support [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ clone
|
|
sematics]. When transfering the value, you have to explicitly
|
|
specify which one to use:&]
|
|
[s4; -|Vector<int> v;&]
|
|
[s4; -|v.Add(1);&]
|
|
[s4; -|v.Add(2);&]
|
|
[s4; &]
|
|
[s4; -|Vector<int> v1 [* `=] pick(v);&]
|
|
[s3; now source Vector [C v] is destroyed `- picked `- and you can
|
|
no longer access its content. This behaviour has many advantages.
|
|
First, it is consistently fast and in most cases, transfer of
|
|
value instead of full copy is exactly what you need. Second,
|
|
NTL containers can store elements that lack copy operation `-
|
|
in that case, pick transfer is the only option anyway.&]
|
|
[s3; If you really need to preserve value of source (and elements
|
|
support deep copy operation), you can use [^topic`:`/`/Core`/srcdoc`/pick`_`$en`-us^ c
|
|
lone]&]
|
|
[s4; -|v [* `=] clone(v1);&]
|
|
[s3; &]
|
|
[s2; 4. Client types&]
|
|
[s3; So far we were using int as type of elements. In order to store
|
|
client defined types into the Vector (and the Vector [^topic`:`/`/Core`/src`/Overview`$en`-us^ f
|
|
lavor]) the type must satisfy [^topic`:`/`/Core`/src`/Moveable`$en`-us^ moveable]
|
|
requirement `- in short, it must not contain back`-pointers nor
|
|
virtual methods. Type must be marked as moveable in order to
|
|
define interface contract using&]
|
|
[s4; -|struct Distribution : [* Moveable]<Distribution> `{&]
|
|
[s4; -|-|String text;&]
|
|
[s4; -|-|Vector<int> data;&]
|
|
[s4; &]
|
|
[s4; -|-|rval`_default(Distribution);&]
|
|
[s4; -|-|Distribution() `{`}&]
|
|
[s4; -|`};&]
|
|
[s3; Note that rval`_default macro is helper to restore default deleted
|
|
r`-value constructor in C`+`+11 and default constructor is also
|
|
default deleted in C`+`+11.&]
|
|
[s3; Now to add Distribution elements you cannot use Add with parameter,
|
|
because it requires elements to have default deep`-copy constructor
|
|
`- and Distribution does not have one, as Vector<int> has default
|
|
pick`-constructor, so Distribution itself has pick`-constructor.
|
|
It would no be a good idea either, because deep`-copy would involve
|
|
expensive copying of inner Vector.&]
|
|
[s3; Instead, [* Add] without parameters has to be used `- it default
|
|
constructs (that is cheap) element in Vector and returns reference
|
|
to it&]
|
|
[s4; -|Vector<Distribution> dist;&]
|
|
[s4; -|for(int n `= 5; n <`= 10; n`+`+) `{&]
|
|
[s4; -|-|Distribution`& d `= dist.[* Add]();&]
|
|
[s4; -|-|d.text << `"Test `" << n;&]
|
|
[s4; -|-|for(int i `= 0; i < 10000; i`+`+)&]
|
|
[s4; -|-|-|d.data.At(rand() % n, 0)`+`+;&]
|
|
[s4; -|`}&]
|
|
[s4; &]
|
|
[s6; Test 5: `{ 2018, 1992, 2025, 1988, 1977 `}&]
|
|
[s6; Test 6: `{ 1670, 1682, 1668, 1658, 1646, 1676 `}&]
|
|
[s6; Test 7: `{ 1444, 1406, 1419, 1493, 1370, 1418, 1450 `}&]
|
|
[s6; Test 8: `{ 1236, 1199, 1245, 1273, 1279, 1302, 1250, 1216 `}&]
|
|
[s6; Test 9: `{ 1115, 1111, 1100, 1122, 1192, 1102, 1089, 1064, 1105
|
|
`}&]
|
|
[s6; Test 10: `{ 969, 956, 1002, 1023, 1006, 994, 1066, 1022, 929,
|
|
1033 `}&]
|
|
[s3; Another possibility is to use AddPick method, which uses pick`-constructor
|
|
instead of deep`-copy constructor. E.g. Distribution elements
|
|
might be generated by some function &]
|
|
[s4; -|Distribution CreateDist(int n);&]
|
|
[s3; and code for adding such elements to Vector then looks like&]
|
|
[s4; -|for(n `= 5; n <`= 10; n`+`+)&]
|
|
[s4; -|-|dist.[* AddPick](CreateDist(n));&]
|
|
[s3; alternatively, you can use default`-constructed variant too&]
|
|
[s4; -|-|dist.Add() `= CreateDist(); // alternative&]
|
|
[s3; &]
|
|
[s2; 5. Array flavor&]
|
|
[s3; If elements do not satisfy requirements for Vector flavor, they
|
|
can still be stored in Array flavor. Another reason for using
|
|
Array is need for referencing elements `- Array flavor never
|
|
invalidates references or pointers to them.&]
|
|
[s3; Example of elements that cannot be stored in Vector flavor are
|
|
STL containers (STL does not specify the NTL flavor for obvious
|
|
reasons):&]
|
|
[s4; -|[* Array]< std`::list<int> > al;&]
|
|
[s4; -|for(int i `= 0; i < 4; i`+`+) `{&]
|
|
[s4; -|-|std`::list<int>`& l `= al.Add();&]
|
|
[s4; -|-|for(int q `= 0; q < i; q`+`+)&]
|
|
[s4; -|-|-|l.push`_back(q);&]
|
|
[s4; -|`}&]
|
|
[s3; &]
|
|
[s2; 6. Polymorphic Array&]
|
|
[s3; Array can even be used for storing polymorphic elements &]
|
|
[s4; struct Number `{&]
|
|
[s4; -|virtual double Get() const `= 0;&]
|
|
[s4; &]
|
|
[s4; -|String ToString() const `{ return AsString(Get()); `}&]
|
|
[s4; -|&]
|
|
[s4; -|virtual `~Number() `{`}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; struct Integer : public Number `{&]
|
|
[s4; -|int n;&]
|
|
[s4; -|virtual double Get() const `{ return n; `}&]
|
|
[s4; &]
|
|
[s4; -|Integer() `{`}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; struct Double : public Number `{&]
|
|
[s4; -|double n;&]
|
|
[s4; -|virtual double Get() const `{ return n; `}&]
|
|
[s4; &]
|
|
[s4; -|Double() `{`}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; bool operator<(const Number`& a, const Number`& b)&]
|
|
[s4; `{&]
|
|
[s4; -|return a.Get() < b.Get();&]
|
|
[s4; `}&]
|
|
[s4; &]
|
|
[s3; In this case, elements are added using Add with pointer to base
|
|
element type parameter. Do not be confused by new and pointer,
|
|
Array takes ownership of passed object and behaves like container
|
|
of base type elements&]
|
|
[s4; -|Array<Number> num;&]
|
|
[s4; -|num.Create<Double>().n `= 15.5;&]
|
|
[s4; -|num.Create<Integer>().n `= 3;&]
|
|
[s5; num `= `{ 15.5, 3 `}&]
|
|
[s3; Thanks to well defined algorithm requirements, you can e.g.
|
|
directly apply Sort on such Array&]
|
|
[s4; -|bool operator<(const Number`& a, const Number`& b)&]
|
|
[s4; -|`{&]
|
|
[s4; -|-|return a.Get() < b.Get();&]
|
|
[s4; -|`}&]
|
|
[s4; &]
|
|
[s4; .......&]
|
|
[s4; &]
|
|
[s4; -|Sort(num);&]
|
|
[s5; num `= `{ 3, 15.5 `}&]
|
|
[s3; &]
|
|
[s2; 7. Bidirectional containers&]
|
|
[s3; Vector and Array containers allow fast adding and removing elements
|
|
at the end of sequence. Sometimes, same is needed at begin of
|
|
sequence too (usually to support FIFO like operations). In such
|
|
case, BiVector and BiArray should be used&]
|
|
[s4; -|BiVector<int> n;&]
|
|
[s4; -|n.[* AddHead](1);&]
|
|
[s4; -|n.[* AddTail](2);&]
|
|
[s4; -|n.AddHead(3);&]
|
|
[s4; -|n.AddTail(4);&]
|
|
[s5; n `= `{ 3, 1, 2, 4 `}&]
|
|
[s4; -|n.[* DropHead]();&]
|
|
[s5; n `= `{ 1, 2, 4 `}&]
|
|
[s4; -|n.[* DropTail]();&]
|
|
[s5; n `= `{ 1, 2 `}&]
|
|
[s4; -|BiArray<Number> num;&]
|
|
[s4; -|num.CreateHead<Integer>().n `= 3;&]
|
|
[s4; -|num.CreateTail<Double>().n `= 15.5;&]
|
|
[s4; -|num.CreateHead<Double>().n `= 2.23;&]
|
|
[s4; -|num.CreateTail<Integer>().n `= 2;&]
|
|
[s5; num `= `{ 2.23, 3, 15.5, 2 `}&]
|
|
[s3; &]
|
|
[s2; 8. Index&]
|
|
[s3; Index is a container very similar to the plain Vector (it is
|
|
random access array of elements with fast addition at the end)
|
|
with one unique feature `- it is able to fast retrieve position
|
|
of element with required value using [* Find] method&]
|
|
[s4; -|[* Index]<String> ndx;&]
|
|
[s4; -|ndx.[* Add](`"alfa`");&]
|
|
[s4; -|ndx.Add(`"beta`");&]
|
|
[s4; -|ndx.Add(`"gamma`");&]
|
|
[s4; -|ndx.Add(`"delta`");&]
|
|
[s4; -|ndx.Add(`"kappa`");&]
|
|
[s5; ndx `= `{ alfa, beta, gamma, delta, kappa `}&]
|
|
[s4; -|DUMP(ndx.[* Find](`"beta`"));&]
|
|
[s5; ndx.Find(`"beta`") `= 1&]
|
|
[s3; If element is not present in Index, Find returns a negative
|
|
value&]
|
|
[s4; -|DUMP(ndx.Find(`"something`"));&]
|
|
[s5; ndx.Find(`"something`") `= `-1&]
|
|
[s3; Any element can be replaced using [* Set] method&]
|
|
[s4; -|ndx.[* Set](0, `"delta`");&]
|
|
[s5; ndx `= `{ delta, beta, gamma, delta, kappa `}&]
|
|
[s3; If there are more elements with the same value, they can be
|
|
iterated using [* FindNext] method&]
|
|
[s4; -|int fi `= ndx.Find(`"delta`");&]
|
|
[s4; -|while(fi >`= 0) `{&]
|
|
[s4; -|-|DUMP(fi);&]
|
|
[s4; -|-|fi `= ndx.[* FindNext](fi);&]
|
|
[s4; -|`}&]
|
|
[s4; -|cout << `'n`';&]
|
|
[s5; 0 3-|&]
|
|
[s3; [* FindAdd] method retrieves position of element like Find, but
|
|
if element is not present in Index, it is added&]
|
|
[s4; -|DUMP(ndx.[* FindAdd](`"one`"));&]
|
|
[s4; -|DUMP(ndx.FindAdd(`"two`"));&]
|
|
[s4; -|DUMP(ndx.FindAdd(`"three`"));&]
|
|
[s4; -|DUMP(ndx.FindAdd(`"two`"));&]
|
|
[s4; -|DUMP(ndx.FindAdd(`"three`"));&]
|
|
[s4; -|DUMP(ndx.FindAdd(`"one`"));&]
|
|
[s4; &]
|
|
[s6; ndx.FindAdd(`"one`") `= 5&]
|
|
[s6; ndx.FindAdd(`"two`") `= 6&]
|
|
[s6; ndx.FindAdd(`"three`") `= 7&]
|
|
[s6; ndx.FindAdd(`"two`") `= 6&]
|
|
[s6; ndx.FindAdd(`"three`") `= 7&]
|
|
[s6; ndx.FindAdd(`"one`") `= 5&]
|
|
[s3; Removing elements from random access sequence is always expensive,
|
|
that is why rather than remove, Index supports [* Unlink] and [* UnlinkKey
|
|
]operations, which leave element in Index but make it invisible
|
|
for Find operation&]
|
|
[s4; -|ndx.[* Unlink](2);&]
|
|
[s4; -|ndx.[* UnlinkKey](`"kappa`");&]
|
|
[s4; &]
|
|
[s4; -|DUMP(ndx.Find(ndx`[2`]));&]
|
|
[s4; -|DUMP(ndx.Find(`"kappa`"));&]
|
|
[s4; &]
|
|
[s6; ndx.Find(ndx`[2`]) `= `-1&]
|
|
[s6; ndx.Find(`"kappa`") `= `-1&]
|
|
[s3; You can test whether element at given position is unlinked using
|
|
[* IsUnlinked] method&]
|
|
[s4; -|DUMP(ndx.[* IsUnlinked](1));&]
|
|
[s4; -|DUMP(ndx.IsUnlinked(2));&]
|
|
[s4; &]
|
|
[s6; ndx.IsUnlinked(1) `= 0&]
|
|
[s6; ndx.IsUnlinked(2) `= 1&]
|
|
[s3; Unlinked positions can be reused by [* Put] method&]
|
|
[s4; -|ndx.[* Put](`"foo`");&]
|
|
[s5; ndx `= `{ delta, beta, foo, delta, kappa, one, two, three `}&]
|
|
[s4; -|DUMP(ndx.Find(`"foo`"));&]
|
|
[s5; ndx.Find(`"foo`") `= 2&]
|
|
[s3; You can also remove all unlinked elements from Index using [* Sweep]
|
|
method&]
|
|
[s4; -|ndx.Sweep();&]
|
|
[s5; ndx `= `{ delta, beta, foo, delta, one, two, three `}&]
|
|
[s3; As we said, operations directly removing or inserting elements
|
|
of Index are very expensive, but sometimes this might not matter,
|
|
so they are available too&]
|
|
[s4; -|ndx.[* Remove](1);&]
|
|
[s5; ndx `= `{ delta, foo, delta, one, two, three `}&]
|
|
[s4; -|ndx.[* RemoveKey](`"two`");&]
|
|
[s5; ndx `= `{ delta, foo, delta, one, three `}&]
|
|
[s4; -|ndx.[* Insert](0, `"insert`");&]
|
|
[s5; ndx `= `{ insert, delta, foo, delta, one, three `}&]
|
|
[s3; Finally, [* PickKeys] operation allows you to obtain Vector of
|
|
elements of Index in low constant time operation (while destroying
|
|
source Index)&]
|
|
[s4; -|Vector<String> d `= ndx.[* PickKeys]();&]
|
|
[s4; -|Sort(d);&]
|
|
[s5; d `= `{ delta, delta, foo, insert, one, three `}&]
|
|
[s3; &]
|
|
[s2; 9. Index and client types&]
|
|
[s3; In order to store elements to Index, they must be moveable (you
|
|
can use [* ArrayIndex] for types that are not) and they must have
|
|
defined the operator`=`= and a function to compute hash value.
|
|
Notice usage THE of [* CombineHash] to combine hash values of types
|
|
already known to U`+`+ into final result&]
|
|
[s4; &]
|
|
[s4; struct Person : Moveable<Person> `{&]
|
|
[s4; -|String name;&]
|
|
[s4; -|String surname;&]
|
|
[s4; &]
|
|
[s4; -|unsigned [* GetHashValue]() const `{ return [* CombineHash](name,
|
|
surname); `}&]
|
|
[s4; -|bool [* operator`=`=](const Person`& b) const `{ return name
|
|
`=`= b.name `&`& surname `=`= b.surname; `}&]
|
|
[s4; &]
|
|
[s4; -|Person(String name, String surname) : name(name), surname(surname)
|
|
`{`}&]
|
|
[s4; -|Person() `{`}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; .......&]
|
|
[s4; &]
|
|
[s4; -|Index<Person> p;&]
|
|
[s4; -|p.Add(Person(`"John`", `"Smith`"));&]
|
|
[s4; -|p.Add(Person(`"Paul`", `"Carpenter`"));&]
|
|
[s4; -|p.Add(Person(`"Carl`", `"Engles`"));&]
|
|
[s4; -| &]
|
|
[s4; -|DUMP(p.Find(Person(`"Paul`", `"Carpenter`")));&]
|
|
[s5; p.Find(Person(`"Paul`", `"Carpenter`")) `= 1&]
|
|
[s3; &]
|
|
[s2; 10. VectorMap, ArrayMap&]
|
|
[s3; VectorMap is nothing more than a simple composition of Index
|
|
and Vector. You can use [* Add] methods to put elements into the
|
|
VectorMap&]
|
|
[s4; -|[* VectorMap]<String, Person> m;&]
|
|
[s4; -|&]
|
|
[s4; -|m.[* Add](`"1`", Person(`"John`", `"Smith`"));&]
|
|
[s4; -|m.Add(`"2`", Person(`"Carl`", `"Engles`"));&]
|
|
[s4; &]
|
|
[s4; -|Person`& p `= m.[* Add](`"3`");&]
|
|
[s4; -|p.name `= `"Paul`";&]
|
|
[s4; -|p.surname `= `"Carpenter`";&]
|
|
[s3; VectorMap provides constant access to its underlying Index and
|
|
Vector&]
|
|
[s4; -|DUMP(m.[* GetKeys]());&]
|
|
[s4; -|DUMP(m.[* GetValues]());&]
|
|
[s4; &]
|
|
[s6; m.GetKeys() `= `{ 1, 2, 3 `}&]
|
|
[s6; m.GetValues() `= `{ John Smith, Carl Engles, Paul Carpenter
|
|
`}&]
|
|
[s4; &]
|
|
[s3; You can use indices to iterate map contents&]
|
|
[s4; -|for(int i `= 0; i < m.GetCount(); i`+`+)&]
|
|
[s4; -|-|LOG(m.[* GetKey](i) << `": `" << m[* `[]i[* `]]);&]
|
|
[s4; &]
|
|
[s6; 1: John Smith&]
|
|
[s6; 2: Carl Engles&]
|
|
[s6; 3: Paul Carpenter&]
|
|
[s4; &]
|
|
[s3; You can use [* Find] method to retrieve position of element with
|
|
required key&]
|
|
[s4; -|DUMP(m.[* Find](`"2`"));&]
|
|
[s5; m.Find(`"2`") `= 1&]
|
|
[s3; or [* Get] method to retrieve corresponding value&]
|
|
[s4; -|DUMP(m.[* Get](`"2`"));&]
|
|
[s5; m.Get(`"2`") `= Carl Engles&]
|
|
[s3; Passing key not present in VectorMap as Get parameter is a logic
|
|
error, but there exists two parameter version that returns second
|
|
parameter if key is not in VectorMap&]
|
|
[s4; -|DUMP(m.Get(`"33`", Person(`"unknown`", `"person`")));&]
|
|
[s5; m.Get(`"33`", Person(`"unknown`", `"person`")) `= unknown person&]
|
|
[s3; As with Index, you can use [* Unlink] to make elements invisible
|
|
for Find operations&]
|
|
[s4; -|m.Unlink(1);&]
|
|
[s4; -|DUMP(m.Find(`"2`"));&]
|
|
[s5; m.Find(`"2`") `= `-1&]
|
|
[s3; You can use [* SetKey] method to change the key of the element&]
|
|
[s4; -|m.[* SetKey](1, `"33`");&]
|
|
[s4; -|DUMP(m.Get(`"33`", Person(`"unknown`", `"person`")));&]
|
|
[s5; m.Get(`"33`", Person(`"unknown`", `"person`")) `= Carl Engles&]
|
|
[s3; If there are more elements with the same key in VectorMap, you
|
|
can iterate them using [* FindNext] method:&]
|
|
[s4; -|m.Add(`"33`", Person(`"Peter`", `"Pan`"));&]
|
|
[s4; &]
|
|
[s6; m.GetKeys() `= `{ 1, 33, 3, 33 `}&]
|
|
[s6; m.GetValues() `= `{ John Smith, Carl Engles, Paul Carpenter,
|
|
Peter Pan `}&]
|
|
[s4; &]
|
|
[s4; -|int q `= m.Find(`"33`");&]
|
|
[s4; -|while(q >`= 0) `{&]
|
|
[s4; -|-|cout << m`[q`] << `'n`';&]
|
|
[s4; -|-|q `= m.[* FindNext](q);&]
|
|
[s4; -|`}&]
|
|
[s4; -|&]
|
|
[s6; Carl Engles&]
|
|
[s6; Peter Pan&]
|
|
[s4; &]
|
|
[s3; You can reuse unlinked positions using [* Put] method:&]
|
|
[s4; -|m.[* UnlinkKey](`"33`");&]
|
|
[s4; -|m.[* Put](`"22`", Person(`"Ali`", `"Baba`"));&]
|
|
[s4; -|m.Put(`"44`", Person(`"Ivan`", `"Wilks`"));&]
|
|
[s4; &]
|
|
[s6; m.GetKeys() `= `{ 1, 22, 3, 44 `}&]
|
|
[s6; m.GetValues() `= `{ John Smith, Ali Baba, Paul Carpenter, Ivan
|
|
Wilks `}&]
|
|
[s4; &]
|
|
[s3; [* GetSortOrder] algorithm returns order of elements as Vector<int>
|
|
container. You can use it to order content of VectorMap without
|
|
actually moving its elements&]
|
|
[s4; -|bool operator<(const Person`& a, const Person`& b)&]
|
|
[s4; -|`{&]
|
|
[s4; -|-|return a.surname `=`= b.surname ? a.name < b.name&]
|
|
[s4; -| : a.surname < b.surname;&]
|
|
[s4; -|`}&]
|
|
[s4; &]
|
|
[s4; .......&]
|
|
[s4; &]
|
|
[s4; -|Vector<int> order `= [* GetSortOrder](m.GetValues());&]
|
|
[s5; order `= `{ 1, 2, 0, 3 `}&]
|
|
[s4; -|for(int i `= 0; i < order.GetCount(); i`+`+)&]
|
|
[s4; -|-|cout << m.GetKey(order`[i`]) << `": `" << m`[order`[i`]`] <<
|
|
`'n`';&]
|
|
[s4; &]
|
|
[s6; 22: Ali Baba&]
|
|
[s6; 3: Paul Carpenter&]
|
|
[s6; 1: John Smith&]
|
|
[s6; 44: Ivan Wilks&]
|
|
[s4; &]
|
|
[s3; You can get Vector of values or keys using [* PickValues] resp.
|
|
[* PickKeys] methods in low constant time, while destroying content
|
|
of source VectorMap&]
|
|
[s4; -|Vector<Person> ps `= m.[* PickValues]();&]
|
|
[s5; ps `= `{ John Smith, Ali Baba, Paul Carpenter, Ivan Wilks `}&]
|
|
[s3; If type of values does not satisfy requirements for Vector elements
|
|
or if references to elements are needed, you can use [* ArrayMap]
|
|
instead&]
|
|
[s4; -|[* ArrayMap]<String, Number> am;&]
|
|
[s4; -|am.Create<Integer>(`"A`").n `= 11;&]
|
|
[s4; -|am.Create<Double>(`"B`").n `= 2.1;&]
|
|
[s4; &]
|
|
[s6; am.GetKeys() `= `{ A, B `}&]
|
|
[s6; am.GetValues() `= `{ 11, 2.1 `}&]
|
|
[s4; &]
|
|
[s4; -|DUMP(am.Get(`"A`"));&]
|
|
[s4; -|DUMP(am.Find(`"B`"));&]
|
|
[s4; &]
|
|
[s6; am.Get(`"A`") `= 11&]
|
|
[s6; am.Find(`"B`") `= 1&]
|
|
[s3; &]
|
|
[s2; 11. One&]
|
|
[s3; One is a container that can store none or one element of T or
|
|
derived from T. It functionally quite similiar to std`::unique`_ptr,
|
|
but has some more convenient features (like Create method).&]
|
|
[s4; struct Base `{&]
|
|
[s4; -|virtual String Get() `= 0;&]
|
|
[s4; -|virtual `~Base() `{`}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; struct Derived1 : Base `{&]
|
|
[s4; -|virtual String Get() `{ return `"Derived1`"; `}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; struct Derived2 : Base `{&]
|
|
[s4; -|virtual String Get() `{ return `"Derived2`"; `}&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; void MakeDerived1(One<Base>`& t)&]
|
|
[s4; `{&]
|
|
[s4; -|t.Create<Derived1>();&]
|
|
[s4; `}&]
|
|
[s4; &]
|
|
[s4; void MakeDerived2(One<Base>`& t)&]
|
|
[s4; `{&]
|
|
[s4; -|t.Create<Derived2>();&]
|
|
[s4; `}&]
|
|
[s4; &]
|
|
[s4; .......&]
|
|
[s4; -|[* One]<Base> s;&]
|
|
[s3; Operator bool of one returns true if it contains an element:&]
|
|
[s4; -|DUMP([* (bool)]s);&]
|
|
[s4; &]
|
|
[s6; (bool)s `= false&]
|
|
[s4; &]
|
|
[s4; -|&]
|
|
[s4; -|s.[* Create]<Derived1>();&]
|
|
[s4; -|DUMP((bool)s);&]
|
|
[s4; -|DUMP(s`->Get());&]
|
|
[s4; &]
|
|
[s6; (bool)s `= true&]
|
|
[s6; s`->Get() `= Derived1&]
|
|
[s4; &]
|
|
[s3; Clear method removes the element from One:&]
|
|
[s4; -|s.[* Clear]();&]
|
|
[s4; -|DUMP((bool)s);&]
|
|
[s4; &]
|
|
[s6; (bool)s `= false&]
|
|
[s4; &]
|
|
[s4; &]
|
|
[s3; One represents a convenient and recommended method how to deal
|
|
with class factories in U`+`+: Define them as a function (or method)
|
|
with reference to One parameter, e.g.:&]
|
|
[s4; void MakeDerived1(One<Base>`& t)&]
|
|
[s4; `{&]
|
|
[s4; -|t.Create<Derived1>();&]
|
|
[s4; `}&]
|
|
[s4; &]
|
|
[s4; void MakeDerived2(One<Base>`& t)&]
|
|
[s4; `{&]
|
|
[s4; -|t.Create<Derived2>();&]
|
|
[s4; `}&]
|
|
[s4; &]
|
|
[s4; VectorMap<int, void (`*)(One<Base>`&)> factories;&]
|
|
[s4; &]
|
|
[s4; INITBLOCK `{&]
|
|
[s4; -|factories.Add(0, MakeDerived1);&]
|
|
[s4; -|factories.Add(1, MakeDerived2);&]
|
|
[s4; `};&]
|
|
[s4; &]
|
|
[s4; void Create(One<Base>`& t, int what)&]
|
|
[s4; `{&]
|
|
[s4; -|(`*factories.Get(what))(t);&]
|
|
[s4; `}&]
|
|
[s3; &]
|
|
[s2; 12. Any&]
|
|
[s3; Any is a container that can contain none or one element of [*/ any]
|
|
type, the only requirement is that the type has default constructor.
|
|
Important thing to remember is that [* Is] method matches [/ exact]
|
|
type ignoring class hierarchies (FileIn is derived from Stream,
|
|
but if Any contains FileIn, Is<Stream>() returns false).&]
|
|
[s4; void Do([* Any]`& x)&]
|
|
[s4; `{&]
|
|
[s4; -|if(x.[* Is]<String>())&]
|
|
[s4; -|-|LOG(`"String: `" << x.[* Get]<String>());&]
|
|
[s4; -|if(x.[* Is]<FileIn>()) `{&]
|
|
[s4; -|-|LOG(`"`-`-`- File: `");&]
|
|
[s4; -|-|LOG(LoadStream(x.[* Get]<FileIn>()));&]
|
|
[s4; -|-|LOG(`"`-`-`-`-`-`-`-`-`-`-`");&]
|
|
[s4; -|`}&]
|
|
[s4; -|if(x.[* IsEmpty]())&]
|
|
[s4; -|-|LOG(`"empty`");&]
|
|
[s4; `}&]
|
|
[s4; &]
|
|
[s4; .....&]
|
|
[s4; &]
|
|
[s4; -|Any x;&]
|
|
[s4; -|x.[* Create]<String>() `= `"Hello!`";&]
|
|
[s4; -|Do(x);&]
|
|
[s4; -|x.[* Create]<FileIn>().Open(GetDataFile(`"Ntl12.cpp`"));&]
|
|
[s4; -|Do(x);&]
|
|
[s4; -|x.[* Clear]();&]
|
|
[s4; -|Do(x);&]
|
|
[s3; &]
|
|
[s2; 13. InVector, InArray&]
|
|
[s3; InVector and InArray are vector types quite similar to Vector/Array,
|
|
but they trade the speed of operator`[`] with the ability to
|
|
insert or remove elements at any position quickly. You can expect
|
|
operator`[`] to be about 10 times slower than in Vector (but
|
|
that is still very fast), while Insert at any position scales
|
|
well up to hundreds of megabytes of data (e.g. InVector containing
|
|
100M of String elements is handled without problems).&]
|
|
[s4; -|[* InVector]<int> v;&]
|
|
[s4; -|for(int i `= 0; i < 1000000; i`+`+)&]
|
|
[s4; -|-|v.Add(i);&]
|
|
[s4; -|v.[* Insert](0, `-1); // This is fast&]
|
|
[s4; &]
|
|
[s3; While the interface of InVector/InArray is almost identical
|
|
to Vector/Array, InVector/InArray in addition implements FindLowerBound/FindUpper
|
|
Bound functions `- while normal random access algorithms work,
|
|
it is possible to provide InVector specific optimization that
|
|
basically matches the performace of Find`*Bound on sample Vector.&]
|
|
[s4; &]
|
|
[s4; -|DUMP(v.[* FindLowerBound](55));&]
|
|
[s3; &]
|
|
[s2; 14. SortedIndex, SortedVectorMap, SortedArrayMap&]
|
|
[s3; SortedIndex is similar to regular Index, but keeps its elements
|
|
in sorted order (sorting predicate is a template parameter, defaults
|
|
to StdLess). Implementation is using InVector, so it works fine
|
|
even with very large number of elements (performance is similar
|
|
to tree based std`::set). Unlike Index, SortedIndex provides lower/upper
|
|
bounds searches, so it allow range search.&]
|
|
[s4; -|[* SortedIndex]<int> x;&]
|
|
[s4; -|x.Add(5);&]
|
|
[s4; -|x.Add(3);&]
|
|
[s4; -|x.Add(7);&]
|
|
[s4; -|x.Add(1);&]
|
|
[s4; -|&]
|
|
[s4; -|DUMPC(x);&]
|
|
[s4; -|DUMP(x.[* Find](3));&]
|
|
[s4; -|DUMP(x.[* Find](3));&]
|
|
[s4; -|DUMP(x.[* FindLowerBound](3));&]
|
|
[s4; -|DUMP(x.[* FindUpperBound](6));&]
|
|
[s4; &]
|
|
[s3; SortedVectorMap and SortedArrayMap are then SortedIndex based
|
|
equivalents to VectorMap/ArrayMap `- maps that keep keys sorted:&]
|
|
[s4; -|[* SortedVectorMap]<String, int> m;&]
|
|
[s4; -|m.Add(`"zulu`", 11);&]
|
|
[s4; -|m.Add(`"frank`", 12);&]
|
|
[s4; -|m.Add(`"alfa`", 13);&]
|
|
[s4; -|&]
|
|
[s4; -|DUMPM(m);&]
|
|
[s4; -|DUMP(m.[* Get](`"zulu`"));&]
|
|
[s3; &]
|
|
[s2; 15. Tuples&]
|
|
[s3; U`+`+ has template classes Tuple2, Tuple3 and Tuple4 for combining
|
|
2`-4 values with different types. These are quite similiar to
|
|
std`::tuple class, with some advantages.&]
|
|
[s3; To create a Tuple value, you can use the Tuple function. If
|
|
correct types canot be deduced from parameters, you can specify
|
|
them explicitly:&]
|
|
[s4; -|Tuple3<int, String, String> x `= [* Tuple]<int, String, String>(12,
|
|
`"hello`", `"world`");&]
|
|
[s4; &]
|
|
[s3; Individual values are accessible as members a .. d:&]
|
|
[s4; &]
|
|
[s4; -|DUMP(x.a);&]
|
|
[s4; -|DUMP(x.b);&]
|
|
[s4; -|DUMP(x.c);&]
|
|
[s4; -|&]
|
|
[s3; As long as all individual types have conversion to String (AsString),
|
|
the tuple also has such conversion and thus can e.g. be easily
|
|
logged:&]
|
|
[s4; &]
|
|
[s4; -|DUMP(x);&]
|
|
[s4; &]
|
|
[s3; Also, as long as individual types have defined GetHashValue,
|
|
so does Tuple:&]
|
|
[s4; &]
|
|
[s4; -|DUMP(GetHashValue(x));&]
|
|
[s4; &]
|
|
[s3; As long as individual types have defined operator`=`=, Tuple
|
|
has defined operator`=`= and operator!`=&]
|
|
[s4; &]
|
|
[s4; -|Tuple3<int, String, String> y `= x;&]
|
|
[s4; -|DUMP(x !`= y);&]
|
|
[s4; &]
|
|
[s3; Finally, as long as all individual types have defined SgnCompare
|
|
(most U`+`+ types have), Tuple has SgnCompare, Compare method
|
|
and operators <, <`=, >, >`=:&]
|
|
[s4; &]
|
|
[s4; -|DUMP(x.Compare(y));&]
|
|
[s4; -|&]
|
|
[s4; -|y.b `= `"a`";&]
|
|
[s4; -|&]
|
|
[s4; -|DUMP(SgnCompare(x, y));&]
|
|
[s4; -|DUMP(x < y);&]
|
|
[s4; -|&]
|
|
[s4; &]
|
|
[s3; U`+`+ Tuples are strictly designed as POD type, which allows
|
|
POD arrays to be intialized with classic C style:&]
|
|
[s4; -|static Tuple2<int, const char `*> map`[`] `= `{&]
|
|
[s4; -|-|`{ 1, `"one`" `},&]
|
|
[s4; -|-|`{ 2, `"one`" `},&]
|
|
[s4; -|-|`{ 3, `"one`" `},&]
|
|
[s4; -|`};&]
|
|
[s4; -|&]
|
|
[s3; &]
|
|
[s3; Simple FindTuple template function is provided to search for
|
|
tuple based on the first value:&]
|
|
[s3; &]
|
|
[s2; 15. Sorting&]
|
|
[s3; IndexSort is sort variant that is able to sort two random access
|
|
container (like Vector or Array) of the same size, based on values
|
|
in on of containers:&]
|
|
[s4; -|Vector<int> a;&]
|
|
[s4; -|Vector<String> b;-|&]
|
|
[s4; -|&]
|
|
[s4; -|a << 5 << 10 << 2 << 9 << 7 << 3;&]
|
|
[s4; -|b << `"five`" << `"ten`" << `"two`" << `"nine`" << `"seven`"
|
|
<< `"three`";&]
|
|
[s4; -|&]
|
|
[s4; -|[* IndexSort](a, b);&]
|
|
[s4; &]
|
|
[s6; a `= `[2, 3, 5, 7, 9, 10`]&]
|
|
[s6; b `= `[two, three, five, seven, nine, ten`]&]
|
|
[s4; &]
|
|
[s4; -|[* IndexSort](b, a);&]
|
|
[s4; &]
|
|
[s6; a `= `[5, 9, 7, 10, 3, 2`]&]
|
|
[s6; b `= `[five, nine, seven, ten, three, two`]&]
|
|
[s3; Order of sorted items is defined by sorting predicate. By default,
|
|
operator< comparing items of container is used (this predicate
|
|
can be provided by StdLess template), but it is possible to specify
|
|
different sorting order, e.g. by using predefined StdGreater
|
|
predicate:&]
|
|
[s4; -|Sort(a, [* StdGreater]<int>());&]
|
|
[s4; &]
|
|
[s6; a `= `[10, 9, 7, 5, 3, 2`]&]
|
|
[s3; Sometimes, instead of sorting items in the container, it is
|
|
useful to know the order of items as sorted, using GetSortOrder:&]
|
|
[s4; -|Vector<int> o `= [* GetSortOrder](a);&]
|
|
[s4; &]
|
|
[s6; o `= `[5, 4, 3, 2, 1, 0`]&]
|
|
[s3; FieldRelation predefined predicate can be used to sort container
|
|
of structures by specific field:&]
|
|
[s4; -|Vector<Point> p;&]
|
|
[s4; -|p << Point(5, 10) << Point(7, 2) << Point(4, 8) << Point(1,
|
|
0);&]
|
|
[s4; -|&]
|
|
[s4; -|Sort(p, [* FieldRelation](`&Point`::x, StdLess<int>()));&]
|
|
[s4; &]
|
|
[s6; p `= `[`[1, 0`], `[4, 8`], `[5, 10`], `[7, 2`]`]&]
|
|
[s3; MethodRelation is good for sorting of structures based on constant
|
|
method of structure:-|&]
|
|
[s4; struct Foo `{&]
|
|
[s4; -|String a;&]
|
|
[s4; -|&]
|
|
[s4; -|int [* Get]() const `{ return atoi(a); `}&]
|
|
[s4; -|....&]
|
|
[s4; `};&]
|
|
[s4; ....&]
|
|
[s4; -|Array<Foo> f;&]
|
|
[s4; -|f << `"12`" << `"1`" << `"10`" << `"7`" << `"5`";&]
|
|
[s4; -|&]
|
|
[s4; -|Sort(f, [* MethodRelation](`&Foo`::[* Get], StdLess<int>()));&]
|
|
[s4; &]
|
|
[s6; f `= `[1, 5, 7, 10, 12`]&]
|
|
[s3; Normal Sort is not stable `- equal items can appear in sorted
|
|
sequence in random order. If maintaining original order of equal
|
|
items is important, use StableSort variant (with slight performance
|
|
penalty):&]
|
|
[s4; -|Vector<Point> t;&]
|
|
[s4; -|t << Point(10, 10) << Point(7, 1) << Point(7, 2) << Point(7,
|
|
3) << Point(1, 0);&]
|
|
[s4; -|[* StableSort](t, FieldRelation(`&Point`::x, StdLess<int>()));&]
|
|
[s4; &]
|
|
[s6; t `= `[`[1, 0`], `[7, 1`], `[7, 2`], `[7, 3`], `[10, 10`]`]&]
|
|
[s3; &]
|
|
[s2; Recommended tutorials:&]
|
|
[s3; If you want to learn more, we have several tutorials that you
|
|
can find useful:&]
|
|
[s3;l160;i150;O0; [^topic`:`/`/Core`/srcdoc`/CoreTutorial`$en`-us^ U`+`+
|
|
Core value types] `- if you miss this one`- it it a good occasion
|
|
to catch up. Here you will learn about Core basics.&]
|
|
[s0; [^topic`:`/`/Sql`/srcdoc`/tutorial`$en`-us^ SQL] `- learn how
|
|
to use databases with U`+`+ framework.&]
|
|
[s0; ]] |