Skip to content

Instantly share code, notes, and snippets.

@Wigny
Created November 10, 2023 18:47
Show Gist options
  • Save Wigny/0f0829481d62d3f7b4eb9874f0f6cf42 to your computer and use it in GitHub Desktop.
Save Wigny/0f0829481d62d3f7b4eb9874f0f6cf42 to your computer and use it in GitHub Desktop.
defmodule Decimal.Range do
defstruct [:first, :last, :step]
def new(first, last, step) do
%__MODULE__{first: first, last: last, step: step}
end
def size(range) do
range.last
|> Decimal.sub(range.first)
|> Decimal.div(range.step)
|> Decimal.add(1)
|> Decimal.round(0, :floor)
|> Decimal.to_integer()
end
defimpl Enumerable do
def reduce(_range, {:halt, acc}, _reducer) do
{:halted, acc}
end
def reduce(range, {:suspend, acc}, reducer) do
{:suspended, acc, fn next_acc -> reduce(range, next_acc, reducer) end}
end
def reduce(range, {:cont, acc}, reducer) do
inside_positive_range? =
Decimal.positive?(range.step) and not Decimal.gt?(range.first, range.last)
inside_negative_range? =
Decimal.negative?(range.step) and not Decimal.lt?(range.first, range.last)
if inside_positive_range? or inside_negative_range? do
next_range = %{range | first: Decimal.add(range.first, range.step)}
next_acc = reducer.(range.first, acc)
reduce(next_range, next_acc, reducer)
else
{:done, acc}
end
end
def slice(range) do
{:ok, @for.size(range), &Enum.to_list/1}
end
end
end
@Wigny
Copy link
Author

Wigny commented Nov 10, 2023

iex> range = Decimal.Range.new Decimal.from_float(0.1), Decimal.from_float(0.6), Decimal.from_float(0.2)
%Decimal.Range{first: Decimal.new("0.1"), last: Decimal.new("0.6"), step: Decimal.new("0.2")}

iex> Enum.to_list range
[Decimal.new("0.1"), Decimal.new("0.3"), Decimal.new("0.5")]

iex> Enum.random range
Decimal.new("0.5")

iex> Enum.reduce range, &Decimal.add/2
Decimal.new("0.9")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment