How about this one:
defmodule Example do
@spec sample(list(), list(), non_neg_integer()) :: list()
# ensures that parameters are in correct types
# calculates evenly spacing
def sample(first, second, rotation \\ 0)
when is_list(first) and is_list(second) and is_integer(rotation) and rotation >= 0 do
first_length = length(first)
second_length = length(second)
if first_length > second_length do
every = div(first_length, second_length)
do_sample(first, second, every, rotation)
else
every = div(second_length, first_length)
do_sample(second, first, every, rotation)
end
end
# make sure that rotation does not conflicts with evenly spacing
defp do_sample(first, second, every, rotation) when rotation > every do
corrected_rotation = rem(rotation, every)
do_sample(first, second, every, corrected_rotation)
end
# for each indexed element
# in reversed order (important for inserting index conflicts)
# call custom insert with index calculated by index of second list, every and rotation
defp do_sample(first, second, every, rotation) do
second
|> Enum.with_index()
|> Enum.reverse()
|> Enum.reduce(first, &custom_insert(&1, &2, every, rotation))
end
# small pattern-matching optimization
defp custom_insert({element, 0}, acc, _every, rotation),
do: List.insert_at(acc, rotation, element)
# small pattern-matching optimization
defp custom_insert({element, 1}, acc, every, rotation),
do: List.insert_at(acc, every + rotation, element)
defp custom_insert({element, index}, acc, every, rotation),
do: List.insert_at(acc, every * index + rotation, element)
end
a = Enum.to_list(1..4) # range to list: [1, 2, 3, 4]
b = String.graphemes("ab") # string to grapheme list: ["a", "b"]
Example.sample(a, b) # ["a", 1, 2, "b", 3, 4]
Example.sample(a, b, 0) # ["a", 1, 2, "b", 3, 4]
Example.sample(a, b, 1) # [1, "a", 2, 3, "b", 4]
Example.sample(a, b, 2) # [1, 2, "a", 3, 4, "b"]
Example.sample(a, b, 3) # [1, "a", 2, 3, "b", 4]
Example.sample(a, b, 4) # ["a", 1, 2, "b", 3, 4]
Example.sample(a, b, 5) # [1, "a", 2, 3, "b", 4]