Avatar of paulfioravanti

paulfioravanti's solution

to Largest Series Product in the Elixir Track

Published at Aug 06 2019 · 0 comments
Instructions
Test suite
Solution

Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.

For example, for the input '1027839564', the largest product for a series of 3 digits is 270 (9 * 5 * 6), and the largest product for a series of 5 digits is 7560 (7 * 8 * 3 * 9 * 5).

Note that these series are only required to occupy adjacent positions in the input; the digits need not be numerically consecutive.

For the input '73167176531330624919225119674426574742355349194934', the largest product for a series of 6 digits is 23520.

Running tests

Execute the tests with:

$ mix test

Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant @tag :pending with a # symbol.

For example:

# @tag :pending
test "shouting" do
  assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end

Or, you can enable all the tests by commenting out the ExUnit.configure line in the test suite.

# ExUnit.configure exclude: :pending, trace: true

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

Source

A variation on Problem 8 at Project Euler http://projecteuler.net/problem=8

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

largest_series_product_test.exs

defmodule LargestSeriesProductTest do
  use ExUnit.Case

  # @tag :pending
  test "largest product of 2" do
    assert Series.largest_product("0123456789", 2) == 72
  end

  @tag :pending
  test "largest product of a tiny number" do
    assert Series.largest_product("12", 2) == 2
  end

  @tag :pending
  test "another tiny number" do
    assert Series.largest_product("19", 2) == 9
  end

  @tag :pending
  test "largest product of 2 shuffled" do
    assert Series.largest_product("576802143", 2) == 48
  end

  @tag :pending
  test "largest product of 3" do
    assert Series.largest_product("0123456789", 3) == 504
  end

  @tag :pending
  test "largest product of 3 shuffled" do
    assert Series.largest_product("1027839564", 3) == 270
  end

  @tag :pending
  test "largest product of 5" do
    assert Series.largest_product("0123456789", 5) == 15120
  end

  @tag :pending
  test "some big number" do
    assert Series.largest_product("73167176531330624919225119674426574742355349194934", 6) ==
             23520
  end

  @tag :pending
  test "some other big number" do
    assert Series.largest_product("52677741234314237566414902593461595376319419139427", 6) ==
             28350
  end

  @tag :pending
  test "number with all zeroes" do
    assert Series.largest_product("0000", 2) == 0
  end

  @tag :pending
  test "number where all products are zero" do
    assert Series.largest_product("99099", 3) == 0
  end

  @tag :pending
  test "identity with empty string" do
    assert Series.largest_product("", 0) == 1
  end

  @tag :pending
  test "identity with non-empty string" do
    assert Series.largest_product("123", 0) == 1
  end

  @tag :pending
  test "raises if span is too large" do
    assert_raise ArgumentError, fn ->
      Series.largest_product("123", 4)
    end
  end

  @tag :pending
  test "raises with empty string but non-zero span size" do
    assert_raise ArgumentError, fn ->
      Series.largest_product("", 1)
    end
  end

  @tag :pending
  test "raises with non-empty string and negative span size" do
    assert_raise ArgumentError, fn ->
      Series.largest_product("1234", -1)
    end
  end
end

test_helper.exs

ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)
defmodule Series do
  defguardp span_too_large?(number_string, size)
            when size > byte_size(number_string)

  defguardp non_zero_size_on_empty_string?(number_string, size)
            when size != 0 and number_string == ""

  defguardp negative_size_on_non_empty_string?(number_string, size)
            when size < 0 and number_string != ""

  defguardp invalid?(number_string, size)
            when span_too_large?(number_string, size) or
                   non_zero_size_on_empty_string?(number_string, size) or
                   negative_size_on_non_empty_string?(number_string, size)

  @zero_series_product 1

  @doc """
  Finds the largest product of a given number of consecutive numbers in a given string of numbers.
  """
  @spec largest_product(String.t(), non_neg_integer) :: non_neg_integer
  def largest_product(number_string, size) when invalid?(number_string, size) do
    raise ArgumentError
  end

  def largest_product(_number_string, 0), do: @zero_series_product

  def largest_product(number_string, size) do
    number_string
    |> String.graphemes()
    |> Enum.map(&String.to_integer/1)
    |> Enum.chunk_every(size, 1, :discard)
    |> Enum.reduce(0, &compare_products/2)
  end

  defp compare_products(subseries, acc) do
    product = Enum.reduce(subseries, &*/2)
    if product > acc, do: product, else: acc
  end
end

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?