Releases: samchon/tstl
TypeScript-STL v1.6.9
New Features
Iterator Pattern
STL (Standard Template Library) is composed with 4 modules;
- containers
- iterators
- algorithms
- functors
C++ standard committee has defined lots of STL features to use Iterator Pattern with generic and functional programming; especially, global functions of algorithms. However, until v1.5, TSTL had a restriction on using the iterator pattern; only built-in iterators are allowed. All of the global functions in the algorithms module, they had allowed only built-in iterators on their parameters of the iterator pattern. Customized iterator defined by user never could not be used.
Since v1.6 update, such restriction is cleared. TSTL provides common interfaces for the iterator pattern. You can put your customized iterators on the global functions of algorithms or other STL features following the iterator pattern. Just implements below interfaces and enjoy the iterator pattern of STL.
namespace std
{
export interface IForwardIterator<T>
{
equals(obj: IForwardIterator<T>): boolean;
next(): IForwardIterator<T>;
advance(n: number): IForwardIterator<T>;
}
export interface IBidirectionalIterator<T> extends IForwardIterator<T>
{
prev(): IBidirectionalIterator<T>;
}
export interface IRandomAccessIterator<T> extends IBidirectionalIterator<T>
{
index(): IRandomAccessIterator<T>;
}
}
namespace std.JSArray
{
declare class Iterator<T> implements IRandomAccessIterator<T>;
declare class ReverseIterator<T> implements IRandomAccessIterator<T>;
}
namespace std
{
declare function count<T, InputIterator extends IForwardIterator<T>>
(first: InputIterator, last: InputItrator): number;
declare function is_sorted_until<T, InputIterator extends IForwardIterator<T>>
(first: InputIterator, last: InputItrator): InputIterator;
declare class Deque<Key, T>
{
public assign<InputIterator extends IForwradIterator<T>>
(first: InputIterator, last: InputIterator);
}
}
You also can take advantage of STL's iterator pattern with JS-Array. Create iterators for JS-Array with below functions.
- Formal Iterators
- Reverse Iterators
import std = require("tstl");
let array: Array<number> = [1, 2, 3, 4, 5, 6, 7];
let first = std.begin(array); // std.JSArray.Iterator<number>
let last = std.end(array);
std.foreach(first, last, function (val: number): void
{
console.log(val);
});
ForwardList
<forward_list>
has implemented.
Unlike std.List
who represents doubly linked list and shares same interfaces with other linear containers, std.ForwardList
represents the singly linked list and does not share same interface with other linear containers. The std.ForwardList
container has its own specific insertion and deletion methods.
Name | Data Structure | Common Interface |
---|---|---|
std.ForwardList |
Singly Linked List | O |
std.List |
Doubly Linked List | X |
ConditionVariable
Since v1.6 update, <condition_variable>
, a class of supporting critical section in C++, has implemented in the TSTL.
namespace std
{
export class ConditionVariable
{
// DO NOT NEED unique_lock
public constructor();
public wait(): Promise<void>;
public wait_for(ms: number): Promise<boolean>;
public wait_until(at: Date): Promise<boolean>;
public notify_one(): void;
public notify_all(): void;
}
export type condition_variable = ConditionVariable;
}
experiments.Semaphore
Since v1.6 update, experiments.semaphore
and experiments.timed_semaphore
has implemented. Note that, they're just experimental classes defined in the experiments
namespace, not defined in the regular STL; defined by C++ standard committe.
namespace std.experiments
{
export class Semaphore implements ILockable
{
public constructor(size: number);
public size(): number;
public lock(count: number = 1): Promise<void>;
public try_lock(count: number = 1): boolean;
public unlock(count: number = 1): Promise<void>;
}
export class TimedSemaphore implements Semaphore
{
public try_lock_for(ms: number, count: number = 1): Promise<boolean>;
public try_lock_until(at: Date, count: number = 1): Promise<boolean>;
}
}
Error Fixes
Equality Function on Tree Containers
Equlity function on the Tree Containers has changed to such below
- Before:
std.equal_to(x, y)
- From now on:
!comp(x, y) && !comp(y, x)
I had repeated a mistake on the equality function of Tree Containers.
- TreeSet
set
- TreeMultiSet
multiset
- TreeMap
map
- TreeMultiMap
multimap
It has been possible to specializing compare function on Tree Containers in construction level. However, the equality function on the Tree Containers had not utilized the compare function. I'd repeated a mistake that Tree Containers to use std.equal_to
function instead of the specialized compare function.
Since this v1.6 update, the equality function of the Tree Containers has changed to utilizing the specialized compare function such below:
!comp(x, y) && !comp(y, x);
Specializations of Hash Containers
I'd forgotten some features, specifying custom hash & equality functions on the Hash Containers, for a long time. Since this v1.6 update, you can use your custom hash functions and equality functions on the Hash Containers:
- HashSet
unordered_set
- HashMultiSet
unordered_multiset
- HashMap
unordered_map
- HashMultiMap
unordered_multimap
Until this update, you couldn't use your specialized hash & equality function on the Hash Containers. The Hash Containers only used the std.hash
and std.equal_to
. However, from now on, you can specialize the hash & equality functions in the constructor level.
Reference the below code and create your specialize hash containers by inserting your custom functions as parameters on the constructor. If you do not specify the hash and equality functions, std.hash
and std.equal_to
functions will be used like before.
namespace std
{
type HashFunction<T> = (val: T) => number;
type EqualFunction<T> = (x: T, y: T) => boolean;
export class HashMap<Key, T>
{
// DEFAULT CONSTRUCTOR
public constructor(
hash: HashFunction<Key> = std.hash,
equal: EqualFunction<Key> = std.equal_to
);
// COPY CONSTRUCTOR
public constructor(
obj: HashMap<Key, T>,
hash: HashFunction<Key> = std.hash,
equal: EqualFunction<Key> = std.equal_to
);
// ASSIGN CONSTRUCTOR
public constructor(
first: IForwardIterator<IPair<Key, T>>,
last: IForwardIterator<IPair<Key, T>>,
hash: HashFunction<Key> = std.hash,
equal: EqualFunction<Key> = std.equal_to
);
}
}
let hash_fn = function (val: number): number { return val ^ 122174131 - val; }
let equal_fn = function (x: number, y: number): boolean { return x == y; }
let words: std.HashMap<number, string> = new std.HashMap(hash_fn, equal_fn);
words.emplace(1, "First");
words.emplace(2, "Second");
let it = std.HashMap.Iterator<number, string> = words.find(1);
console.log(it.first, it.second); // 1, "First"
By adding the specialization features, observer methods referencing the specialized functions and accessor functions for hash buckets are also added.
- Observers
hash_function()
key_eq()
- Accessors to Hash Buckets
bucket()
bucket_count()
bucket_size()
load_factor()
max_load_factor()
reserve()
rehash()
Break Changes
Iterators're placed into their containers.
// CONTAINERS AND
let v: std.Vector<number>;
let m: std.TreeMap<number, number>;
let s: std.unordered_multiset<string>;
// THEIR ITERATORS
let v_it: std.Vector.ReverseIterator<number> = v.rbegin();
let m_it: std.TreeMap.Iterator<number, number> = m.begin();
let s_it: std.unordered_multiset.reverse_iterator<string> = s.rend();
Since v1.6 update, built-in iterators are moved into their domain containers.
C++ standard committee defined built-in iterators to be placed in their owner containers. Most compiler vendors follow the rule by defining iterator classes to somewhere hidden and making shortcuts of them using the typedef
statement.
#include <vector>
namespace std
{
template <typename T>
class vector
{
typedef _base::vector_iterator<T> iterator;
typedef _base::vector_reverse_iterator<T> reverse_iterator;
};
}
int main()
{
std::vector<int> v = {1, 2, 3, 4};
std::vector<int>::iterator it = v.begin();
return 0;
}
Unt...
TypeScript-STL v1.5.6
New Features
For ... of iteration
Until the v1.4, there had been only a generic iterator pattern using the begin()
and end()
methods. However, since this v1.5 update, you also can utilize the for ... of
iteration statement.
When you need to iterate full elements of a STL-container, then the for ... of iteration would be much convenient.
function old_iteration(): void
{
let v = new std.Vector<number>();
for (let i: number = 0; i < 10; ++i)
v.push_back(i);
// NEW FEATURE - FOR OF ITERATION
for (let it = v.begin(); !it.equal_to(v.end()); it = it.next())
console.log(it.value);
}
function for_of_vector(): void
{
let v = new std.Vector<number>();
for (let i: number = 0; i < 10; ++i)
v.push_back(i);
// NEW FEATURE - FOR OF ITERATION
for (let elem of v)
console.log(elem);
}
function for_of_map(): void
{
let m = new std.Map<string, number>();
m.emplace("First", 1);
m.emplace("Second", 2);
m.emplace("Third", 3);
for (let pair of m)
console.log(pair.first, pair.second);
}
<thread>
Since this TypeScript-STL v1.5 update, the <thread>
has implemented in. However, notice that; JavaScript code runs in only a thread. The function and classes in the <thread>
are all fake instances, based on the Promise
. They just imitate the <thread>
's behaviors.
async function test_sleep_for(): Promise<void>
{
await std.sleep_for(2000); // sleep for 2 seconds.
}
When you call the std.sleep_until()
function, then it doesn't lock your program. It just returns a Promise
object that calling the call-back function at the specified datetime.
<thread>
features implemented in the TypeScript-STL v1.5 are such below:
global functions
namespace std
{
export function sleep_for(ms: number): Promise<void>;
export function sleep_until(at: Date): Promise<void>;
export function lock(...items: ILockable[]): Promise<void>;
export function try_lock(...items: ILockable[]): number;
export interface ILockable
{
public lock(): Promise<void>;
public try_lock(): boolean;
}
}
mutex & timed_mutex
namespace std
{
export class Mutex implements ILockable
{
public lock(): Promise<void>;
public try_lock(): boolean;
public unlock(): void;
}
export class TimedMutex implements Mutex
{
public try_lock_for(ms: number): Promise<boolean>;
public try_lock_until(at: Date): Promise<boolean>;
}
}
shared_mutex
namespace std
{
export class SharedMutex implements Mutex
{
public lock_shared(): Promise<void>;
public try_lock_shared(): boolean;
public unlock_shared(): void;
}
export class SharedTimedMutex implements SharedMutex, TimedMutex
{
public try_lock_shared_for(ms: number): Promise<boolean>;
public try_lock_shared_until(at: Date): Promise<boolean>;
}
}
Break Changes
Exception extends Error
Due to the transpiler problem (Extending built-ins like Error, Array, and Map may no longer work), I determined Exception
not to extends the Error
class, the built-in-class of JavaScript.
However, not extending the Error class, tracing call stack had been difficult such below:
So I determined to roll back the Exception
class to extends the Error
again.
namespace std
{
export class Exception extends Error;
export class LogicError extends Exception;
export class RuntimeError extends Exception;
}
Container.swap
Until the v1.5 update, calling Container.swap()
function between heterogeneous containers was possible. However, since this v1.5 update, the Container.swap()
function works only for the homogeneous containers.
Additionally, until the v1.5 update, when the Container.swap()
is called, Iterator.source()
doesn't point the swapped container, but the old container. Such bug also has been fixed since this v1.5 update.
namespace std
{
export class List<T>
{
public swap(obj: List<T>); // ONLY SAME TYPE IS ALLOWED
// public swap(obj: base.Container<T>); -> HETEROGENEOUS -> IT IS TRUNCATED
}
}
TypeScript-STL v1.4.3
TreeMultiSet & TreeMultiMap
Faster key access
Until the v1.4
update, time complexity of key access on TreeSet
and TreeMap
had been O(log N) ~ O(N)
. When keys are not duplicated (unique), then those time complexity had been O(log N)
, however, the more duplicated key exists and you try to access to the duplicated key, then the time complexity had been more converged to the O(N)
.
However, since the v1.4
update, even how much duplicated key exists, time complexity of key access on TreeSet
and TreeMap
are exactly O(log N)
. Thus, key access on TreeSet
and TreeMap
are much faster than before.
Exact arrangement
Until the v1.4
update, when lots of duplicated key are inserted, then arrangement of the elements had been twisted and it even had caused the mis-structuration on the RB-Tree.
Since this v1.4
update, the mis-arrangement problem is clearly fixed. The RB-Tree doesn't be mis-structured more. Duplicated key elements, their sequence follows the word: "Enter first, then be the front". The principle is always be kept without reference to the relationships of nodes at the RB-Tree.
- Of course, you can twister the sequence manually by the insertion hint.
Allow comparisons function who covers the equal
.
The default comparison function of Tree-based containers are std.less and you also can specify the comparison function as you want.
However, the C++ (original) STL doesn't allow to specifying the comparison function who covers the equal. The specified comparison function must not cover the equal and if you violate the rule, then it causes not compile error, but the run-time error.
The violation doesn't be detected on compile level. Thus, when you specify a comparison function who covers the equal and you try to insert some duplicated key elements, then you may understand that you'd violated the rule by meeting the critical run-time error. I think it is very dangerous and it's a type of wrong design in C++/STL.
- I can understand the violation of comparison function who cover
equal
in TreeSet and TreeMap is right who doesn't allow the duplicated key element. However, they also can't detect the violation in the compilation level, thus it's still dangerous. - However, the TreeMultiSet and TreeMultiMap, who allow duplicated key, I don't understand the reason why.
In my TypeScript-STL, I allow you to specifying the comparison function to cover the equal
. Some expert developers, who know the RB-Tree well, may have a question: "If the comparison function can cover the equal, then how you prevent the twistering problem in the RB-Tree?" I solved the problem like below code:
Development Environments
Visual Code
Since the v1.4
update, TypeScript-STL uses Visual Studio Code (VSCode)
instead of the previous Visual Studio
. Configuration file for the Visual Code
is placed on the vscode/launch.json
Builder
Since v1.4
update, instead of Visual Studio
compiles, tests and prepares distribution by one click, build.js
will substitute the automation role.
By executing the build.js
with the command node build
, below steps will be proceeded:
- Compile TypeScript sources
1.1. At first, compile with (API) comments and generates the definition (d.ts
) file.
1.2. Re-compile, excluding (API) comments and the definition file (js
only).- When compile error occurs, then the builder will be halted.
- Execute testing unit.
- The testing unit will validate source codes with some testing functions.
- When error occurs, then the builder will be halted, too.
- Prepare distribution.
- Arrange built files to release.
Comments are only placed in the header (d.ts
)
I decided to shrink body (js
) file's size, by removing comments, even though it needs the duplicated compilation.
Thus, since the v1.4
update, (API) comments are only wrote on header, the definition (d.ts
) file. The body (js
) file does not contain any comment from now on, to shrink its file size.
New Features
<algorithm>
TypeScript-STL v1.3.8
Breaking Changes
Vector no longer extends the Array
As the ES2015
has newly approached, It's hard to extend built-in classes with the transpilers like TypeScript. The only way to extend those built-in classes are using setPrototypeOf
statement, which is not supported in IE10 and priors.
class Vector<T> extends Array<T>
{
constructor(...args: any[])
{
super();
Object.setPrototypeOf(this, Vector.prototype);
}
}
In such reason, TypeScript-STL's Vector and Exception do not extend Array and Error, the built-in classes, more. As the same reason, base.IContainer
is deprecated
//----
// ORDINARY VECTOR AND EXCEPTION, THEY HAD EXTENDED THE BUILT-IN CLASSES
//----
namespace std
{
export class Vector<T> extends Array<T> implements base.IContainer<T>;
export class Exception extends Error;
}
//----
// HOWEVER, IT DOES NOT EXTEND BUILT-INS MORE
//----
namespace std
{
export class Vector<T> extends base.Container<T>;
export class Exception;
}
Unnecessary comments are all deprecated
Unnecessary comments for API documentation are all deprecated. Private members and some methods start from the _
prefix, their symbols are all changed to the @hidden
.
namespace std.base
{
export abstract class MapContainer<Key, T>
{
/**
* @hidden
*/
private data_: _MapElementList<Key, T>;
/**
* @hidden
*/
protected _Handle_insert(first: MapIterator<Key, T>, last: MapIterator<Key, T>): void;
}
}
Error Fix
std.shuffle
& std.random_shuffle
There was an error on std.shuffle and std.random_shuffle when the range iterators are pointing to the end.
let container: std.Vector<number> = new std.Vector<number>();
containers.push(1 , 2, 3, 4, 5);
// NO PROBLEM IN THAT CASE
std.sort(container.begin(), container.end().prev());
//--------
// WHEN END() OR REND() IS POINTED, THEN ERROR HAD OCCURED
//--------
// HOWEVER, THE ERROR DOES NOT OCCURE MORE
std.sort(container.begin(), container.end());
std.sort(container.rbegin(), container.rend());
TypeScript-STL v1.2.4
IComparable has changed
IComparable.equal_to
->IComparable.equals
IComparable.hash
->IComparable.hashCode?
namespace std
{
export interface IComparable<T>
{
less(obj: T): boolean;
equals(obj: T): boolean;
hashCode?(): number;
}
}
Iteration of Associative Containers
Iteration of associative containers are much faster than before.
Associative Containers
- Set Containers
- Map Containers
Permutation Algorithm
Three functions about permutation algorithm are newly added.
TypeScript-STL v1.1.0
A minor update v1.1 has released.
New Features
std.for_each_n
- New feature of C++17
std.base.UniqueMap.extract
- New feature of C++17.
- Extract an element (Pair) from a UniqueMap container.
- Removes an element from a UniqueMap and returns the elemnet.
std.base.UniqueMap.insert_or_assign
- New feature of C++17
- Find matched key. If exists, change the mapped value. If not, then inserts the new element.
- It's same with the base.UniqueMap.set(). I have predicted that such function will be added in C++17, however, failed to predict its name.
std.base.UniqueMap.emplace
In C++ STL, emplace() is a method who calls insert() with new T object, constructed from arguments of varadic template in the emplace(). Using paraeterms in emplace() to creating a new T object, it's possible because the characteristics of C++ template system. Implementing it is not possible in the JavaScript.
However, the map containers, they are storing elements with capsulizing key and mapped value into a std.Pair object. It means that the map containers, their value type is specified exactly (as std.Pair). Thus only the map containers, realizing emplace() is feasible.
template <typename T, typename Alloc = std::allocator<T>, typename Compare = std::less<T>>
class unordered_set
{
public:
template <typename ...Args>
void emplace(Args&&... args)
{
// VARADIC TEMPLATE, IT'S ONLY POSSIBLE IN C++
T &element(args...);
insert(element);
}
}
std.base.MultiMap.emplace
- Same with the std.base.UniqueMap.emplace
std.base.MapContainer.emplace_hint
- Same with the std.base.UniqueMap.emplace
std.base.UniqueSet.extract
- Same with the std.base.UniqueMap.extract
std.base.UniqueSet.insert_or_assign
- Same with the std.base.UniqueMap.insert_or_assign
Fixed Errors
std.Vector.iterator.advance
- Calling advance() with minus value had ignored.
- When the iterator is pointing to the Vector.end().
std.Deque.iterator.advance
- Same with the std.Vector.iterator.advance
std.Deque.erase
- There had been an error that a Deque is being locked.
- When only an element was existed in a Deque and the last element is deleted, the last row in the matrix (who is storing Deque's elements in) is also deleted. Thus any row exists in the matrix and it causes any element can be inserted into the Deque.
std.reverse
- It had not worked at all.
Refactoring
Sorting algorithm on the std.List.sort() has changed.
Until before, std.sort() had subrogated the std.List.sort().
- Elements in std.List are copied into a new std.Vector
- After sorting via std.sort(), elements in the std.Vector are assigned the the std.List.
class List<T>
{
public sort(): void
{
// UNTIL BEFORE, STD.SORT HAD SUBROGATED
let vector: Vector<T> = new Vector<T>(this.begin(), this.end());
std.sort(vector.begin(), vector.end());
this.assign(vector.begin(), vector.end());
}
}
From now on, the sorting is done by std.List itself.
Protected methods are renamed.
- Some methods, they can't avoid to be protected accessor type in domain reason.
- They're all derived from std.base.MapContainer and many methods of them are protected methods.
- The protected members, they can be accessed by subclasses and it may cause critical error.
- The best way is to changing them to be private, but it's impossible. Instead, I renamed them start from capital letter and have prefix underscore (_).
std.base.SetContainer.handle_insaert
-> std.base.SetContainer._Handle_insertstd.base.MapContainer.insert_by_hint
->std.base.MapContainer._Insert_by_hint
TypeScript v1.0.0
Finally, TypeScript-STL has fully implemented and released to v1.0.
TypeScript-STL supports all type of STL containers and algorithms
Containers
- Linear containers
- Associative Containers
- Adaptor Containers