#functional-programming #elixir
#функциональное программирование #elixir
Вопрос:
Я прохожу курс функционального программирования и, исходя из ООП, у меня болит мозг, пытаясь решить что-то, что я считаю довольно тривиальным, но я просто не понимаю концепцию здесь. Это упражнение, которое мне нужно сделать для школы
Учитывая фразу, в моем случае это
«Сопоставление шаблонов с Elixir. Помните, что знак равенства — это оператор сопоставления, а не присваивание «
Мне нужно проверить начальные буквы каждого слова на соответствие шаблону и применить определенную модификацию в зависимости от шаблона.
Я даже не совсем уверен, что создает мой текущий код, когда я проверяю x и y, я вроде понимаю, что делают циклы, у них есть список для каждого слова фразы, и этот внутренний список состоит из проверок на каждую отдельную букву, nil, если она не начинается с этой буквы и измененного словаесли это произойдет.
ООП во мне хочет, чтобы эти циклы не возвращали никаких «нулевых значений» и возвращали только одно отредактированное слово на каждой итерации. В функциональном программировании я не могу прерывать циклы и принудительно возвращать, поэтому мне нужно подумать об этом по-другому.
Мой вопрос в том, как мне подойти к этой проблеме с точки зрения функционального программирования? Вначале я получаю список слов, которые образуют фразу, затем я хочу отредактировать каждое слово и в конце снова получить список, содержащий эти отредактированные слова. Возможно, псевдокодоподобная структура о том, как решить эту проблему, помогла бы мне понять основные концепции.
Вот мой текущий код:
#Task two
def taskTwo() do
IO.puts "Task Two"
IO.puts "Pattern Matching with Elixir. Remember that equals sign is a match operator, not an assignment"
IO.puts "Task Twon...Editing Words ..."
phrase = String.downcase("Pattern Matching with Elixir. Remember that equals sign is a match operator, not an assignment") |> String.split()
x = for word <- phrase do
checkVowels(word)
end
y = for word <- phrase do
checkConsonants(word)
end
IO.inspect x
IO.inspect y
end
#check vowels
def checkVowels(word) do
vowels = ["a","e","i","o","u"]
for vowel <- vowels do
if String.starts_with?(word, vowel) do
word <> "ay "
end
end
end
#check consonants
def checkConsonants(word) do
consonants = ["b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","z","y"]
for consonant <- consonants do
if String.starts_with?(word, consonant) do
edited = String.replace_prefix(word, consonant, "")
edited <> consonant <> "ay "
end
end
end
Изменения, которые мне нужно применить: сначала проверьте начальную букву и примените модификацию, затем проверьте еще раз, есть ли внутри word какие-либо комбинации из нескольких букв
Words beginning with consonants should have the consonant moved to the end of the word, followed by "ay".
Words beginning with vowels (aeiou) should have "ay" added to the end of the word.
Some groups of letters are treated like consonants, including "ch", "qu", "squ", "th", "thr", and "sch".
Some groups are treated like vowels, including "yt" and "xr".
Комментарии:
1. «Мне нужно проверить начальные буквы каждого слова на соответствие шаблону и применить определенную модификацию в зависимости от шаблона». Что это за шаблон и какие изменения вам нужно применить?
2. @AdamMillerchip Я отредактировал исходный пост, в конце концов, это требования. Сначала мне нужно проверить начальную букву и внести изменения. Это будет сделано для каждого слова, но после первой модификации мне нужно еще раз проверить, содержит ли word какие-либо комбинации из нескольких букв, а затем выполнить и это.
3. «Я не могу прерывать циклы» да, это одна из многих причин, почему обычно в FP вы их не используете. Я не знаю elixir, чтобы предложить альтернативу, но был бы шокирован, если бы ее не существовало. Опять же, я не знаю elixir, но опубликованный вами код выглядит чисто процедурным, в нем практически нет функциональности. Насколько я могу судить, вы могли бы перевести это почти строка за строкой на Python. Вы уверены, что именно так вы должны это решать?
4. @JaredSmith Вы правы! До сих пор мы провели пару уроков, и перед этим заданием мы рассмотрели сопоставление с образцом, те примеры, над которыми мы работали, в основном все такие, как в руководстве по началу работы Elixir. Затем мы получили задание, но я все еще так привязан к ООП, что не понимаю, как мне поступить с FP. Здесь мне не хватает какой-то фундаментальной концепции или идей.
5. Часть путаницы может быть связана с синтаксисом for / if:
for
на самом деле это скорееEnum.map/2
скрыто. Таким образом, он вернет список с таким количеством элементов, сколько в исходном перечислимом (за исключением случаев фильтрации). Точно так жеif
всегда будет возвращать значение, если у вас его нетelse
nil
. Я настоятельно рекомендую ознакомиться сEnum
модулем, который должен предоставить вам почти все, что вам нужно для замены типичных «циклов». В вашем случае я думаю, чтоEnum.find/2
это то, что вы ищете.
Ответ №1:
Один из возможных способов (возможно, не самый эффективный, но использующий некоторые возможности Elixir) — использовать рекурсию и сопоставление с образцом. Итак, один из возможных способов решить первую часть вашего упражнения.
defmodule Example do
@vowels ~w[a e i o u y]
@phrase "Pattern Matching with Elixir. Remember that equals sign is a match operator, not an assignment"
def task_two() do
phrase =
@phrase
|> String.downcase()
|> String.split()
|> Enum.reverse()
# splitting the first letter of each word
|> Enum.map(amp;String.split_at(amp;1, 1))
# pass the phrase and an accumulator as arguments
result = check_words(phrase, [])
IO.inspect(result)
end
# using recursion to traverse the list
# when the phrase have no more words, it will match the empty list
def check_words([], phrase) do
Enum.join(phrase, " ")
end
# pattern match on the first letter and using guards to decide if vowel
def check_words([{first_letter, rest_of_word} | rest_of_list], accumulator)
when first_letter in @vowels do
# call the function again, with the rest of the list
new_word = first_letter <> rest_of_word <> "ay"
check_words(rest_of_list, [new_word | accumulator])
end
# when the pattern does not match for vowels, it should be a consonant
def check_words([{first_letter, rest_of_word} | rest_of_list], accumulator) do
new_word = rest_of_word <> first_letter <> "ay"
check_words(rest_of_list, [new_word | accumulator])
end
end
Вам нужно будет пропустить / обработать запятые и, возможно, еще несколько настроек, чтобы быть полностью функциональными. Но надеюсь, что это поможет в качестве общей идеи.
Примечания:
- пишите тесты. Они действительно помогут вам понять, что делает код
- Elixir — удивительный язык. Если вы пришли из ООП, вначале это может показаться странным. Эта книга:
Programming Elixir
действительно хороша и очень поможет вам, если вы хотите быстро прогрессировать.
Комментарии:
1. Я думаю, что в этой части есть небольшая ошибка
result = check_words(phrase, [])
. Разве параметры не должны быть изменены (на основе того, как функции определены ниже)?2. Спасибо за проверку. Я уже поменял их местами перед началом рекурсии, в верхней функции
|> Enum.reverse()
, поэтому в конце должно быть в правильном порядке.3. Теперь, когда я снова смотрю на это, я не уверен, что я видел. Для меня это выглядит правильно. Извините за путаницу.