Initial commit
This commit is contained in:
commit
a6272848f9
379 changed files with 74829 additions and 0 deletions
407
build/packages/gleam_stdlib/src/gleam/set.gleam
Normal file
407
build/packages/gleam_stdlib/src/gleam/set.gleam
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
import gleam/dict.{type Dict}
|
||||
import gleam/list
|
||||
import gleam/result
|
||||
|
||||
// A list is used as the dict value as an empty list has the smallest
|
||||
// representation in Erlang's binary format
|
||||
@target(erlang)
|
||||
type Token =
|
||||
List(Nil)
|
||||
|
||||
@target(erlang)
|
||||
const token = []
|
||||
|
||||
@target(javascript)
|
||||
type Token =
|
||||
Nil
|
||||
|
||||
@target(javascript)
|
||||
const token = Nil
|
||||
|
||||
/// A set is a collection of unique members of the same type.
|
||||
///
|
||||
/// It is implemented using the `gleam/dict` module, so inserts and lookups have
|
||||
/// logarithmic time complexity.
|
||||
///
|
||||
pub opaque type Set(member) {
|
||||
Set(dict: Dict(member, Token))
|
||||
}
|
||||
|
||||
/// Creates a new empty set.
|
||||
///
|
||||
pub fn new() -> Set(member) {
|
||||
Set(dict.new())
|
||||
}
|
||||
|
||||
/// Gets the number of members in a set.
|
||||
///
|
||||
/// This function runs in constant time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// new()
|
||||
/// |> insert(1)
|
||||
/// |> insert(2)
|
||||
/// |> size
|
||||
/// // -> 2
|
||||
/// ```
|
||||
///
|
||||
pub fn size(set: Set(member)) -> Int {
|
||||
dict.size(set.dict)
|
||||
}
|
||||
|
||||
/// Determines whether or not the set is empty.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// new() |> is_empty
|
||||
/// // -> True
|
||||
/// ```
|
||||
///
|
||||
/// ```gleam
|
||||
/// new() |> insert(1) |> is_empty
|
||||
/// // -> False
|
||||
/// ```
|
||||
///
|
||||
pub fn is_empty(set: Set(member)) -> Bool {
|
||||
set == new()
|
||||
}
|
||||
|
||||
/// Inserts an member into the set.
|
||||
///
|
||||
/// This function runs in logarithmic time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// new()
|
||||
/// |> insert(1)
|
||||
/// |> insert(2)
|
||||
/// |> size
|
||||
/// // -> 2
|
||||
/// ```
|
||||
///
|
||||
pub fn insert(into set: Set(member), this member: member) -> Set(member) {
|
||||
Set(dict: dict.insert(set.dict, member, token))
|
||||
}
|
||||
|
||||
/// Checks whether a set contains a given member.
|
||||
///
|
||||
/// This function runs in logarithmic time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// new()
|
||||
/// |> insert(2)
|
||||
/// |> contains(2)
|
||||
/// // -> True
|
||||
/// ```
|
||||
///
|
||||
/// ```gleam
|
||||
/// new()
|
||||
/// |> insert(2)
|
||||
/// |> contains(1)
|
||||
/// // -> False
|
||||
/// ```
|
||||
///
|
||||
pub fn contains(in set: Set(member), this member: member) -> Bool {
|
||||
set.dict
|
||||
|> dict.get(member)
|
||||
|> result.is_ok
|
||||
}
|
||||
|
||||
/// Removes a member from a set. If the set does not contain the member then
|
||||
/// the set is returned unchanged.
|
||||
///
|
||||
/// This function runs in logarithmic time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// new()
|
||||
/// |> insert(2)
|
||||
/// |> delete(2)
|
||||
/// |> contains(1)
|
||||
/// // -> False
|
||||
/// ```
|
||||
///
|
||||
pub fn delete(from set: Set(member), this member: member) -> Set(member) {
|
||||
Set(dict: dict.delete(set.dict, member))
|
||||
}
|
||||
|
||||
/// Converts the set into a list of the contained members.
|
||||
///
|
||||
/// The list has no specific ordering, any unintentional ordering may change in
|
||||
/// future versions of Gleam or Erlang.
|
||||
///
|
||||
/// This function runs in linear time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// new() |> insert(2) |> to_list
|
||||
/// // -> [2]
|
||||
/// ```
|
||||
///
|
||||
pub fn to_list(set: Set(member)) -> List(member) {
|
||||
dict.keys(set.dict)
|
||||
}
|
||||
|
||||
/// Creates a new set of the members in a given list.
|
||||
///
|
||||
/// This function runs in loglinear time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// import gleam/int
|
||||
/// import gleam/list
|
||||
///
|
||||
/// [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort(by: int.compare)
|
||||
/// // -> [1, 2, 3, 4]
|
||||
/// ```
|
||||
///
|
||||
pub fn from_list(members: List(member)) -> Set(member) {
|
||||
let dict =
|
||||
list.fold(over: members, from: dict.new(), with: fn(m, k) {
|
||||
dict.insert(m, k, token)
|
||||
})
|
||||
Set(dict)
|
||||
}
|
||||
|
||||
/// Combines all entries into a single value by calling a given function on each
|
||||
/// one.
|
||||
///
|
||||
/// Sets are not ordered so the values are not returned in any specific order.
|
||||
/// Do not write code that relies on the order entries are used by this
|
||||
/// function as it may change in later versions of Gleam or Erlang.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// from_list([1, 3, 9])
|
||||
/// |> fold(0, fn(accumulator, member) { accumulator + member })
|
||||
/// // -> 13
|
||||
/// ```
|
||||
///
|
||||
pub fn fold(
|
||||
over set: Set(member),
|
||||
from initial: acc,
|
||||
with reducer: fn(acc, member) -> acc,
|
||||
) -> acc {
|
||||
dict.fold(over: set.dict, from: initial, with: fn(a, k, _) { reducer(a, k) })
|
||||
}
|
||||
|
||||
/// Creates a new set from an existing set, minus any members that a given
|
||||
/// function returns `False` for.
|
||||
///
|
||||
/// This function runs in loglinear time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// import gleam/int
|
||||
///
|
||||
/// from_list([1, 4, 6, 3, 675, 44, 67])
|
||||
/// |> filter(keeping: int.is_even)
|
||||
/// |> to_list
|
||||
/// // -> [4, 6, 44]
|
||||
/// ```
|
||||
///
|
||||
pub fn filter(
|
||||
in set: Set(member),
|
||||
keeping predicate: fn(member) -> Bool,
|
||||
) -> Set(member) {
|
||||
Set(dict.filter(in: set.dict, keeping: fn(m, _) { predicate(m) }))
|
||||
}
|
||||
|
||||
/// Creates a new set from a given set with the result of applying the given
|
||||
/// function to each member.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// from_list([1, 2, 3, 4])
|
||||
/// |> map(with: fn(x) { x * 2 })
|
||||
/// |> to_list
|
||||
/// // -> [2, 4, 6, 8]
|
||||
/// ```
|
||||
pub fn map(set: Set(member), with fun: fn(member) -> mapped) -> Set(mapped) {
|
||||
fold(over: set, from: new(), with: fn(acc, member) {
|
||||
insert(acc, fun(member))
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new set from a given set with all the same entries except any
|
||||
/// entry found on the given list.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// from_list([1, 2, 3, 4])
|
||||
/// |> drop([1, 3])
|
||||
/// |> to_list
|
||||
/// // -> [2, 4]
|
||||
/// ```
|
||||
pub fn drop(from set: Set(member), drop disallowed: List(member)) -> Set(member) {
|
||||
list.fold(over: disallowed, from: set, with: delete)
|
||||
}
|
||||
|
||||
/// Creates a new set from a given set, only including any members which are in
|
||||
/// a given list.
|
||||
///
|
||||
/// This function runs in loglinear time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// from_list([1, 2, 3])
|
||||
/// |> take([1, 3, 5])
|
||||
/// |> to_list
|
||||
/// // -> [1, 3]
|
||||
/// ```
|
||||
///
|
||||
pub fn take(from set: Set(member), keeping desired: List(member)) -> Set(member) {
|
||||
Set(dict.take(from: set.dict, keeping: desired))
|
||||
}
|
||||
|
||||
/// Creates a new set that contains all members of both given sets.
|
||||
///
|
||||
/// This function runs in loglinear time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// union(from_list([1, 2]), from_list([2, 3])) |> to_list
|
||||
/// // -> [1, 2, 3]
|
||||
/// ```
|
||||
///
|
||||
pub fn union(of first: Set(member), and second: Set(member)) -> Set(member) {
|
||||
let #(larger, smaller) = order(first, second)
|
||||
fold(over: smaller, from: larger, with: insert)
|
||||
}
|
||||
|
||||
fn order(first: Set(member), second: Set(member)) -> #(Set(member), Set(member)) {
|
||||
case dict.size(first.dict) > dict.size(second.dict) {
|
||||
True -> #(first, second)
|
||||
False -> #(second, first)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new set that contains members that are present in both given sets.
|
||||
///
|
||||
/// This function runs in loglinear time.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// intersection(from_list([1, 2]), from_list([2, 3])) |> to_list
|
||||
/// // -> [2]
|
||||
/// ```
|
||||
///
|
||||
pub fn intersection(
|
||||
of first: Set(member),
|
||||
and second: Set(member),
|
||||
) -> Set(member) {
|
||||
let #(larger, smaller) = order(first, second)
|
||||
take(from: larger, keeping: to_list(smaller))
|
||||
}
|
||||
|
||||
/// Creates a new set that contains members that are present in the first set
|
||||
/// but not the second.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list
|
||||
/// // -> [1]
|
||||
/// ```
|
||||
///
|
||||
pub fn difference(
|
||||
from first: Set(member),
|
||||
minus second: Set(member),
|
||||
) -> Set(member) {
|
||||
drop(from: first, drop: to_list(second))
|
||||
}
|
||||
|
||||
/// Determines if a set is fully contained by another.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// is_subset(from_list([1]), from_list([1, 2]))
|
||||
/// // -> True
|
||||
/// ```
|
||||
///
|
||||
/// ```gleam
|
||||
/// is_subset(from_list([1, 2, 3]), from_list([3, 4, 5]))
|
||||
/// // -> False
|
||||
/// ```
|
||||
///
|
||||
pub fn is_subset(first: Set(member), of second: Set(member)) -> Bool {
|
||||
intersection(of: first, and: second) == first
|
||||
}
|
||||
|
||||
/// Determines if two sets contain no common members
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```gleam
|
||||
/// is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6]))
|
||||
/// // -> True
|
||||
/// ```
|
||||
///
|
||||
/// ```gleam
|
||||
/// is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5]))
|
||||
/// // -> False
|
||||
/// ```
|
||||
///
|
||||
pub fn is_disjoint(first: Set(member), from second: Set(member)) -> Bool {
|
||||
intersection(of: first, and: second) == new()
|
||||
}
|
||||
|
||||
/// Creates a new set that contains members that are present in either set, but
|
||||
/// not both.
|
||||
///
|
||||
/// ```gleam
|
||||
/// symmetric_difference(from_list([1, 2, 3]), from_list([3, 4])) |> to_list
|
||||
/// // -> [1, 2, 4]
|
||||
/// ```
|
||||
///
|
||||
pub fn symmetric_difference(
|
||||
of first: Set(member),
|
||||
and second: Set(member),
|
||||
) -> Set(member) {
|
||||
difference(
|
||||
from: union(of: first, and: second),
|
||||
minus: intersection(of: first, and: second),
|
||||
)
|
||||
}
|
||||
|
||||
/// Calls a function for each member in a set, discarding the return
|
||||
/// value.
|
||||
///
|
||||
/// Useful for producing a side effect for every item of a set.
|
||||
///
|
||||
/// ```gleam
|
||||
/// let set = from_list(["apple", "banana", "cherry"])
|
||||
///
|
||||
/// each(set, io.println)
|
||||
/// // -> Nil
|
||||
/// // apple
|
||||
/// // banana
|
||||
/// // cherry
|
||||
/// ```
|
||||
///
|
||||
/// The order of elements in the iteration is an implementation detail that
|
||||
/// should not be relied upon.
|
||||
///
|
||||
pub fn each(set: Set(member), fun: fn(member) -> a) -> Nil {
|
||||
fold(set, Nil, fn(nil, member) {
|
||||
fun(member)
|
||||
nil
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue