Skip to content

Instantly share code, notes, and snippets.

@Wigny
Last active October 31, 2024 20:07
Show Gist options
  • Save Wigny/4425e0a9e917e632a6d059ddfce64686 to your computer and use it in GitHub Desktop.
Save Wigny/4425e0a9e917e632a6d059ddfce64686 to your computer and use it in GitHub Desktop.
Elixir Queue data type
defmodule Queue do
defstruct value: :queue.new()
def new do
%Queue{}
end
def new(enumerable) do
list = Enum.to_list(enumerable)
%Queue{value: :queue.from_list(list)}
end
def to_list(%Queue{value: q}) do
:queue.to_list(q)
end
def member?(%Queue{value: q}, item) do
:queue.member(item, q)
end
def length(%Queue{value: q}) do
:queue.len(q)
end
def peek(%Queue{value: q}) do
:queue.peek(q)
end
def empty?(%Queue{value: q}) do
:queue.is_empty(q)
end
def push(%Queue{value: q} = queue, item) do
%{queue | value: :queue.in(item, q)}
end
def pop(%Queue{value: q} = queue) do
{result, q} = :queue.out(q)
{result, %{queue | value: q}}
end
def join(%Queue{value: q1} = queue1, %Queue{value: q2}) do
%{queue1 | value: :queue.join(q1, q2)}
end
defimpl Collectable do
def into(queue) do
collector = fn
acc, {:cont, elem} -> @for.push(acc, elem)
acc, :done -> acc
_acc, :halt -> :ok
end
{queue, collector}
end
end
defimpl Enumerable do
def member?(queue, element) do
{:ok, @for.member?(queue, element)}
end
def count(queue) do
{:ok, @for.length(queue)}
end
def reduce(_queue, {:halt, acc}, _fun) do
{:halted, acc}
end
def reduce(queue, {:suspend, acc}, fun) do
{:suspended, acc, &reduce(queue, &1, fun)}
end
def reduce(queue, {:cont, acc}, fun) do
case @for.pop(queue) do
{{:value, item}, next} -> reduce(next, fun.(item, acc), fun)
{:empty, _next} -> {:done, acc}
end
end
def slice(queue) do
{:ok, @for.length(queue), &@for.to_list/1}
end
end
defimpl Inspect do
import Inspect.Algebra
def inspect(queue, opts) do
concat(["Queue.new(", Inspect.List.inspect(@for.to_list(queue), opts), ")"])
end
end
end
ExUnit.start()
ExUnit.run()
defmodule QueueTest do
use ExUnit.Case, async: true
test "new/0" do
queue = Queue.new()
assert Enum.empty?(queue)
end
test "new/1" do
queue = Queue.new(1..3)
assert [1, 2, 3] = Enum.to_list(queue)
end
test "to_list/1" do
queue = Queue.new(1..3)
assert [1, 2, 3] = Queue.to_list(queue)
end
test "member?/2" do
queue = Queue.new(1..3)
assert Queue.member?(queue, 2)
refute Queue.member?(queue, 4)
end
test "empty?/1" do
refute Queue.empty?(Queue.new(1..3))
assert Queue.empty?(Queue.new())
end
test "length/1" do
queue = Queue.new(1..3)
assert 3 == Queue.length(queue)
end
test "push/2" do
queue = Queue.push(Queue.new([:a, :b, :c]), :d)
assert Enum.member?(queue, :d)
assert 3 == Enum.find_index(queue, &(&1 == :d))
end
test "pop/1" do
assert {{:value, 1}, Queue.new(2..3)} == Queue.pop(Queue.new(1..3))
assert {:empty, Queue.new()} == Queue.pop(Queue.new())
end
test "join/2" do
queue = Queue.join(Queue.new(1..3), Queue.new(4..6))
assert [1, 2, 3, 4, 5, 6] = Enum.to_list(queue)
end
test "peek/1" do
assert {:value, 1} == Queue.peek(Queue.new(1..3))
assert :empty == Queue.peek(Queue.new())
end
test "inspect" do
assert "Queue.new([1, 2, 3, 4])" = inspect(Queue.new(1..4))
end
test "take" do
assert [1, 2, 3] = Enum.take(Queue.new(1..100), 3)
end
test "into" do
queue = Queue.new([1, 2])
queue = Enum.into(3..5, queue)
assert [1, 2, 3, 4, 5] == Enum.to_list(queue)
end
test "split" do
queue = Queue.new(1..15)
{queue1, queue2} = Enum.split(queue, 5)
assert [1, 2, 3, 4, 5] == Enum.to_list(queue1)
assert 10 == Enum.count(queue2)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment