Normally you should use the stdlib if there is some help there (most of the time there is, in this case chunk_by/2 can do all the hard work). But you can also do it by hand…
defmodule Chunker do
def chunk(), do: 
def chunk([head|tail]), do: _chunk(tail, , 1, head)
# this is the stop condition, all elements of the input are consumed
defp _chunk(, acc, count, _), do: acc ++ [count]
# this function only matches when the head of the list is the same as the last
# you just have to give them the same name ("last") - nice, isn't it?
defp _chunk([last|tail], acc, count, last), do: _chunk(tail, acc, count+1, last)
# a new chunk begins!
defp _chunk([head|tail], acc, count, _last), do: _chunk(tail, acc ++ [count], 1, head)
Note: this is slow for large lists because of
acc ++ [count]
In each of those, the whole acc-list is walked until the end is found.
Can be optimized by
Indeed, it is a really simple and performant solution.
You do one pass (iteration) to count the values, and one pass to reverse them.
There are no additional copies of the list, if you give 1 million elements in the list, there will never be more than 1million elements in total between the current list and the accumulator, and as the list is process this number will decrease.
Sebb’s solution is pretty much the same but with function definitions.