Skip to content

Commit

Permalink
Hello, Joe!
Browse files Browse the repository at this point in the history
  • Loading branch information
lpil committed Nov 20, 2024
0 parents commit 8e22025
Show file tree
Hide file tree
Showing 8 changed files with 724 additions and 0 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: test

on:
push:
branches:
- master
- main
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "27.1.2"
gleam-version: "1.6.1"
rebar3-version: "3"
# elixir-version: "1.15.4"
- run: gleam deps download
- run: gleam test --target erlang
- run: gleam test --target javascript
- run: gleam format --check src test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## v1.0.0 - 2024-11-20

- Initial release! Extracted from `gleam_stdlib` v0.43.0
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# deque

[![Package Version](https://img.shields.io/hexpm/v/deque)](https://hex.pm/packages/deque)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/deque/)

```sh
gleam add deque@1
```
```gleam
import deque
pub fn main() {
let letters =
deque.new()
|> deque.push_front("u")
|> deque.push_back("c")
|> deque.push_front("L")
|> deque.push_back("y")
deque.to_list(letters)
// -> ["L", "u", "c", "y"]
}
```

Further documentation can be found at <https://hexdocs.pm/deque>.
16 changes: 16 additions & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name = "deque"
version = "1.0.0"
gleam = ">= 1.0.0"
licences = ["Apache-2.0"]
description = "A double-ended queue data structure"

repository = { type = "github", user = "gleam-lang", repo = "deque" }
links = [
{ title = "Sponsor", href = "https://github.com/sponsors/lpil" },
]

[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
11 changes: 11 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
@@ -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.43.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "69EF22E78FDCA9097CBE7DF91C05B2A8B5436826D9F66680D879182C0860A747" },
{ 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" }
291 changes: 291 additions & 0 deletions src/deque.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
import gleam/list

/// A deque (double-ended-queue) is an ordered collection of elements. It is
/// similar to a list.
/// Unlike a list elements can be added to or removed from either the front or
/// the back in a performant fashion.
///
/// The internal representation may be different for two deques with the same
/// elements in the same order if the deques were constructed in different
/// ways. This is the price paid for a deque's fast access at both the front
/// and the back.
///
/// Because of unpredictable internal representation the equality operator `==`
/// may return surprising results, and the `is_equal` and `is_logically_equal`
/// functions are the recommended way to test deques for equality.
///
pub opaque type Deque(a) {
Deque(in: List(a), out: List(a))
}

/// Creates a fresh deque that contains no values.
///
pub fn new() -> Deque(a) {
Deque(in: [], out: [])
}

/// Converts a list of elements into a deque of the same elements in the same
/// order. The first element in the list becomes the front element in the deque.
///
/// This function runs in constant time.
///
/// # Examples
///
/// ```gleam
/// [1, 2, 3] |> from_list |> length
/// // -> 3
/// ```
///
pub fn from_list(list: List(a)) -> Deque(a) {
Deque(in: [], out: list)
}

/// Converts a deque of elements into a list of the same elements in the same
/// order. The front element in the deque becomes the first element in the list.
///
/// This function runs in linear time.
///
/// # Examples
///
/// ```gleam
/// new() |> push_back(1) |> push_back(2) |> to_list
/// // -> [1, 2]
/// ```
///
pub fn to_list(deque: Deque(a)) -> List(a) {
deque.out
|> list.append(list.reverse(deque.in))
}

/// Determines whether or not the deque is empty.
///
/// This function runs in constant time.
///
/// ## Examples
///
/// ```gleam
/// [] |> from_list |> is_empty
/// // -> True
/// ```
///
/// ```gleam
/// [1] |> from_list |> is_empty
/// // -> False
/// ```
///
/// ```gleam
/// [1, 2] |> from_list |> is_empty
/// // -> False
/// ```
///
pub fn is_empty(deque: Deque(a)) -> Bool {
deque.in == [] && deque.out == []
}

/// Counts the number of elements in a given deque.
///
/// This function has to traverse the deque to determine the number of elements,
/// so it runs in linear time.
///
/// ## Examples
///
/// ```gleam
/// length(from_list([]))
/// // -> 0
/// ```
///
/// ```gleam
/// length(from_list([1]))
/// // -> 1
/// ```
///
/// ```gleam
/// length(from_list([1, 2]))
/// // -> 2
/// ```
///
pub fn length(deque: Deque(a)) -> Int {
list.length(deque.in) + list.length(deque.out)
}

/// Pushes an element onto the back of the deque.
///
/// # Examples
///
/// ```gleam
/// [1, 2] |> from_list |> push_back(3) |> to_list
/// // -> [1, 2, 3]
/// ```
///
pub fn push_back(onto deque: Deque(a), this item: a) -> Deque(a) {
Deque(in: [item, ..deque.in], out: deque.out)
}

/// Pushes an element onto the front of the deque.
///
/// # Examples
///
/// ```gleam
/// [0, 0] |> from_list |> push_front(1) |> to_list
/// // -> [1, 0, 0]
/// ```
///
pub fn push_front(onto deque: Deque(a), this item: a) -> Deque(a) {
Deque(in: deque.in, out: [item, ..deque.out])
}

/// Gets the last element from the deque, returning the
/// element and a new deque without that element.
///
/// This function typically runs in constant time, but will occasionally run in
/// linear time.
///
/// # Examples
///
/// ```gleam
/// new()
/// |> push_back(0)
/// |> push_back(1)
/// |> pop_back
/// // -> Ok(#(1, push_front(new(), 0)))
/// ```
///
/// ```gleam
/// new()
/// |> push_front(0)
/// |> pop_back
/// // -> Ok(#(0, new()))
/// ```
///
/// ```gleam
/// new() |> pop_back
/// // -> Error(Nil)
/// ```
///
pub fn pop_back(from deque: Deque(a)) -> Result(#(a, Deque(a)), Nil) {
case deque {
Deque(in: [], out: []) -> Error(Nil)
Deque(in: [], out: out) -> pop_back(Deque(in: list.reverse(out), out: []))
Deque(in: [first, ..rest], out: out) -> {
let deque = Deque(in: rest, out: out)
Ok(#(first, deque))
}
}
}

/// Gets the first element from the deque, returning the
/// element and a new deque without that element.
///
/// This function typically runs in constant time, but will occasionally run in
/// linear time.
///
/// # Examples
///
/// ```gleam
/// new()
/// |> push_front(1)
/// |> push_front(0)
/// |> pop_front
/// // -> Ok(#(0, push_back(new(), 1)))
/// ```
///
/// ```gleam
/// new()
/// |> push_back(0)
/// |> pop_front
/// // -> Ok(#(0, new()))
/// ```
///
/// ```gleam
/// new() |> pop_back
/// // -> Error(Nil)
/// ```
///
pub fn pop_front(from deque: Deque(a)) -> Result(#(a, Deque(a)), Nil) {
case deque {
Deque(in: [], out: []) -> Error(Nil)
Deque(in: in, out: []) -> pop_front(Deque(in: [], out: list.reverse(in)))
Deque(in: in, out: [first, ..rest]) -> {
let deque = Deque(in: in, out: rest)
Ok(#(first, deque))
}
}
}

/// Creates a new deque from a given deque containing the same elements, but in
/// the opposite order.
///
/// This function runs in constant time.
///
/// ## Examples
///
/// ```gleam
/// [] |> from_list |> reverse |> to_list
/// // -> []
/// ```
///
/// ```gleam
/// [1] |> from_list |> reverse |> to_list
/// // -> [1]
/// ```
///
/// ```gleam
/// [1, 2] |> from_list |> reverse |> to_list
/// // -> [2, 1]
/// ```
///
pub fn reverse(deque: Deque(a)) -> Deque(a) {
Deque(in: deque.out, out: deque.in)
}

/// Checks whether two deques have equal elements in the same order, where the
/// equality of elements is determined by a given equality checking function.
///
/// This function is useful as the internal representation may be different for
/// two deques with the same elements in the same order depending on how they
/// were constructed, so the equality operator `==` may return surprising
/// results.
///
/// This function runs in linear time multiplied by the time taken by the
/// element equality checking function.
///
pub fn is_logically_equal(
a: Deque(a),
to b: Deque(a),
checking element_is_equal: fn(a, a) -> Bool,
) -> Bool {
check_equal(a.out, a.in, b.out, b.in, element_is_equal)
}

fn check_equal(
xs: List(a),
x_tail: List(a),
ys: List(a),
y_tail: List(a),
eq: fn(a, a) -> Bool,
) -> Bool {
case xs, x_tail, ys, y_tail {
[], [], [], [] -> True
[x, ..xs], _, [y, ..ys], _ ->
case eq(x, y) {
False -> False
True -> check_equal(xs, x_tail, ys, y_tail, eq)
}
[], [_, ..], _, _ -> check_equal(list.reverse(x_tail), [], ys, y_tail, eq)
_, _, [], [_, ..] -> check_equal(xs, x_tail, list.reverse(y_tail), [], eq)
_, _, _, _ -> False
}
}

/// Checks whether two deques have the same elements in the same order.
///
/// This function is useful as the internal representation may be different for
/// two deques with the same elements in the same order depending on how they
/// were constructed, so the equality operator `==` may return surprising
/// results.
///
/// This function runs in linear time.
///
pub fn is_equal(a: Deque(a), to b: Deque(a)) -> Bool {
check_equal(a.out, a.in, b.out, b.in, fn(a, b) { a == b })
}
Loading

0 comments on commit 8e22025

Please sign in to comment.