graph
Here’s a handy index you can use to browse through the various graph functions.
operation kind | functions |
---|---|
creating graphs | new |
turning graphs into lists | nodes |
querying a graph | size , has_node , has_edge , get_context , match |
adding/removing elements from a graph | insert_node , insert_directed_edge , insert_undirected_edge , remove_node , remove_directed_edge , remove_undirected_edge |
transforming graphs | fold , reverse , map_contexts , map_values , map_labels , reverse_edges , to_directed |
Types
The context associated with a node in a graph: it contains the node itself and all the incoming and outgoing edges. Edges are stored in a dict going from neighbour’s id to the edge label.
pub type Context(value, label) {
Context(
incoming: Dict(Int, label),
node: Node(value),
outgoing: Dict(Int, label),
)
}
Constructors
-
Context( incoming: Dict(Int, label), node: Node(value), outgoing: Dict(Int, label), )
A directed or undirected graph. A graph is made up of nodes and edges
connecting them: each node holds a value
and each edge has a label
.
The graph also carries along its direction
(either Directed
or
Undirected
) in its type so that it’s impossible to mix up Directed
and
Undirected
graphs inadvertently.
pub opaque type Graph(direction, value, label)
A node making up a graph. Every node is identified by a number and can hold an arbitrary value.
pub type Node(value) {
Node(id: Int, value: value)
}
Constructors
-
Node(id: Int, value: value)
The direction of an undirected graph.
pub type Undirected
Functions
pub fn fold(
over graph: Graph(a, b, c),
from initial: d,
with fun: fn(d, Context(b, c)) -> d,
) -> d
Reduces the given graph into a single value by applying function to all its contexts, one after the other.
🚨 Graph’s contexts are not sorted in any way so your folding function should never rely on any accidental order the contexts might have.
Examples
// The size function could be implemented using a fold.
// The real implementation is more efficient because it doesn't have to
// traverse all contexts!
pub fn size(graph) {
fold(
over: graph,
from: 0,
with: fn(size, _context) { size + 1 },
)
}
pub fn get_context(
graph: Graph(a, b, c),
of node: Int,
) -> Result(Context(b, c), Nil)
Returns the context associated with the node with the given id, if present.
Otherwise returns Error(Nil)
.
Examples
new() |> get(1)
// -> Error(Nil)
new() |> insert_node(Node(1, "a node")) |> get_context(of: 1)
// -> Ok(Context(node: Node(1, "a node"), ..))
pub fn has_edge(
graph: Graph(a, b, c),
from source: Int,
to destination: Int,
) -> Bool
Returns True
if the graph has an edge connecting the two nodes with the
given ids.
Examples
let my_graph =
new()
|> insert_node(Node(1, "a node"))
|> insert_node(Node(2, "other node"))
|> insert_directed_edge("edge label", from: 1, to: 2)
my_graph |> has_edge(from: 1, to: 2)
// -> True
my_graph |> has_edge(from: 2, to: 1)
// -> False
pub fn has_node(graph: Graph(a, b, c), node_id: Int) -> Bool
Returns True
if the graph contains a node with the given id.
Examples
let my_graph = new() |> insert_node(Node(1, "a node"))
my_graph |> has_node(1)
// -> True
my_graph |> has_node(2)
// -> False
pub fn insert_directed_edge(
graph: Graph(Directed, a, b),
labelled label: b,
from source: Int,
to destination: Int,
) -> Graph(Directed, a, b)
Adds an edge connecting two nodes in a directed graph.
let my_graph =
new()
|> insert_node(Node(1, "a node"))
|> insert_node(Node(2, "other node"))
|> insert_directed_edge("edge label", from: 1, to: 2)
my_graph |> has_edge(from: 1, to: 2)
// -> True
my_graph |> has_edge(from: 2, to: 1)
// -> False
pub fn insert_node(
graph: Graph(a, b, c),
node: Node(b),
) -> Graph(a, b, c)
Adds a node to the given graph. If the graph already contains a node with the same id, that will be replaced by the new one. The newly added node won’t be connected to any existing node.
Examples
new() |> insert_node(Node(1, "a node")) |> nodes
// -> [Node(1, "a node")]
pub fn insert_undirected_edge(
graph: Graph(Undirected, a, b),
labelled label: b,
between one: Int,
and other: Int,
) -> Graph(Undirected, a, b)
Adds an edge connecting two nodes in an undirected graph.
Examples
let my_graph =
new()
|> insert_node(Node(1, "a node"))
|> insert_node(Node(2, "other node"))
|> insert_undirected_edge("edge label", between: 1, and: 2)
my_graph |> has_edge(from: 1, to: 2)
// -> True
my_graph |> has_edge(from: 2, to: 1)
// -> True
pub fn map_contexts(
in graph: Graph(a, b, c),
with fun: fn(Context(b, c)) -> Context(b, c),
) -> Graph(Directed, b, c)
Transform the contexts associated with each node.
This function can add and remove arbitrary edges from the graph by updating the
incoming
andoutgoing
edges of a context. So we can’t assume the final graph will still beUndirected
, that’s why it is always treated as aDirected
one.
Examples
// The reverse function can be implemented with `map_contexts`
pub fn reverse(graph) {
map_contexts(in: graph, with: fn(context) {
Context(
..context,
incoming: context.outgoing,
outgoing: context.incoming,
)
})
}
pub fn map_labels(
in graph: Graph(a, b, c),
with fun: fn(c) -> d,
) -> Graph(a, b, d)
Transforms the labels of all the graph’s edges using the given function.
Examples
new()
|> insert_node(Node(1, "a node"))
|> insert_undirected_edge(UndirectedEdge(1, 1, "label"))
|> map_labels(fn(label) { label <> "!" })
|> labels
// -> ["label!"]
pub fn map_values(
in graph: Graph(a, b, c),
with fun: fn(b) -> d,
) -> Graph(a, d, c)
Transforms the values of all the graph’s nodes using the given function.
Examples
new()
|> insert_node(Node(1, "a node"))
|> map_nodes(fn(value) { value <> "!" })
|> nodes
// -> [Node(1, "my node!")]
pub fn match(
graph: Graph(a, b, c),
node_id: Int,
) -> Result(#(Context(b, c), Graph(a, b, c)), Nil)
If the graph contains a node with the given id, returns a tuple containing the context of that node (with all edges looping back to itself removed) and the “remaining” graph: that is, the original graph where that node has been removed.
pub fn nodes(graph: Graph(a, b, c)) -> List(Node(b))
Returns a list of all the nodes contained in the graph.
Examples
new() |> nodes
// -> []
new() |> insert_node(Node(1, "a node")) |> nodes
// -> [Node(1, "a node")]
pub fn remove_directed_edge(
graph: Graph(Directed, a, b),
from source: Int,
to destination: Int,
) -> Graph(Directed, a, b)
Removes a directed edge connecting two nodes from a graph.
pub fn remove_node(
graph: Graph(a, b, c),
node_id: Int,
) -> Graph(a, b, c)
Removes a node with the given id from the graph. If there’s no node with the given id it does nothing.
pub fn remove_undirected_edge(
graph: Graph(Undirected, a, b),
between one: Int,
and other: Int,
) -> Graph(Undirected, a, b)
Removes an undirected edge connecting two nodes from a graph.
pub fn reverse_edges(
graph: Graph(Directed, a, b),
) -> Graph(Directed, a, b)
Flips the direction of every edge in the graph. All incoming edges will become outgoing and vice-versa.
pub fn size(graph: Graph(a, b, c)) -> Int
Returns the number of nodes of the graph.
Examples
new() |> size
// -> 0
new() |> insert_node(Node(1, "a node")) |> size
// -> 1
pub fn to_directed(
graph: Graph(Undirected, a, b),
) -> Graph(Directed, a, b)
Turns an undirected graph into a directed one. Every edge connecting two nodes in the original graph will be considered as a pair of edges connecting the nodes going in both directions.