-
-
Save nielsbom/8aaec8838d927cac9f72b0e7c28770b2 to your computer and use it in GitHub Desktop.
Practicing Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ExUnit.start() | |
defmodule Enum2 do | |
# all | |
def all?(list), do: all?(list, &(!!&1 === true)) | |
def all?([], _), do: true | |
# Short circuit with "and" | |
# Tail-call optimized π | |
def all?([head | tail], func), do: | |
func.(head) and all?(tail, func) | |
# each (no relevant return value) | |
def each([], _), do: nil | |
def each([head | tail], fun) do | |
fun.(head) | |
# Tail-call optimized π | |
each(tail, fun) | |
end | |
# filter | |
def filter([], _), do: [] | |
def filter([head | tail], func), do: | |
# Tail-call optimized π | |
# But: list concatenation with linked lists is relatively expensive. | |
if(func.(head), do: [head], else: []) ++ filter(tail, func) | |
# faster filter | |
def ffilter(lst, func), do: _ffilter(lst, func, []) | |
defp _ffilter([], _, result), do: Enum.reverse(result) | |
defp _ffilter([head | tail], func, result), do: | |
# every item that is π is added to it (prepending to LL is cheap) | |
# then when we reach the end, reverse and pass it back (reverse is cheap?) | |
# Tail-call optimized π | |
_ffilter( | |
tail, | |
func, | |
(if(func.(head), do: [head | result], else: result)) | |
) | |
# Splits the enumerable into two enumerables, leaving count elements in the | |
# first one. | |
# If count is a negative number, it starts counting from the back to the | |
# beginning of the enumerable. | |
# Be aware that a negative count implies the enumerable will be enumerated | |
# twice: once to calculate the position, and a second time to do the actual | |
# splitting. | |
def split([], _), do: {[], []} | |
def split(list, 0), do: {[], list} | |
def split(list, n) when n < 0 do | |
{a, b} = split(Enum.reverse(list), abs(n)) | |
{Enum.reverse(b), Enum.reverse(a)} | |
end | |
def split([head | tail], n) do | |
# Not tail-call optimized π | |
{head_rest, tail_rest} = split(tail, n-1) | |
{[head | head_rest], tail_rest} | |
end | |
# More performant split | |
# Tail-call optimized π | |
# Reverse is cheap | |
def fsplit(list, n) when n < 0 do | |
# Can I remove temporary variables? | |
{a, b} = fsplit(Enum.reverse(list), abs(n)) | |
{Enum.reverse(b), Enum.reverse(a)} | |
end | |
def fsplit(list, n), do: | |
_fsplit(list, n, []) | |
defp _fsplit(list, n, result) when list == [] or n == 0, do: | |
{Enum.reverse(result), list} | |
defp _fsplit([head | tail], n, result), do: | |
_fsplit(tail, n-1, [head | result]) | |
# Takes the first amount items from the enumerable. | |
# If a negative amount is given, the amount of last values will be taken. | |
# The enumerable will be enumerated once to retrieve the proper index and | |
# the remaining calculation is performed from the end. | |
def take([], _), do: [] | |
def take(_, 0), do: [] | |
def take([head | tail], n) when n > 0 do | |
[head | Enum2.take(tail, n - 1)] | |
end | |
def take(list = [_ | tail], n) do | |
if Enum.count(list) + n > 0 do | |
Enum2.take(tail, n) | |
else | |
list | |
end | |
end | |
# Tail call optimized π | |
def ftake([], _), do: [] | |
def ftake(_, 0), do: [] | |
def ftake(list, n) when n < 0, do: | |
# Triple reversing when passing in negative... π€ | |
list | |
|> Enum.reverse | |
|> ftake(abs(n)) | |
|> Enum.reverse | |
def ftake(list, n), do: | |
_ftake(list, n, []) | |
# Termination | |
defp _ftake(list, n, result) when list == [] or n == 0, do: | |
Enum.reverse(result) | |
defp _ftake([head | tail], n, result), do: | |
_ftake(tail, n - 1, [head | result]) | |
end | |
defmodule AssertionTest do | |
use ExUnit.Case, async: true | |
import Enum2 | |
test "all?" do | |
assert all?([1,2,3,4]) | |
assert all?([],fn _ -> false end) | |
assert all?([1,2,3,4], fn x -> x > 0 end) | |
refute all?([1,2,3,4], fn x -> x > 2 end) | |
end | |
test "each" do | |
# Return value for `each` is not relevant | |
import ExUnit.CaptureIO | |
sqr = &(&1 * &1 |> IO.puts) | |
assert capture_io(fn -> each([1,2,3,4], sqr) end) == "1\n4\n9\n16\n" | |
assert capture_io(fn -> each([], sqr) end) == "" | |
end | |
test "filter" do | |
assert [] |> filter(fn _ -> true end) == [] | |
assert [1,2,3,4] |> filter(fn x -> x > 0 end) == [1,2,3,4] | |
assert [1,2,3,4] |> filter(fn x -> x > 2 end) == [3,4] | |
assert [1,2,3,4] |> filter(fn _ -> false end) == [] | |
end | |
test "ffilter" do | |
assert [] |> ffilter(fn _ -> true end) == [] | |
assert [1,2,3,4] |> ffilter(fn x -> x > 0 end) == [1,2,3,4] | |
assert [1,2,3,4] |> ffilter(fn x -> x > 2 end) == [3,4] | |
assert [1,2,3,4] |> ffilter(fn _ -> false end) == [] | |
end | |
test "split" do | |
assert [] |> split(0) == {[], []} | |
assert [] |> split(4) == {[], []} | |
assert [] |> split(-4) == {[], []} | |
assert [1,2,3,4] |> split(0) == {[], [1,2,3,4]} | |
assert [1,2,3,4] |> split(2) == {[1,2], [3,4]} | |
assert [1,2,3,4] |> split(10) == {[1,2,3,4], []} | |
assert [1,2,3,4] |> split(-1) == {[1,2,3], [4]} | |
assert [1,2,3,4] |> split(-5) == {[], [1,2,3,4]} | |
end | |
test "fsplit" do | |
assert [] |> fsplit(0) == {[], []} | |
assert [] |> fsplit(4) == {[], []} | |
assert [] |> fsplit(-4) == {[], []} | |
assert [1,2,3,4] |> fsplit(0) == {[], [1,2,3,4]} | |
assert [1,2,3,4] |> fsplit(2) == {[1,2], [3,4]} | |
assert [1,2,3,4] |> fsplit(10) == {[1,2,3,4], []} | |
assert [1,2,3,4] |> fsplit(-1) == {[1,2,3], [4]} | |
assert [1,2,3,4] |> fsplit(-5) == {[], [1,2,3,4]} | |
end | |
test "take" do | |
assert [] |> take(0) == [] | |
assert [] |> take(4) == [] | |
assert [] |> take(-4) == [] | |
assert [1,2,3,4] |> take(0) == [] | |
assert [1,2,3,4] |> take(2) == [1,2] | |
assert [1,2,3,4] |> take(10) == [1,2,3,4] | |
assert [1,2,3,4] |> take(-1) == [4] | |
assert [1,2,3,4] |> take(-5) == [1,2,3,4] | |
end | |
test "ftake" do | |
assert [] |> ftake(0) == [] | |
assert [] |> ftake(4) == [] | |
assert [] |> ftake(-4) == [] | |
assert [1,2,3,4] |> ftake(0) == [] | |
assert [1,2,3,4] |> ftake(2) == [1,2] | |
assert [1,2,3,4] |> ftake(10) == [1,2,3,4] | |
assert [1,2,3,4] |> ftake(-1) == [4] | |
assert [1,2,3,4] |> ftake(-5) == [1,2,3,4] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment