elixir

Elixir pattern matching head in a list


Trying out an elixir tutorial and I found this very interesting construct for recursion.

So I have a list:

    flares = [
        %{classification: :X, scale: 99, date: Date.from({1859, 8, 29})},
        %{classification: :M, scale: 5.8, date: Date.from({2015, 1, 12})},
        %{classification: :M, scale: 1.2, date: Date.from({2015, 2, 9})},
        %{classification: :C, scale: 3.2, date: Date.from({2015, 4, 18})},
        %{classification: :M, scale: 83.6, date: Date.from({2015, 6, 23})},
        %{classification: :C, scale: 2.5, date: Date.from({2015, 7, 4})},
        %{classification: :X, scale: 72, date: Date.from({2012, 7, 23})},
        %{classification: :X, scale: 45, date: Date.from({2003, 11, 4})}
    ]

And I want to calculate the sum of scale's but with a tolerance for each classification. I was hoping I could do something like:

def total_flare_power(flares), do: total_flare_power(flares, 0)
def total_flare_power([%head{classification: :M} | tail], total) do
    total_flare_power(tail, total + head.scale * 0.92) 
end
def total_flare_power([%head{classification: :C} | tail], total) do
    total_flare_power(tail, total + head.scale * 0.78) 
end
def total_flare_power([%head{classification: :X} | tail], total) do
    total_flare_power(tail, total + head.scale * 0.68) 
end
def total_flare_power([], total), do: total

But I endup with this error message:

 ** (FunctionClauseError) no function clause matching in Solar.total_flare_power/2

Looks like my named struct that I am trying to match the head on does not work.


Solution

  • You're doing

    %head{classification: :M}
    

    That matches structs of type head with classification of :M. What you probably meant was:

    def total_flare_power([%{classification: :M} = head | tail], total) do
    ...
    end
    

    Which matches any map with :m under :classification and binds it to the variable head. Incidentally you might want to extract your logic computing the adjusted scales and sum those with library functions like this:

    flares
    |> Enum.map(fn
      %{classification: :M, scale: scale} -> scale * 0.92
      %{classification: :C, scale: scale} -> scale * 0.78
      %{classification: :X, scale: scale} -> scale * 0.68
    end)
    |> Enum.sum
    

    This version also accesses the scale by destructuring, without the need to assign the result of the pattern match.