here’s a slightly more idiomatic version, although I think there might be some corner cases where it’s not as even as it could be:
defmodule M do
@doc """
interleaves a short list b, into a long list a, such that the
presence of the short list is evenly spaced in the long list.
iex> M.interleave([1,2,3,4,5,6,7],[:a, :b])
[1, 2, :a, 3, 4, 5, :b, 6, 7]
"""
def interleave(a, b) do
a_ct = Enum.count(a)
b_ct = Enum.count(b)
# calculates how many items are in the cycle of drawing from a vs b.
recurrence = div(a_ct, b_ct) + 1
# calculate how much we have to offset the recurrence by to center it.
offset = div(a_ct + b_ct - ((b_ct - 1) * recurrence), 2)
# bootstrap the initial condition
interleave(a, b, offset, recurrence, [])
end
# terminating condition: we've exhausted all the items; since we put values on the front
# in the tail calls, reverse the list.
def interleave([], [], _, _, rev_list), do: Enum.reverse(rev_list)
def interleave(a, [b_hd | b_tl], n, recurrence, list_so_far)
when rem(n, recurrence) == 0 do
# draw from the shorter list every <recurrence>. Put on front
# of the list we're building, then tail call in.
interleave(a, b_tl, n + 1, recurrence, [b_hd | list_so_far])
end
def interleave([a_hd | a_tl], b, n, recurrence, list_so_far) do
# draw from the longer list. Put on front of the list we're building,
# then tail call in.
interleave(a_tl, b, n + 1, recurrence, [a_hd | list_so_far])
end
end
if the enum.reverse thing is a bit confusing, you can also do this:
def interleave([], [], _, _, list), do: list
def interleave(a, [b_hd | b_tl], n, interval, list_so_far)
when rem(n, interval) == 0 do
interleave(a, b_tl, n + 1, interval, list_so_far ++ [b_hd])
end
def interleave([a_hd | a_tl], b, n, interval, list_so_far) do
interleave(a_tl, b, n + 1, interval, list_so_far ++ [a_hd])
end






















