From 7e7a539122b68f31b9517a666c4807e8b5131a23 Mon Sep 17 00:00:00 2001 From: "Quirin F. Schroll" Date: Tue, 2 Dec 2025 19:02:30 +0100 Subject: [PATCH 1/4] Rework AA properties table --- spec/hash-map.dd | 58 ++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/spec/hash-map.dd b/spec/hash-map.dd index 77b3b78e29..5f4a8041fc 100644 --- a/spec/hash-map.dd +++ b/spec/hash-map.dd @@ -430,55 +430,65 @@ $(SPEC_RUNNABLE_EXAMPLE_RUN --- ) -$(H2 $(LNAME2 properties, Properties)) +$(H2 $(LNAME2 properties, Properties and Operations)) -$(P Properties for associative arrays are:) +$(P Properties and operations for associative arrays are:) $(TABLE_2COLS Associative Array Properties, - $(THEAD Property, Description) - $(TROW $(D .sizeof), Returns the size of the reference to the associative - array; it is 4 in 32-bit builds and 8 on 64-bit builds.) - $(TROW $(D .length), $(ARGS Returns number of values in the + $(THEAD Property/Operation, Description) + $(TROW $(D sizeof), $(ARGS The size of the reference to the associative + array; it is 4 in 32-bit builds and 8 on 64-bit builds.)) + $(TROW $(D empty), $(ARGS $(D true) if the associative array has no elements; otherwise $(D false).)) + $(TROW $(D length), $(ARGS The number of values in the associative array. Unlike for dynamic arrays, it is read-only.)) - $(TROW $(D .dup), Create a new associative array of the same size + $(TROW $(D dup()), Creates a new associative array of the same size and copy the contents of the associative array into it.) - $(TROW $(D .keys), $(ARGS Returns dynamic array, the elements of which are the keys in + $(TROW $(D keys()), $(ARGS Returns dynamic array, the elements of which are the keys in the associative array.)) - $(TROW $(D .values), $(ARGS Returns dynamic array, the elements of which are the values in + $(TROW $(D values()), $(ARGS Returns dynamic array, the elements of which are the values in the associative array.)) - $(TROW $(D .rehash), $(ARGS Reorganizes the associative array in place so that lookups + $(TROW $(D rehash()), $(ARGS Reorganizes the associative array in place so that lookups are more efficient. $(D rehash) is effective when, for example, the program is done loading up a symbol table and now needs fast lookups in it. Returns a reference to the reorganized array.)) - $(TROW $(D .clear), $(ARGS Removes all remaining keys and values from an associative array. + $(TROW $(D clear()), $(ARGS Removes all remaining keys and values from an associative array. The array is not rehashed after removal, to allow for the existing storage to be reused. This will affect all references to the same instance and is not equivalent to `destroy(aa)` which only sets the current reference to `null`)) - $(TROW $(D .byKey()), $(ARGS Returns a forward range suitable for use + $(TROW $(D byKey()), $(ARGS Returns a forward range suitable for use as a $(I ForeachAggregate) to a $(GLINK2 statement, ForeachStatement) which will iterate over the keys of the associative array.)) - $(TROW $(D .byValue()), $(ARGS Returns a forward range suitable for use + $(TROW $(D byValue()), $(ARGS Returns a forward range suitable for use as a $(I ForeachAggregate) to a $(GLINK2 statement, ForeachStatement) which will iterate over the values of the associative array.)) - $(TROW $(D .byKeyValue()), $(ARGS Returns a forward range suitable for + $(TROW $(D byKeyValue()), $(ARGS Returns a forward range suitable for use as a $(I ForeachAggregate) to a $(GLINK2 statement, ForeachStatement) which will iterate over key-value pairs of the associative array. The returned pairs are represented by an opaque type - with $(D .key) and $(D .value) properties for accessing the key and + with $(D key) and $(D value) properties for accessing the key and value of the pair, respectively. Note that this is a low-level interface to iterating over the associative array and is not compatible with the $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) type in Phobos. For compatibility with `Tuple`, use $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead.)) - $(TROW $(D .get(Key key, lazy Value defVal)), - $(ARGS Looks up $(D key); if it exists returns corresponding value - else evaluates and returns $(D defVal).)) - $(TROW $(D .require(Key key, lazy Value value)), - $(ARGS Looks up $(D key); if it exists returns corresponding value - else evaluates $(D value), adds it to the associative array and returns it.)) - $(TROW $(D .update(Key key, Value delegate() create, Value delegate(Value) update)), - $(ARGS Looks up $(D key); if it exists applies the $(D update) delegate - else evaluates the $(D create) delegate and adds it to the associative array)) + $(TROW $(D Value get(Key key, lazy Value defVal)), + $(ARGS Looks up $(D key); + if it exists, returns corresponding value; + otherwise evaluates and returns $(D defVal) without associating it with $(D key).)) + $(TROW $(D ref Value require(Key key, lazy Value value)), + $(ARGS Looks up $(D key); + if it exists, returns corresponding value by reference; + otherwise evaluates $(D value) and associates it with $(D key) in the associative array, + then returns the newly stored value by reference.)) + $(TROW $(D void update(Key key, Value delegate() creator, Value delegate(Value) updater)), + $(ARGS Looks up $(D key); + if it exists, invokes the $(D updater) on the value (passed by reference if possible) + and associates the result with the key; + otherwise invokes the $(D creator) and associates the result with $(D key) in the associative array)) + $(TROW $(D void update(Key key, Value delegate() creator, void delegate(ref Value) updater)), + $(ARGS Looks up $(D key); + if it exists, invokes the $(D updater) on the value (passed by reference) + otherwise invokes the $(D creator) and associates the result with $(D key) in the associative array)) ) $(H2 $(LNAME2 examples, Examples)) From b9d534bc2087b7f876aa62d61fe29aac5bb6abe4 Mon Sep 17 00:00:00 2001 From: "Quirin F. Schroll" Date: Thu, 4 Dec 2025 11:37:54 +0100 Subject: [PATCH 2/4] `empty` is not defined in object.d --- spec/hash-map.dd | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/hash-map.dd b/spec/hash-map.dd index 5f4a8041fc..5357f6bad8 100644 --- a/spec/hash-map.dd +++ b/spec/hash-map.dd @@ -438,7 +438,6 @@ $(P Properties and operations for associative arrays are:) $(THEAD Property/Operation, Description) $(TROW $(D sizeof), $(ARGS The size of the reference to the associative array; it is 4 in 32-bit builds and 8 on 64-bit builds.)) - $(TROW $(D empty), $(ARGS $(D true) if the associative array has no elements; otherwise $(D false).)) $(TROW $(D length), $(ARGS The number of values in the associative array. Unlike for dynamic arrays, it is read-only.)) $(TROW $(D dup()), Creates a new associative array of the same size From bcff58ba1092432b2e05b106158f7e768f8b7ce3 Mon Sep 17 00:00:00 2001 From: "Quirin F. Schroll" Date: Thu, 4 Dec 2025 13:03:47 +0100 Subject: [PATCH 3/4] Improve section associative array properties and operations --- spec/hash-map.dd | 84 +++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/spec/hash-map.dd b/spec/hash-map.dd index 5357f6bad8..8092239503 100644 --- a/spec/hash-map.dd +++ b/spec/hash-map.dd @@ -432,44 +432,39 @@ $(SPEC_RUNNABLE_EXAMPLE_RUN $(H2 $(LNAME2 properties, Properties and Operations)) -$(P Properties and operations for associative arrays are:) - $(TABLE_2COLS Associative Array Properties, - $(THEAD Property/Operation, Description) + $(THEAD Property name, Description) $(TROW $(D sizeof), $(ARGS The size of the reference to the associative array; it is 4 in 32-bit builds and 8 on 64-bit builds.)) $(TROW $(D length), $(ARGS The number of values in the associative array. Unlike for dynamic arrays, it is read-only.)) - $(TROW $(D dup()), Creates a new associative array of the same size - and copy the contents of the associative array into it.) - $(TROW $(D keys()), $(ARGS Returns dynamic array, the elements of which are the keys in - the associative array.)) - $(TROW $(D values()), $(ARGS Returns dynamic array, the elements of which are the values in - the associative array.)) + ) + + $(P There is no built-in `empty` property. + Phobos provides an implementation of `empty` in `std.range.primitives`.) + + $(TABLE_2COLS Associative Array Operations, + $(THEAD Operation, Description) + $(TROW $(D dup()), Returns `null` If the associative array is `null`; + otherwise returns a newly allocated associative array with copies of the keys and values of the associative array.) + $(TROW $(D keys()), $(ARGS Returns a newly allocated dynamic array containing copies of the keys in + the associative array in an order that is consistent with `values()` but otherwise unspecified.)) + $(TROW $(D values()), $(ARGS Returns a newly allocated dynamic arraycontaining copies of the values in + the associative array in an order that is consistent with `keys()` but otherwise unspecified.)) $(TROW $(D rehash()), $(ARGS Reorganizes the associative array in place so that lookups - are more efficient. $(D rehash) is effective when, for example, - the program is done loading up a symbol table and now needs - fast lookups in it. Returns a reference to the reorganized array.)) - $(TROW $(D clear()), $(ARGS Removes all remaining keys and values from an associative array. - The array is not rehashed after removal, to allow for the existing storage to be reused. + are more efficient.)) + $(TROW $(D clear()), $(ARGS Removes all keys and values from an associative array. + The array is not rehashed after removal to allow for the existing storage to be reused. This will affect all references to the same instance and is not equivalent to `destroy(aa)` which only sets the current reference to `null`)) - $(TROW $(D byKey()), $(ARGS Returns a forward range suitable for use - as a $(I ForeachAggregate) to a $(GLINK2 statement, ForeachStatement) - which will iterate over the keys of the associative array.)) - $(TROW $(D byValue()), $(ARGS Returns a forward range suitable for use - as a $(I ForeachAggregate) to a $(GLINK2 statement, ForeachStatement) - which will iterate over the values of the associative array.)) - $(TROW $(D byKeyValue()), $(ARGS Returns a forward range suitable for - use as a $(I ForeachAggregate) to a $(GLINK2 statement, - ForeachStatement) which will iterate over key-value pairs of the - associative array. The returned pairs are represented by an opaque type - with $(D key) and $(D value) properties for accessing the key and - value of the pair, respectively. Note that this is a low-level - interface to iterating over the associative array and is not compatible - with the $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) - type in Phobos. For compatibility with `Tuple`, use - $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead.)) + $(TROW $(D byKey()), $(ARGS Returns a forward range enumerating the keys by reference + in an order that is consistent with `byValue()` but otherwise unspecified. + $(B Bug:) The keys are provided as mutable, but mutating them is undefined behavior.)) + $(TROW $(D byValue()), $(ARGS Returns a forward range enumerating the values by reference + in an order that is consistent with `byKey()` but otherwise unspecified.)) + $(TROW $(D byKeyValue()), $(ARGS Returns a forward range enumerating opaque objects that provide a `key` and a `value` property + in an unspecified order. The two properties return their result by reference. + $(B Bug:) The keys are provided as mutable, but mutating them is undefined behavior.)) $(TROW $(D Value get(Key key, lazy Value defVal)), $(ARGS Looks up $(D key); if it exists, returns corresponding value; @@ -490,6 +485,35 @@ $(P Properties and operations for associative arrays are:) otherwise invokes the $(D creator) and associates the result with $(D key) in the associative array)) ) + $(P Calling $(D rehash) is effective when, for example, + the program is done loading up a symbol table and now needs + fast lookups in it. Returns a reference to the reorganized array.) + + $(P The order of keys/values returned by + `keys()` and `values()` as well as + `byKey()`, `byValue()`, and `byKeyValue()` is unspecified, + but is guaranteed to be consistent to each other + as long as the associative array has not been reorganized, + e.g. by adding or removing keys between the calls. + Associating an existing key to a new value does not reorganize an associative array. + Reorganizing an associative array invalidates any present input ranges + returned by `byKey()`, `byValue()`, and `byKeyValue()`.) + + $(P Calling `keys()` and `values()` incurs an allocation unless the associative array is `null` or empty. + Use them if you need an independent copy of the keys and/or values; + otherwise consider `byKey()` and `byValue()`.) + + $(P Note that `byKeyValue()` is not compatible + with the $(LINK2 $(ROOT_DIR)phobos/std_typecons.html#.Tuple,`Tuple`) + type in Phobos. For compatibility with `Tuple`, use + $(LINK2 $(ROOT_DIR)phobos/std_array.html#.byPair,std.array.byPair) instead.) + + $(P Associative arrays support $(LINK2 $(ROOT_DIR)spec/statement.html#foreach_over_associative_arrays, `foreach`) + directly for value iteration and key-value iteration. + It is recommended to use `ref` on the key and value variables to avoid unnecessary copies. + For iterating the keys only, use key-value iteration and ignore the value. + Use `byKey()`, `byValue()`, or `byKeyValue()` for more elaborate cases such as range algorithms.) + $(H2 $(LNAME2 examples, Examples)) $(H3 $(LNAME2 aa_example, Associative Array Example: word count)) From ce716ea129e500ecaff409832514ccbfe81eadb0 Mon Sep 17 00:00:00 2001 From: "Quirin F. Schroll" Date: Thu, 4 Dec 2025 15:06:46 +0100 Subject: [PATCH 4/4] Split into different tables --- spec/hash-map.dd | 70 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/spec/hash-map.dd b/spec/hash-map.dd index 8092239503..278cdf8dac 100644 --- a/spec/hash-map.dd +++ b/spec/hash-map.dd @@ -333,8 +333,8 @@ $(H2 $(LNAME2 advanced_updating, Advanced updating)) $(P Sometimes it is necessary to perform different operations depending on whether a value already exists or needs to be constructed. The $(D update) function provides a means to construct a new value via the - $(D create) delegate or update an existing value via the $(D update) - delegate. The $(D update) operation avoids the need to perform multiple + $(D creator) or update an existing value via the $(D updater). + The $(D update) operation avoids the need to perform multiple key lookups.) $(SPEC_RUNNABLE_EXAMPLE_RUN @@ -443,20 +443,27 @@ $(H2 $(LNAME2 properties, Properties and Operations)) $(P There is no built-in `empty` property. Phobos provides an implementation of `empty` in `std.range.primitives`.) - $(TABLE_2COLS Associative Array Operations, - $(THEAD Operation, Description) + $(TABLE_2COLS Associative Array Organizing Operations, $(TROW $(D dup()), Returns `null` If the associative array is `null`; otherwise returns a newly allocated associative array with copies of the keys and values of the associative array.) - $(TROW $(D keys()), $(ARGS Returns a newly allocated dynamic array containing copies of the keys in - the associative array in an order that is consistent with `values()` but otherwise unspecified.)) - $(TROW $(D values()), $(ARGS Returns a newly allocated dynamic arraycontaining copies of the values in - the associative array in an order that is consistent with `keys()` but otherwise unspecified.)) $(TROW $(D rehash()), $(ARGS Reorganizes the associative array in place so that lookups are more efficient.)) $(TROW $(D clear()), $(ARGS Removes all keys and values from an associative array. The array is not rehashed after removal to allow for the existing storage to be reused. This will affect all references to the same instance and is not equivalent to `destroy(aa)` which only sets the current reference to `null`)) + ) + + $(P Calling $(D rehash) is effective when, for example, + the program is done loading up a symbol table and now needs + fast lookups in it. Returns a reference to the reorganized array.) + + $(TABLE_2COLS Associative Array Iteration Operations, + $(THEAD Operation, Description) + $(TROW $(D keys()), $(ARGS Returns a newly allocated dynamic array containing copies of the keys in + the associative array in an order that is consistent with `values()` but otherwise unspecified.)) + $(TROW $(D values()), $(ARGS Returns a newly allocated dynamic arraycontaining copies of the values in + the associative array in an order that is consistent with `keys()` but otherwise unspecified.)) $(TROW $(D byKey()), $(ARGS Returns a forward range enumerating the keys by reference in an order that is consistent with `byValue()` but otherwise unspecified. $(B Bug:) The keys are provided as mutable, but mutating them is undefined behavior.)) @@ -465,37 +472,15 @@ $(H2 $(LNAME2 properties, Properties and Operations)) $(TROW $(D byKeyValue()), $(ARGS Returns a forward range enumerating opaque objects that provide a `key` and a `value` property in an unspecified order. The two properties return their result by reference. $(B Bug:) The keys are provided as mutable, but mutating them is undefined behavior.)) - $(TROW $(D Value get(Key key, lazy Value defVal)), - $(ARGS Looks up $(D key); - if it exists, returns corresponding value; - otherwise evaluates and returns $(D defVal) without associating it with $(D key).)) - $(TROW $(D ref Value require(Key key, lazy Value value)), - $(ARGS Looks up $(D key); - if it exists, returns corresponding value by reference; - otherwise evaluates $(D value) and associates it with $(D key) in the associative array, - then returns the newly stored value by reference.)) - $(TROW $(D void update(Key key, Value delegate() creator, Value delegate(Value) updater)), - $(ARGS Looks up $(D key); - if it exists, invokes the $(D updater) on the value (passed by reference if possible) - and associates the result with the key; - otherwise invokes the $(D creator) and associates the result with $(D key) in the associative array)) - $(TROW $(D void update(Key key, Value delegate() creator, void delegate(ref Value) updater)), - $(ARGS Looks up $(D key); - if it exists, invokes the $(D updater) on the value (passed by reference) - otherwise invokes the $(D creator) and associates the result with $(D key) in the associative array)) ) - $(P Calling $(D rehash) is effective when, for example, - the program is done loading up a symbol table and now needs - fast lookups in it. Returns a reference to the reorganized array.) - - $(P The order of keys/values returned by + $(P The order of keys and values returned by `keys()` and `values()` as well as `byKey()`, `byValue()`, and `byKeyValue()` is unspecified, but is guaranteed to be consistent to each other as long as the associative array has not been reorganized, e.g. by adding or removing keys between the calls. - Associating an existing key to a new value does not reorganize an associative array. + Associating a new value to an existing key does not reorganize an associative array. Reorganizing an associative array invalidates any present input ranges returned by `byKey()`, `byValue()`, and `byKeyValue()`.) @@ -514,6 +499,27 @@ $(H2 $(LNAME2 properties, Properties and Operations)) For iterating the keys only, use key-value iteration and ignore the value. Use `byKey()`, `byValue()`, or `byKeyValue()` for more elaborate cases such as range algorithms.) + $(TABLE_2COLS Associative Array Elaborate Lookup Operations, + $(TROW $(D Value get(Key key, lazy Value defVal)), + $(ARGS Looks up $(D key); + if it exists, returns corresponding value; + otherwise evaluates and returns $(D defVal) without associating it with $(D key).)) + $(TROW $(D ref Value require(Key key, lazy Value value)), + $(ARGS Looks up $(D key); + if it exists, returns corresponding value by reference; + otherwise evaluates $(D value) and associates it with $(D key) in the associative array, + then returns the newly stored value by reference.)) + $(TROW $(D void update(Key key, Creator creator, Updater updater)), + $(ARGS Looks up $(D key); + if it exists, invokes the `updater(value)` on the value (passed by reference if possible) + and then, unless `updater(value)` is `void`, associates the result with the key; + otherwise invokes `creator()` and associates the result with `key` in the associative array.)) + ) + + $(P The `update` operation works with any `creator` and `updater` that is invokable as specified. + An updater can modify the value in-place if it binds its argument by reference. + A `void` returning updater with by-reference parameter is recommended to avoid unnecessary copies.) + $(H2 $(LNAME2 examples, Examples)) $(H3 $(LNAME2 aa_example, Associative Array Example: word count))