Кодирование принципов серверной части игры FizzBuzz

Эта статья посвящена кодированию неявных условий в FizzBuzz. Это заставляет вас переходить по строкам кода, которые дают аналогичный результат с другим синтаксисом.

Кому ?

Это для новичков в Эликсире. Но в конце это будет более информативным.

Что такое Fizz-Buzz?

Когда вы гуглите, вы найдете следующие фразы о FizzBuzz.

FizzBuzz - это очень простая задача программирования, которая используется на собеседовании с разработчиками программного обеспечения, чтобы определить, действительно ли кандидат может писать код.

Шипучка - это групповая игра в слова, которая помогает детям научиться делению. Игроки по очереди подсчитывают постепенно, заменяя любое число, делящееся на три словом «fizz», а любое число, делимое на пять, словом «buzz» и, если он делится на три и пять, со словом FizzBuzz или просто напечатайте само число.

Это все о FizzBuzz. О ... наконец-то вы узнали, что такое FizzBuzz. Так держать…

Что мы будем здесь кодировать?

Мы просто печатаем числа в диапазоне, заменяя их словами Fizz, Buzz и FizzBuzz, используя игровой протокол.

Время писать код…

Раздел Fizz

Здесь я назвал модуль FizzBuzz, конечно, все делают то же самое и написали play/2 функцию.

Сначала мы просто печатаем числа в диапазоне и смотрим, как это работает.

defmodule FizzBuzz do
  def play(min, max) do 
    Enum.each(min..max, fn(num)-> IO.puts num end )
  end
end

играть / 2

Эта функция принимает два числа в качестве входных технически минимального и максимального значений в диапазоне. Итак, мы можем создать диапазон с помощью макроса ../2.

мин Макс

Диапазон внутри представлен в виде структуры. Диапазон реализует протокол Enumerable, что означает, что функции в модуле Enum могут использоваться для работы с диапазонами.

Вы можете проверить реализованные протоколы с помощью i/1

i 1..2

Итак, мы воспользовались помощью функции each в модуле Enum, чтобы выполнить итерацию функции по диапазону и распечатать числа.

fn (num) - ›IO.puts num end

Здесь мы использовали анонимную функцию для печати чисел путем вызова IO.puts внутри функции.

Сопоставление с образцом по входным значениям функции

Здесь мы напишем другую функцию с тем же именем play, но на этот раз она принимает только один вход play/1. В соответствии с ООП это называется перегрузкой метода.

Вот так выглядит код.

defmodule FizzBuzz do
  def play(min, max) do 
    Enum.each(min..max, fn(num)-> play(num) end )
  end
  def play(num) when rem(num, 3) == 0 do
    IO.puts "Fizz"
  end
end

Изменения:

Вместо вызова IO.puts внутри анонимной функции мы создали функцию для печати. Здесь, внутри определения функции play/1, мы воспользовались предложением Elixir guard when, чтобы выполнить условия игры.

Если число делится на три, остаток будет 0. Итак, мы используем функцию rem/2, чтобы получить остаток, и ==, чтобы проверить значение истинности выражения. Если значение истинности равно true, выполняется определение функции, в противном случае определение не выполняется.

Теперь скопируйте код и вставьте его в iex. Компилируется хорошо.

Теперь вызовите функцию воспроизведения как

iex> FizzBuzz.play 1,20

Вы увидите ошибку времени выполнения. Он скомпилировался хорошо, но мы получили ошибку во время выполнения.

Вот так выглядит ошибка.

Исправление проблем

Что не так с нашим кодом?

Первым значением в диапазоне 1..20 будет 1, потому что диапазоны всегда включают.

Первая итерация вызова функции будет выглядеть так:

Enum.each -- 
fn(1)-> play(1) end

Здесь play функция запускается со значением 1, но выражение предохранительного предложения rem(1, 3)==0 приведет к false Таким образом, предложение функции не соответствует переданному значению.

Итак, нам нужно написать еще print функцию, которая будет печатать числа, которые не делятся на 3, и вот так выглядит код.

defmodule FizzBuzz do
  def play(min, max) do 
    Enum.each(min..max, fn(num)-> play(num) end )
  end
  def play(num) when rem(num, 3) == 0 do
    IO.puts "Fizz"
  end
  def play(num) do
    IO.puts num
  end
end

Теперь попробуйте, скопировав вставку вышеперечисленных строк кода внутрь iex и вызовите функцию воспроизведения как FizzBuzz.play 1,20.

Вы увидите, что числа, кратные трем итерациям, будут печатать Fizz вместо числа.

Живая лента

Как и в случае с Fizz, мы должны добавить еще одну функцию play/1 function с защитным условием, проверяющим делимость на 5.

Давайте посмотрим обновленный код

defmodule FizzBuzz do
  def play(min, max) do 
    Enum.each(min..max, fn(num)-> play(num) end )
  end
  def play(num) when rem(num, 3) == 0 do
    IO.puts "Fizz"
  end
 def play(num) when rem(num, 5) == 0 do
    IO.puts "Buzz"
  end
  def play(num) do
    IO.puts num
  end
end

Раздел FizzBuzz

