Make HashTable work complex types as key
- Add toHash() function for String - The key type shouldn't match exact for a lookup. The key type and lookup key type should be comparable. - Move elements when inserting if passed by value.
This commit is contained in:
parent
533fa3b023
commit
5e901f505c
@ -82,12 +82,18 @@ package struct Bucket(K, V = void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool opEquals(ref inout(K) key) inout
|
void moveKey(ref K key)
|
||||||
|
{
|
||||||
|
move(key, this.key());
|
||||||
|
this.status = BucketStatus.used;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(T)(ref const T key) const
|
||||||
{
|
{
|
||||||
return this.status == BucketStatus.used && this.key == key;
|
return this.status == BucketStatus.used && this.key == key;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool opEquals(ref inout(typeof(this)) that) inout
|
bool opEquals(ref const(typeof(this)) that) const
|
||||||
{
|
{
|
||||||
return key == that.key && this.status == that.status;
|
return key == that.key && this.status == that.status;
|
||||||
}
|
}
|
||||||
@ -180,7 +186,7 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
* Returns bucket position for `hash`. `0` may mean the 0th position or an
|
* Returns bucket position for `hash`. `0` may mean the 0th position or an
|
||||||
* empty `buckets` array.
|
* empty `buckets` array.
|
||||||
*/
|
*/
|
||||||
size_t locateBucket(ref const Key key) const
|
size_t locateBucket(T)(ref const T key) const
|
||||||
{
|
{
|
||||||
return this.array.length == 0 ? 0 : hasher(key) % bucketCount;
|
return this.array.length == 0 ? 0 : hasher(key) % bucketCount;
|
||||||
}
|
}
|
||||||
@ -304,7 +310,7 @@ package struct HashArray(alias hasher, K, V = void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool opBinaryRight(string op : "in")(ref inout(Key) key) inout
|
bool opBinaryRight(string op : "in", T)(ref const T key) const
|
||||||
{
|
{
|
||||||
foreach (ref e; this.array[locateBucket(key) .. $])
|
foreach (ref e; this.array[locateBucket(key) .. $])
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.container.hashtable;
|
module tanya.container.hashtable;
|
||||||
|
|
||||||
|
import tanya.algorithm.mutation;
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.entry;
|
import tanya.container.entry;
|
||||||
import tanya.hash.lookup;
|
import tanya.hash.lookup;
|
||||||
@ -155,7 +156,7 @@ struct Range(T)
|
|||||||
* hasher = Hash function for $(D_PARAM Key).
|
* hasher = Hash function for $(D_PARAM Key).
|
||||||
*/
|
*/
|
||||||
struct HashTable(Key, Value, alias hasher = hash)
|
struct HashTable(Key, Value, alias hasher = hash)
|
||||||
if (is(typeof(hasher(Key.init)) == size_t))
|
if (is(typeof(((Key k) => hasher(k))(Key.init)) == size_t))
|
||||||
{
|
{
|
||||||
private alias HashArray = .HashArray!(hasher, Key, Value);
|
private alias HashArray = .HashArray!(hasher, Key, Value);
|
||||||
private alias Buckets = HashArray.Buckets;
|
private alias Buckets = HashArray.Buckets;
|
||||||
@ -468,15 +469,29 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
*
|
*
|
||||||
* Returns: Just inserted element.
|
* Returns: Just inserted element.
|
||||||
*/
|
*/
|
||||||
ref Value opIndexAssign(Value value, Key key)
|
ref Value opIndexAssign()(auto ref Value value, auto ref Key key)
|
||||||
{
|
{
|
||||||
auto e = ((ref v) @trusted => &this.data.insert(v))(key);
|
auto e = ((ref v) @trusted => &this.data.insert(v))(key);
|
||||||
if (e.status != BucketStatus.used)
|
if (e.status != BucketStatus.used)
|
||||||
|
{
|
||||||
|
static if (__traits(isRef, key))
|
||||||
{
|
{
|
||||||
e.key = key;
|
e.key = key;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.moveKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static if (__traits(isRef, value))
|
||||||
|
{
|
||||||
return e.kv.value = value;
|
return e.kv.value = value;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return e.kv.value = move(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
@ -505,7 +520,7 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
*
|
*
|
||||||
* Returns: The number of the inserted elements with a unique key.
|
* Returns: The number of the inserted elements with a unique key.
|
||||||
*/
|
*/
|
||||||
size_t insert(KeyValue keyValue)
|
size_t insert(ref KeyValue keyValue)
|
||||||
{
|
{
|
||||||
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
|
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
|
||||||
size_t inserted;
|
size_t inserted;
|
||||||
@ -518,6 +533,20 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
return inserted;
|
return inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
size_t insert(KeyValue keyValue)
|
||||||
|
{
|
||||||
|
auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
|
||||||
|
size_t inserted;
|
||||||
|
if (e.status != BucketStatus.used)
|
||||||
|
{
|
||||||
|
e.moveKey(keyValue.key);
|
||||||
|
inserted = 1;
|
||||||
|
}
|
||||||
|
move(keyValue.value, e.kv.value);
|
||||||
|
return inserted;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
@ -572,13 +601,15 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
* Find the element with the key $(D_PARAM key).
|
* Find the element with the key $(D_PARAM key).
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
|
* T = Type comparable with the key type, used for the lookup.
|
||||||
* key = The key to be find.
|
* key = The key to be find.
|
||||||
*
|
*
|
||||||
* Returns: The value associated with $(D_PARAM key).
|
* Returns: The value associated with $(D_PARAM key).
|
||||||
*
|
*
|
||||||
* Precondition: Element with $(D_PARAM key) is in this hash table.
|
* Precondition: Element with $(D_PARAM key) is in this hash table.
|
||||||
*/
|
*/
|
||||||
ref Value opIndex(Key key)
|
ref Value opIndex(T)(auto ref const T key)
|
||||||
|
if (ifTestable!(T, a => Key.init == a))
|
||||||
{
|
{
|
||||||
const code = this.data.locateBucket(key);
|
const code = this.data.locateBucket(key);
|
||||||
|
|
||||||
@ -634,12 +665,14 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
* Looks for $(D_PARAM key) in this hash table.
|
* Looks for $(D_PARAM key) in this hash table.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
|
* T = Type comparable with the key type, used for the lookup.
|
||||||
* key = The key to look for.
|
* key = The key to look for.
|
||||||
*
|
*
|
||||||
* Returns: $(D_KEYWORD true) if $(D_PARAM key) exists in the hash table,
|
* Returns: $(D_KEYWORD true) if $(D_PARAM key) exists in the hash table,
|
||||||
* $(D_KEYWORD false) otherwise.
|
* $(D_KEYWORD false) otherwise.
|
||||||
*/
|
*/
|
||||||
bool opBinaryRight(string op : "in")(auto ref inout(Key) key) inout
|
bool opBinaryRight(string op : "in", T)(auto ref const T key) const
|
||||||
|
if (ifTestable!(T, a => Key.init == a))
|
||||||
{
|
{
|
||||||
return key in this.data;
|
return key in this.data;
|
||||||
}
|
}
|
||||||
@ -739,6 +772,9 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
static assert(is(HashTable!(string, int) a));
|
static assert(is(HashTable!(string, int) a));
|
||||||
static assert(is(const HashTable!(string, int)));
|
static assert(is(const HashTable!(string, int)));
|
||||||
static assert(isForwardRange!(HashTable!(string, int).Range));
|
static assert(isForwardRange!(HashTable!(string, int).Range));
|
||||||
|
|
||||||
|
static assert(is(HashTable!(int, int, (ref const int) => size_t.init)));
|
||||||
|
static assert(is(HashTable!(int, int, (int) => size_t.init)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructs by reference
|
// Constructs by reference
|
||||||
@ -809,3 +845,36 @@ if (is(typeof(hasher(Key.init)) == size_t))
|
|||||||
assert(hashtable[-1131293824] == 6);
|
assert(hashtable[-1131293824] == 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@nogc nothrow pure @safe unittest
|
||||||
|
{
|
||||||
|
static struct String
|
||||||
|
{
|
||||||
|
bool opEquals(string) const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref const string) const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(String) const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool opEquals(ref const String) const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t toHash() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static assert(is(typeof("asdf" in HashTable!(String, int)())));
|
||||||
|
static assert(is(typeof(HashTable!(String, int)()["asdf"])));
|
||||||
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
module tanya.container.set;
|
module tanya.container.set;
|
||||||
|
|
||||||
import tanya.algorithm.mutation;
|
|
||||||
import tanya.container.array;
|
import tanya.container.array;
|
||||||
import tanya.container.entry;
|
import tanya.container.entry;
|
||||||
import tanya.hash.lookup;
|
import tanya.hash.lookup;
|
||||||
@ -460,6 +459,17 @@ if (is(typeof(hasher(T.init)) == size_t))
|
|||||||
*
|
*
|
||||||
* Returns: Amount of new elements inserted.
|
* Returns: Amount of new elements inserted.
|
||||||
*/
|
*/
|
||||||
|
size_t insert(ref T value)
|
||||||
|
{
|
||||||
|
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
|
||||||
|
if (e.status != BucketStatus.used)
|
||||||
|
{
|
||||||
|
e.moveKey(value);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
size_t insert(T value)
|
size_t insert(T value)
|
||||||
{
|
{
|
||||||
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
|
auto e = ((ref v) @trusted => &this.data.insert(v))(value);
|
||||||
@ -551,12 +561,14 @@ if (is(typeof(hasher(T.init)) == size_t))
|
|||||||
* $(D_KEYWORD in) operator.
|
* $(D_KEYWORD in) operator.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
|
* U = Type comparable with the element type, used for the lookup.
|
||||||
* value = Element to be searched for.
|
* value = Element to be searched for.
|
||||||
*
|
*
|
||||||
* Returns: $(D_KEYWORD true) if the given element exists in the container,
|
* Returns: $(D_KEYWORD true) if the given element exists in the container,
|
||||||
* $(D_KEYWORD false) otherwise.
|
* $(D_KEYWORD false) otherwise.
|
||||||
*/
|
*/
|
||||||
bool opBinaryRight(string op : "in")(auto ref inout(T) value) inout
|
bool opBinaryRight(string op : "in", U)(auto ref const U value) const
|
||||||
|
if (ifTestable!(U, a => T.init == a))
|
||||||
{
|
{
|
||||||
return value in this.data;
|
return value in this.data;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import std.algorithm.mutation : bringToFront, copy;
|
|||||||
import std.algorithm.searching;
|
import std.algorithm.searching;
|
||||||
import tanya.algorithm.comparison;
|
import tanya.algorithm.comparison;
|
||||||
import tanya.algorithm.mutation;
|
import tanya.algorithm.mutation;
|
||||||
|
import tanya.hash.lookup;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
import tanya.meta.trait;
|
import tanya.meta.trait;
|
||||||
import tanya.meta.transform;
|
import tanya.meta.transform;
|
||||||
@ -1614,6 +1615,16 @@ struct String
|
|||||||
assert(s == "Казнить, нельзя помиловать.");
|
assert(s == "Казнить, нельзя помиловать.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the hash value for the string.
|
||||||
|
*
|
||||||
|
* Returns: Hash value for the string.
|
||||||
|
*/
|
||||||
|
size_t toHash() const @nogc nothrow pure @safe
|
||||||
|
{
|
||||||
|
return hash(get);
|
||||||
|
}
|
||||||
|
|
||||||
mixin DefaultAllocator;
|
mixin DefaultAllocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user