Add some missing Dict and Set functions

Also remove some unnecessary Hash and Eq restrictions
This commit is contained in:
Richard Feldman 2023-06-27 11:38:38 -04:00
parent 82a2f3eb99
commit ed9d9b12f5
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
4 changed files with 104 additions and 5 deletions

View file

@ -7,6 +7,7 @@ interface Dict
clear,
capacity,
len,
isEmpty,
get,
contains,
insert,
@ -21,6 +22,8 @@ interface Dict
insertAll,
keepShared,
removeAll,
map,
joinMap,
]
imports [
Bool.{ Bool, Eq },
@ -139,12 +142,12 @@ empty = \{} ->
## Returns the max number of elements the dictionary can hold before requiring a rehash.
## ```
## foodDict =
## Dict.empty {}
## |> Dict.insert "apple" "fruit"
## Dict.empty {}
## |> Dict.insert "apple" "fruit"
##
## capacityOfDict = Dict.capacity foodDict
## ```
capacity : Dict k v -> Nat | k has Hash & Eq
capacity : Dict * * -> Nat
capacity = \@Dict { dataIndices } ->
cap = List.len dataIndices
@ -192,10 +195,20 @@ fromList = \data ->
## |> Dict.len
## |> Bool.isEq 3
## ```
len : Dict k v -> Nat | k has Hash & Eq
len : Dict * * -> Nat
len = \@Dict { size } ->
size
## Check if the dictinoary is empty.
## ```
## Dict.isEmpty (Dict.empty {} |> Dict.insert "key" 42)
##
## Dict.isEmpty (Dict.empty {})
## ```
isEmpty : Dict * * -> Bool
isEmpty = \@Dict { size } ->
size == 0
## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
## ```
## songs =
@ -225,6 +238,28 @@ clear = \@Dict { metadata, dataIndices, data } ->
size: 0,
}
## Convert each value in the dictionary to something new, by calling a conversion
## function on each of them which receives both the key and the old value. Then return a
## new dictionary containing the same keys and the converted values.
map : Dict k a, (k, a -> b) -> Dict k b | k has Hash & Eq, b has Hash & Eq
map = \dict, transform ->
init = withCapacity (capacity dict)
walk dict init \answer, k, v ->
insert answer k (transform k v)
## Like [Dict.map], except the transformation function wraps the return value
## in a dictionary. At the end, all the dictionaries get joined together
## (using [Dict.insertAll]) into one dictionary.
##
## You may know a similar function named `concatMap` in other languages.
joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y | a has Hash & Eq, x has Hash & Eq
joinMap = \dict, transform ->
init = withCapacity (capacity dict) # Might be a pessimization
walk dict init \answer, k, v ->
insertAll answer (transform k v)
## Iterate through the keys and values in the dictionary and call the provided
## function with signature `state, k, v -> state` for each value, with an
## initial `state` value provided for the first call.

View file

@ -208,6 +208,9 @@ interface List
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
# separator so List.isEmpty doesn't absorb the above into its doc comment
## Check if the list is empty.
## ```
## List.isEmpty [1, 2, 3]

View file

@ -7,6 +7,8 @@ interface Set
walkUntil,
insert,
len,
isEmpty,
capacity,
remove,
contains,
toList,
@ -14,6 +16,8 @@ interface Set
union,
intersection,
difference,
map,
joinMap,
]
imports [
List,
@ -59,6 +63,13 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner
empty : {} -> Set k | k has Hash & Eq
empty = \{} -> @Set (Dict.empty {})
## Return a dictionary with space allocated for a number of entries. This
## may provide a performance optimization if you know how many entries will be
## inserted.
withCapacity : Nat -> Set k | k has Hash & Eq
withCapacity = \cap ->
@Set (Dict.withCapacity cap)
## Creates a new `Set` with a single value.
## ```
## singleItemSet = Set.single "Apple"
@ -115,10 +126,32 @@ expect
##
## expect countValues == 3
## ```
len : Set k -> Nat | k has Hash & Eq
len : Set * -> Nat
len = \@Set dict ->
Dict.len dict
## Returns the max number of elements the set can hold before requiring a rehash.
## ```
## foodSet =
## Set.empty {}
## |> Set.insert "apple"
##
## capacityOfSet = Set.capacity foodSet
## ```
capacity : Set * -> Nat
capacity = \@Set dict ->
Dict.capacity dict
## Check if the set is empty.
## ```
## Set.isEmpty (Set.empty {} |> Set.insert 42)
##
## Set.isEmpty (Set.empty {})
## ```
isEmpty : Set * -> Bool
isEmpty = \@Set dict ->
Dict.isEmpty dict
# Inserting a duplicate key has no effect on length.
expect
actual =
@ -261,6 +294,28 @@ walk : Set k, state, (state, k -> state) -> state | k has Hash & Eq
walk = \@Set dict, state, step ->
Dict.walk dict state (\s, k, _ -> step s k)
## Convert each value in the set to something new, by calling a conversion
## function on each of them which receives the old value. Then return a
## new set containing the converted values.
map : Set a, (a -> b) -> Set b | a has Hash & Eq, b has Hash & Eq
map = \set, transform ->
init = withCapacity (capacity set)
walk set init \answer, k ->
insert answer (transform k)
## Like [Set.map], except the transformation function wraps the return value
## in a set. At the end, all the sets get joined together
## (using [Set.union]) into one set.
##
## You may know a similar function named `concatMap` in other languages.
joinMap : Set a, (a -> Set b) -> Set b | a has Hash & Eq, b has Hash & Eq
joinMap = \set, transform ->
init = withCapacity (capacity set) # Might be a pessimization
walk set init \answer, k ->
union answer (transform k)
## Iterate through the values of a given `Set` and build a value, can stop
## iterating part way through the collection.
## ```