Первая попытка на Haskell: преобразование строчных букв в прописные
Я недавно начал изучать Haskell, и я попытался создать функцию для преобразования слова в нижнем регистре в слово в верхнем регистре, это работает, но я не знаю, насколько это хорошо, и у меня есть несколько вопросов. Код:
lowerToUpperImpl element list litereMari litereMici =
do
if not (null list) then
if (head list) == element then
['A'..'Z'] !! (length ['A'..'Z'] - length (tail list ) -1)
else
lowerToUpperImpl element (tail list) litereMari litereMici
else
'0' --never to be reached
lowerToUpper element = lowerToUpperImpl element ['a'..'z'] ['A'..'Z'] ['a'..'z']
lowerToUpperWordImpl word =
do
if not (null word) then
lowerToUpper (head (word)):(lowerToUpperWordImpl (tail word))
else
""
- Мне не нравится, как я передал буквы в верхнем и нижнем регистре, я не могу просто объявить глобальные переменные или что-то еще?
- Как бы вы подошли к заполнению мертвой ветки?
Каковы будут ваши предложения по улучшению этого?
2 ответа
Во-первых, если /else обычно рассматривается как опора в функциональных языках программирования, именно потому, что они на самом деле не должны использоваться как операции ветвления, а как функции. Также помните, что списки не знают своей длины в Haskell, поэтому вычисление O(n)
шаг. Это особенно плохо для бесконечных списков.
Я бы написал так: (если бы я не импортировал библиотеки):
uppercase :: String -> String
uppercase = map (\c -> if c >= 'a' && c <= 'z' then toEnum (fromEnum c - 32) else c)
Позволь мне объяснить. Этот код использует Enum
а также Ord
Типы классов, которые Char
удовлетворяет. fromEnum c
переводит c
к его ASCII-коду и toEnum
принимает коды ASCII к их эквивалентным символам. Функция, которую я поставляю map
просто проверяет, что символ в нижнем регистре и вычитает 32 (разница между "A" и "a"), если он есть, и оставляет его в покое в противном случае.
Конечно, вы всегда можете написать:
import Data.Char
uppercase :: String -> String
uppercase = map toUpper
Надеюсь это поможет!
Вот что я всегда рекомендую людям в ваших обстоятельствах:
- Разбейте проблему на более мелкие части и напишите отдельные функции для каждой части.
- Используйте библиотечные функции везде, где вы можете решить мелкие подзадачи.
- В качестве упражнения после того, как вы закончите, выясните, как самостоятельно написать используемые вами библиотечные функции.
В этом случае мы можем применить пункты следующим образом. Во-первых, так как String
в Хаскеле это синоним [Char]
(список Char
), мы можем разбить вашу проблему на две части:
- Превратите персонажа в заглавную копию.
- Преобразуйте список, применяя функцию отдельно для каждого из его членов.
Второй момент: как указывает ответ Алекса, Data.Char
стандартный модуль библиотеки поставляется с функцией toUpper
который выполняет первую задачу, а Prelude
библиотека поставляется с map
который выполняет второй. Таким образом, использование этих двух вместе решает вашу проблему немедленно (и это именно тот код, который Алекс написал ранее):
import Data.Char
uppercase :: String -> String
uppercase = map toUpper
Но я бы сказал, что это лучшее решение (самое короткое и ясное), и для новичка это первый ответ, который вы должны попробовать.
Применяя мой третий пункт: после того, как вы придумали стандартное решение, очень полезно попытаться написать свои собственные версии функций библиотеки, которые вы использовали. Дело в том, что таким образом вы изучаете три вещи:
- Как разбить проблемы на более легкие, более мелкие части, желательно многоразовые;
- Содержание стандартных библиотек языка;
- Как написать простые "фундаментальные" функции, которые предоставляет библиотека.
Таким образом, в этом случае вы можете попробовать написать свои собственные версии toUpper
а также map
, Я предоставлю скелет для map
:
map :: (a -> b) -> [a] -> [b]
map f [] = ???
map f (x:xs) = ???