Для реального кода вы должны использовать заранее подготовленное решение:
String.to_integer(string, 2)
При этом давайте рассмотрим это как упражнение для получения дополнительной информации об Elixir.Общая ошибка с Elixir - неправильное представление о переменной области видимости.Переменные в Elixir являются неизменяемыми и всегда локальными по объему, но они могут быть связаны с новыми значениями.Это усложняет задачу, если вы только начинаете, потому что похоже на , вы можете изменить значение, но на самом деле вы просто привязываете имя переменной к новому значению.Это означает, что исходная область будет по-прежнему привязана к тому же значению:
counter = 0
for i <- 1..10 do
counter = counter + 1
end
IO.inspect counter # prints 0
Еще один способ думать об этом состоит в том, что внутренняя counter
- это просто новая переменная, имя которой совпадает с именемвнешний.Приведенный выше пример эквивалентен:
counter = 0
for i <- 1..10 do
new_counter = counter + 1
end
Чтобы обойти это, как вы уже правильно заметили, мы используем такие функции, как Enum.reduce/2-3
, которые позволяют нам сохранять промежуточные результаты в аккумулятор .Таким образом, каждая из переменных, которые нужно запомнить, должна быть в аккумуляторе.Значение для следующей итерации затем возвращается из анонимной функции, которая позволяет Enum.reduce
передать его обратно в следующую итерацию.
В вашем случае это означает, что вы захотите запомнить len
и sum
, которые мы можем поместить в кортеж, чтобы передать его как {sum, len}
.Основная структура вашего сокращения должна быть:
result = Enum.reduce list, {0, len}, fn {sum, len} ->
new_sum = sum + ...
new_len = ...
{new_sum, new_len}
end
{sum, _} = result
sum
Если вы хотите сделать еще один шаг и лучше понять, как все эти части сочетаются друг с другом, я настоятельно рекомендую прочитать первые несколько глав Эликсир программирования Дэйва Томаса .Он включает в себя некоторые упражнения для создания таких утилит, как Enum.reduce
с нуля, что очень помогает.
В качестве заключительного замечания, в вашем коде есть некоторые вещи, которые можно улучшить.Например, он с радостью примет недопустимые цифры, такие как «2».Как вдохновение, вот как я бы решил эту проблему:
string
|> String.graphemes
|> Enum.reverse
|> Enum.with_index
|> Enum.reduce(0, fn
{"0", _}, acc -> acc
{"1", i}, acc -> acc + :math.pow(2, i)
end)
|> trunc()
Я уже слышу крики некоторых голосов: «но теперь вы повторяете несколько раз по списку», на что я отвечу, что ясность идетоптимизация производительности, пока это не является узким местом.Если вы действительно обнаружите, что вам нужно выжать из этого последний бит производительности, обычно лучше написать хвосто-рекурсивную функцию, которая вообще не зависит от модуля Enum
, но я оставлю это на ваше усмотрениеисследовать.