Initial Commit
Some checks failed
test / test (push) Has been cancelled

This commit is contained in:
2025-10-17 21:59:17 +01:00
parent e1faecce11
commit 5125f08562
7 changed files with 364 additions and 2 deletions

270
src/dllist.gleam Normal file
View File

@@ -0,0 +1,270 @@
import gleam/bool
import gleam/dict
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/result
/// https://hackage.haskell.org/package/liboleg-2009.9.1/docs/src/Data-FDList.html#empty
pub opaque type Node(a) {
Node(val: a, left: Int, right: Int)
}
pub type DLList(a) {
DLList(counter: Int, current: Int, mem: dict.Dict(Int, Node(a)))
// CyclicList(counter: Int, current: Int, mem: dict.Dict(Int, Node(a)))
}
pub fn main() {
let l = new()
let assert Ok(l2) = insert_right(l, 123) |> echo
let assert Ok(l3) = insert_right(l2, 456) |> echo
let assert Ok(l4) = insert_right(l3, 789)
let assert Ok(l5) = insert_right(l4, 0)
get(l5) |> echo
l5 |> echo
l5 |> to_list |> echo
let l6 = delete(l5)
l6 |> echo
let l7 = delete(l6)
l7 |> echo
let l8 = delete(l7)
l8 |> echo
let l9 = delete(l8)
l9 |> echo
from_list([1, 2, 3, 4]) |> echo
let assert Ok(l1) = new() |> insert_right(1)
l1 |> echo
let l1t = take(l1, 5)
l1t |> echo
let assert Ok(l2) = insert_right(l1, 2)
l2 |> echo
let l2t = take(l2, 5)
l2t |> echo
}
pub fn new() -> DLList(a) {
DLList(1, 0, dict.new())
}
fn get_curr_node(list: DLList(a)) -> Result(Node(a), Nil) {
dict.get(list.mem, list.current)
}
pub fn get(list: DLList(a)) -> Result(a, Nil) {
get_curr_node(list) |> result.map(fn(n) { n.val })
}
pub fn is_empty(list: DLList(a)) -> Bool {
dict.is_empty(list.mem)
}
pub fn insert_right(list: DLList(a), val: a) -> Result(DLList(a), Nil) {
let ref = list.counter
case is_empty(list) {
True -> {
let node = Node(val, ref, ref)
Ok(DLList(ref + 1, ref, dict.insert(list.mem, ref, node)))
}
False -> {
use curr_node <- result.try(dict.get(list.mem, list.current))
let curr_node_2 = Node(..curr_node, right: ref)
let next = curr_node.right
let next_node = case next == list.current {
True -> Ok(curr_node_2)
False -> dict.get(list.mem, next)
}
let new_mem = case next == list.current {
True -> list.mem
False -> dict.insert(list.mem, list.current, curr_node_2)
}
let new_mem = case next_node {
Ok(next_node) -> {
let next_node_2 = Node(..next_node, left: ref)
dict.insert(new_mem, next, next_node_2)
}
Error(_) -> new_mem
}
let new_node = Node(val, list.current, next)
let new_mem = dict.insert(new_mem, ref, new_node)
Ok(DLList(list.counter + 1, list.counter, new_mem))
}
}
}
pub fn delete(list: DLList(a)) -> DLList(a) {
let #(mem, curr_node) = remove_lookup(list, list.current)
case curr_node {
None -> list
Some(curr_node) -> {
let l = curr_node.left
let r = curr_node.right
case l, r {
0, 0 -> new()
0, n if n == list.current -> new()
n, 0 if n == list.current -> new()
n, m if n == list.current && m == list.current -> new()
_, 0 -> {
DLList(
..list,
current: l,
mem: update_dict_entry(mem, l, fn(n) { Node(..n, right: r) }),
)
}
_, r if r == list.current -> {
DLList(
..list,
current: l,
mem: update_dict_entry(mem, l, fn(n) { Node(..n, right: l) }),
)
}
0, _ -> {
DLList(
..list,
current: r,
mem: update_dict_entry(mem, r, fn(n) { Node(..n, left: l) }),
)
}
l, _ if l == list.current -> {
DLList(
..list,
current: r,
mem: update_dict_entry(mem, r, fn(n) { Node(..n, left: r) }),
)
}
l, r if l == r -> {
DLList(
..list,
current: r,
mem: update_dict_entry(mem, r, fn(n) {
Node(..n, left: r, right: r)
}),
)
}
_, _ -> {
DLList(
..list,
current: r,
mem: update_dict_entry(mem, r, fn(n) { Node(..n, left: l) })
|> update_dict_entry(l, fn(n) { Node(..n, right: r) }),
)
}
}
}
}
}
pub fn move_right(list: DLList(a)) -> DLList(a) {
do_move_right(list) |> result.unwrap(list)
}
fn do_move_right(list: DLList(a)) -> Result(DLList(a), Nil) {
case get_curr_node(list) {
Error(_) -> Error(Nil)
Ok(node) -> {
case node.right {
0 -> Ok(list)
r -> Ok(DLList(..list, current: r))
}
}
}
}
pub fn move_left(list: DLList(a)) -> DLList(a) {
do_move_left(list)
|> result.unwrap(list)
}
fn do_move_left(list: DLList(a)) -> Result(DLList(a), Nil) {
case get_curr_node(list) {
Error(_) -> Error(Nil)
Ok(node) -> {
case node.left {
0 -> Ok(list)
l -> Ok(DLList(..list, current: l))
}
}
}
}
pub fn from_list(list: List(a)) -> Result(DLList(a), Nil) {
list.try_fold(list, new(), insert_right)
}
pub fn take(list: DLList(a), n_times: Int) -> List(a) {
take_loop(list, n_times, [])
}
fn take_loop(list: DLList(a), n_times: Int, acc: List(a)) -> List(a) {
use <- bool.guard(when: is_empty(list), return: [])
case n_times {
0 -> list.reverse(acc)
n -> {
let assert Ok(item) = get(list)
take_loop(move_right(list), n - 1, [item, ..acc])
}
}
}
pub fn to_list(list: DLList(a)) -> List(a) {
case is_empty(list) {
True -> []
False -> {
let assert Ok(a) = get(list)
[a, ..to_list_loop(list.current, do_move_right(list))]
}
}
}
fn to_list_loop(ref: Int, list: Result(DLList(a), Nil)) -> List(a) {
case list {
Error(_) -> []
Ok(DLList(_, current, _)) if current == ref -> []
Ok(list) -> {
let assert Ok(a) = get(list)
[a, ..to_list_loop(ref, do_move_right(list))]
}
}
}
pub fn update(list: DLList(a), value: a) -> Result(DLList(a), Nil) {
use <- bool.guard(when: is_empty(list), return: Error(Nil))
let assert Ok(curr_node) = get_curr_node(list)
Ok(
DLList(
..list,
mem: dict.insert(list.mem, list.current, Node(..curr_node, val: value)),
),
)
}
fn update_dict_entry(
list: dict.Dict(Int, Node(a)),
ref: Int,
upd: fn(Node(a)) -> Node(a),
) -> dict.Dict(Int, Node(a)) {
case dict.get(list, ref) {
Ok(n) -> {
dict.insert(list, ref, upd(n))
}
Error(_) -> list
}
}
fn remove_lookup(
list: DLList(a),
id: Int,
) -> #(dict.Dict(Int, Node(a)), Option(Node(a))) {
case dict.get(list.mem, id) {
Ok(Node(_, _, _) as n) -> {
#(dict.delete(list.mem, id), Some(n))
}
Error(_) -> {
#(list.mem, None)
}
}
}