feat: implement stack

This commit is contained in:
Alex 2024-09-19 09:09:22 +02:00
parent e3ada19976
commit 7885da4e29
Signed by: l-x
SSH key fingerprint: SHA256:MK3uQVPHEV0Oo2ry/dAqvVK3pAwegKAwSlyfgLd/yQM
6 changed files with 168 additions and 12 deletions

11
manifest.toml Normal file
View file

@ -0,0 +1,11 @@
# This file was generated by Gleam
# You typically do not need to edit this file
packages = [
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
]
[requirements]
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }

View file

@ -1,5 +0,0 @@
import gleam/io
pub fn main() {
io.println("Hello from fortheck!")
}

4
src/fortheck/error.gleam Normal file
View file

@ -0,0 +1,4 @@
pub type Error {
StackOverflow
StackUnderflow
}

61
src/fortheck/stack.gleam Normal file
View file

@ -0,0 +1,61 @@
import fortheck/error.{type Error, StackOverflow, StackUnderflow}
import gleam/bool
import gleam/list
import gleam/result
pub opaque type Stack(a) {
Stack(capacity: Int, length: Int, data: List(a))
}
pub fn new() -> Stack(a) {
Stack(131_072, 0, [])
}
pub fn from_list(list: List(a)) -> Result(Stack(a), Error) {
list.try_fold(over: list, from: new(), with: push)
}
pub fn to_list(stack: Stack(a)) -> List(a) {
list.reverse(stack.data)
}
pub fn push(onto stack: Stack(a), this item: a) -> Result(Stack(a), Error) {
use <- bool.guard(
when: stack.length >= stack.capacity,
return: Error(StackOverflow),
)
Ok(Stack(..stack, length: stack.length + 1, data: [item, ..stack.data]))
}
pub fn pop(from stack: Stack(a)) -> Result(#(a, Stack(a)), Error) {
case stack.data {
[] -> Error(StackUnderflow)
[x, ..xs] -> Ok(#(x, Stack(..stack, length: stack.length - 1, data: xs)))
}
}
pub fn try_pop(
from stack: Stack(a),
apply fun: fn(a, Stack(a)) -> Result(b, Error),
) -> Result(b, Error) {
use #(item, stack) <- result.try(pop(from: stack))
fun(item, stack)
}
pub fn pop_2(from stack: Stack(a)) -> Result(#(#(a, a), Stack(a)), Error) {
use b, stack <- try_pop(from: stack)
use a, stack <- try_pop(from: stack)
Ok(#(#(a, b), stack))
}
pub fn try_pop_2(
from stack: Stack(a),
apply fun: fn(a, a, Stack(a)) -> Result(b, Error),
) -> Result(b, Error) {
use #(#(a, b), stack) <- result.try(pop_2(from: stack))
fun(a, b, stack)
}

View file

@ -1,12 +1,5 @@
import gleeunit import gleeunit
import gleeunit/should
pub fn main() { pub fn main() {
gleeunit.main() gleeunit.main()
} }
// gleeunit test functions end in `_test`
pub fn hello_world_test() {
1
|> should.equal(1)
}

92
test/stack_test.gleam Normal file
View file

@ -0,0 +1,92 @@
import fortheck/error
import fortheck/stack
import gleam/iterator
import gleam/pair
import gleam/result
import gleeunit
import gleeunit/should
pub fn main() {
gleeunit.main()
}
pub fn new_test() {
stack.new()
|> stack.to_list
|> should.equal([])
}
pub fn push_test() {
stack.new()
|> stack.push(123)
|> should.be_ok
|> stack.push(456)
|> should.be_ok
|> stack.to_list
|> should.equal([123, 456])
}
pub fn push_stack_overflow_test() {
iterator.range(1, 131_072)
|> iterator.try_fold(stack.new(), stack.push)
|> should.be_ok
|> stack.push(1)
|> should.be_error
|> should.equal(error.StackOverflow)
}
pub fn from_list_test() {
[123, 456]
|> stack.from_list
|> should.be_ok
|> stack.to_list
|> should.equal([123, 456])
}
pub fn pop_stack_underflow_test() {
stack.new()
|> stack.pop
|> should.be_error
|> should.equal(error.StackUnderflow)
}
pub fn pop_test() {
let assert Ok(stack) = stack.from_list([1, 2, 3])
stack
|> stack.pop
|> should.be_ok
|> pair.first
|> should.equal(3)
{
use a, _ <- stack.try_pop(stack)
Ok(a)
}
|> should.be_ok
|> should.equal(3)
}
pub fn pop_2_test() {
let assert Ok(stack) = stack.from_list([1, 2, 3])
stack
|> stack.pop_2
|> should.be_ok
|> pair.first
|> should.equal(#(2, 3))
{
use a, b, _ <- stack.try_pop_2(stack)
Ok(#(a, b))
}
|> should.be_ok
|> should.equal(#(2, 3))
}
pub fn pop_2_stack_underflow_test() {
stack.from_list([1])
|> result.try(stack.pop_2)
|> should.be_error
|> should.equal(error.StackUnderflow)
}