Help shepherd to count sheep in Elixir

jetthoughts-dev

JetThoughts Dev

Posted on June 7, 2024

Help shepherd to count sheep in Elixir

Shepard (Mass Effect)

I have been thinking about learning a functional language for a long time. So recently I’ve started to play around with Elixir. I am reading official guide and docs for theory. And also I do simple katas on Codewars for practice.

The last kata is **Counting sheep… **And I’ve come up with 13 solutions for it. Let me show it to you and explain why I need so many.

Task

Consider an array of sheep where some sheep may be missing from their place. We need a function that counts the number of sheep present in the array (true means present).

For example,

[true,  true,  true,  false,
  true,  true,  true,  true ,
  true,  false, true,  false,
  true,  false, false, true ,
  true,  true,  true,  true ,
  false, false, true,  true]
Enter fullscreen mode Exit fullscreen mode

The correct answer would be 17.

Solution boilerplate


defmodule Shepherd do
  def count_sheeps(sheeps) do
    # TODO: for Elixir only true/false values can be presented the in sheeps list
  end
end
Enter fullscreen mode Exit fullscreen mode

Sample Tests

defmodule TestShepherd do
  use ExUnit.Case
  import Shepherd, only: [count_sheeps: 1]

  test "work for some examples" do
    assert count_sheeps([]) == 0
    assert count_sheeps([true]) == 1
    assert count_sheeps([true, false]) == 1
    assert count_sheeps([false, false]) == 0
    assert count_sheeps(
    [true,  true,  true,  false,
     true,  true,  true,  true ,
     true,  false, true,  false,
     true,  false, false, true]) == 11
  end
end
view raw
Enter fullscreen mode Exit fullscreen mode

My solutions

defmodule ShepherdHeadTail do
  def count_sheeps([]), do: 0

  def count_sheeps(sheeps) do
    [head | tail] = sheeps
    count_sheeps(tail) + (if head, do: 1, else: 0)
  end
end

defmodule ShepherdAccumulator do
  def count_sheeps(sheeps, accumulator \\ 0)

  def count_sheeps([], accumulator) do
    accumulator
  end

  def count_sheeps([head | tail], accumulator) do
    count_sheeps tail, accumulator + (if head, do: 1, else: 0)
  end
end

defmodule ShepherdIsSheep do
  def is_sheep?(true), do: 1
  def is_sheep?(false), do: 0

  def count_sheeps([]), do: 0

  def count_sheeps([head | tail]) do
    is_sheep?(head) + count_sheeps(tail)
  end
end

defmodule ShepherdIsSheepAccumulator do
  def is_sheep?(true), do: 1
  def is_sheep?(false), do: 0

  def count_sheeps(sheeps, accumulator \\ 0)

  def count_sheeps([], accumulator), do: accumulator

  def count_sheeps([head | tail], accumulator) do
    count_sheeps(tail, is_sheep?(head) + accumulator)
  end
end

defmodule ShepherdFoldl do
  def is_sheep?(true, acc), do: 1 + acc
  def is_sheep?(false, acc), do: acc

  def count_sheeps(sheeps) do
    List.foldl sheeps, 0, &is_sheep?/2
  end
end

defmodule ShepherdCount do
  def count_sheeps(sheeps) do
    Enum.count sheeps, fn x -> x end
  end
end

defmodule ShepherdFilter do
  def count_sheeps(sheeps) do
    Enum.filter(sheeps, fn x -> x end) |> Enum.count
  end
end

defmodule ShepherdGroupBy do
  defp counter(:error), do: []
  defp counter({:ok, list}), do: list

  def count_sheeps(sheeps) do
    # alternative: split_with
    Enum.group_by(sheeps, &(&1)) |> Map.fetch(true) |> counter |> Enum.count
  end
end

defmodule ShepherdMapJoin do
  def count_sheeps(sheeps) do
    Enum.map_join(sheeps, &(if &1, do: "1", else: "")) |> String.length
  end
end

defmodule ShepherdReduce do
  def accumulate(true, acc), do: acc + 1
  def accumulate(false, acc), do: acc

  def count_sheeps(sheeps) do
    Enum.reduce(sheeps, 0, &accumulate/2)
  end
end

defmodule ShepherdSum do
  def count_sheeps(sheeps) do
    Enum.map(sheeps, fn(x) -> if x, do: 1, else: 0 end) |> Enum.sum
  end
end

defmodule ShepherdDuplicate do
  def count_sheeps(sheeps) do
    (sheeps -- List.duplicate(false, length(sheeps))) |> length
  end
end

defmodule ShepherdDelete do
  def count_sheeps(sheeps, acc \\ 0) do
    new_sheeps = List.delete sheeps, true
    if new_sheeps == sheeps do
      acc
    else
      count_sheeps new_sheeps, acc + 1
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

When I first saw this kata I had very small grasp on how to solve it or cycle through a list in Elixir in general. So I had to look for some clues in guides.

Also during my little investigation I browsed docs on Enum and *List *and I was curious to try few functions from there too. Lastly, while I am still on that task I decided to try out syntax peculiarities of Elixir that I already read about.

So this ends my little story on learning a new language. I think this is an interesting approach: take a relatively simple task in a new language and try to solve it in different ways. I see quite a few benefits here.

It would be cool if you propose your solution in comments or share your thoughts on how to learn a new language.

💖 💪 🙅 🚩
jetthoughts-dev
JetThoughts Dev

Posted on June 7, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Help shepherd to count sheep in Elixir