Подобно Fizz и Buzz, мы можем проверить делимость числа на 3 и 5 с учетом НОК (наименьшее общее кратное) обоих чисел. НОК 3 и 5 равно 15. Итак, если число делится на 15, то оно делится как на 3, так и на 5.

Давайте добавим еще одну функцию.

defmodule FizzBuzz do
  def play(min, max) do 
    Enum.each(min..max, fn(num)-> play(num) end )
  end
  def play(num) when rem(num, 3) == 0 do
    IO.puts "Fizz"
  end
  def play(num) when rem(num, 5) == 0 do
    IO.puts "Buzz"
  end
  def play(num) when rem(num, 15) == 0 do
    IO.puts "FizzBuzz"
  end
  def play(num) do
    IO.puts num
  end
end

Теперь скопируйте и вставьте приведенные выше строки кода и запустите функцию play/2.

К нашему удивлению, он печатает Fizz вместо FizzBuzz на итерации 15.

Что не так с нашим кодом?

Исправление проблем

defmodule FizzBuzz do
def play(min, max) do 
    Enum.each(min..max, fn(num)-> play(num) end )
  end
def play(num) when rem(num, 3) == 0 do
    IO.puts "Fizz"
  end
def play(num) when rem(num, 5) == 0 do
    IO.puts "Buzz"
  end
def play(num) when rem(num, 15) == 0 do
    IO.puts "FizzBuzz"
  end
def play(num) do
    IO.puts num
  end
end

Давайте интерпретируем код на 15 итерациях.

Enum.each -- #15
play(15)
-- 
play(15) when rem(15, 3) == 0 do 

Выражение rem(15, 3)==0 приведет к true. Итак, он просто печатает Fizz и переходит к следующей итерации.

Итак, нам нужно изменить порядок наших функций. Функция play/1 с защитным условием rem(num, 15) == 0 должна быть выше всех остальных функций. Итак, сначала проверяется делимость на 15, затем на 3, а затем на 5.

Давайте обновим код.

defmodule FizzBuzz do
def play(min, max) do 
    Enum.each(min..max, fn(num)-> play(num) end )
  end
def play(num) when rem(num, 15) == 0 do
    IO.puts "FizzBuzz"
  end
def play(num) when rem(num, 3) == 0 do
    IO.puts "Fizz"
  end
def play(num) when rem(num, 5) == 0 do
    IO.puts "Buzz"
  end
def play(num) when rem(num, 15) == 0 do
    IO.puts "FizzBuzz"
  end
def play(num) do
    IO.puts num
  end
end

Теперь скопируйте и вставьте приведенные выше строки кода в iex и запустите функцию FizzBuzz.play 1, 20.

Ура! Мы закончили логику кода.

Но в названии статьи написано, что нам нужно сделать это в семи (7) строках кода.

Давайте улучшим наш код.

Что еще мы можем улучшить?

Вместо того, чтобы каждый раз копировать вставку в iex, мы можем создать файл fizz_buzz.ex и вставить в него код.

Теперь запустите команду

iex fizz_buzz.ex

Вы должны находиться в том же каталоге, где существует файл.

В противном случае вам нужно запустить команду как

iex path/to/file/fizz_buzz.ex

Он загружает модуль в ваш новый iex сеанс. Вы все еще можете проверить, загружен ли модуль с помощью Code.ensure_loaded FizzBuzz

Изменения в файле fizz_buzz.ex можно перекомпилировать с помощью r FizzBuzz. Здесь r перекомпилирует и перезагружает данный модуль.

Улучшение кода.

Пока что наш код состоит из 20 строк после удаления пустых новых строк.

Теперь нам нужно улучшить наш код, чтобы он выглядел намного лучше, чем раньше.

Замены

В elixir мы можем заменить анонимную функцию fn(num)-> play(num) end на &(play(&1))

Так что обновите строку

to

Сохраните файл и перекомпилируйте его с r fizz_buzz.ex

Мы можем упростить это, просто передав имя функции с арностью для перебора диапазона в each

Итак, обновим строку

Enum.each(min..max, &(play(&1)))

to

Enum.each(min..max, &play/1)

Теперь сохраните файл с обновлениями, затем перекомпилируйте и протестируйте его. Это все еще работает.

Однострочные функции в Elixir

Если вы четко соблюдаете логику кода, большинство наших определений функций представляют собой одну строку. Таким образом, мы можем избежать do .. end блоков. Мы можем обновить определения наших функций с помощью do: однострочного стиля.

#before
def play(num) when rem(num, 15) == 0 do
  IO.puts "FizzBuzz"
end
#after updating
def play(num, 3) when rem(num, 3) == 0, do: IO.puts "FizzBuzz"

Точно так же мы обновим остальные определения функций с помощью базы кода в стиле одной строки.

Вот так это выглядит после обновления

defmodule FizzBuzz do
  def play(min, max), do: Enum.each(min..max, &play/1)
  def play(num) when rem(num, 15) == 0, do: IO.puts "FizzBuzz"
  def play(num) when rem(num, 3) == 0, do: IO.puts "Fizz"
  def play(num) when rem(num, 5) == 0, do: IO.puts "Buzz"
  def play(num), do: IO.puts num
end

Бум! Мы разработали кодовую логику в 7 строках. Это было здорово с твоей стороны ...

Эта статья изначально размещена в Блоге Ahamtech

Удачного кодирования…