DEV Community

Cover image for Help shepherd to count sheep in Elixir
JetThoughts Dev for JetThoughts

Posted on • Edited on • Originally published at jetthoughts.com

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.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay