.
Шифр Цезаря – это простой метод кодирования сообщения путём сдвига каждого символа на фиксированное количество позиций алфавита. На самом деле мы реализуем некую вариацию шифра Цезаря, поскольку не будем ограничиваться только алфавитом, а возьмём весь диапазон символов Unicode.
Для сдвига символов вперёд и назад по кодовой таблице будем применять функции >ord
и >chr
, находящиеся в модуле >Data.Char
. Эти функции преобразуют символы к соответствующим кодам и наоборот:
>ghci> ord 'a'
>97
>ghci> chr 97
>'a'
>ghci> map ord "abcdefgh"
>[97,98,99,100,101,102,103,104]
Функция >ord 'a'
возвращает 97, поскольку символ >'a'
является девяносто седьмым символом в таблице символов Unicode.
Разность между значениями функции >ord
для двух символов равна расстоянию между ними в кодовой таблице.
Напишем функцию, которая принимает количество позиций сдвига и строку и возвращает новую строку, где каждый символ сдвинут вперёд по кодовой таблице на указанное число позиций.
>import Data.Char
>encode :: Int -> String -> String
>encode offset msg = map (\c -> chr $ ord c + offset) msg
Кодирование строки тривиально настолько, насколько легко взять сообщение и пройтись по каждому его символу функцией, преобразующей его в соответствующий код, прибавляющей смещение и конвертирующей результат обратно в символ. Любитель композиции записал бы такую функцию как >(chr . (+offset) . ord)
.
>ghci> encode 3 "привет марк"
>"тулеих#пгун"
>ghci> encode 5 "прикажи своим людям"
>"фхнпелн%цзунс%рйєс"
>ghci> encode 1 "веселиться вволю"
>"гжтжмйуэт!ггпмя"
И вправду закодировано!
Декодирование сообщения – это всего навсего сдвиг обратно на то же количество позиций, на которое ранее проводился сдвиг вперёд.
>decode :: Int -> String -> String
>decode shift msg = encode (negate shift) msg
Теперь проверим, декодируется ли сообщение Цезаря:
>ghci> decode 3 "тулеих#пгун"
>"привет марк"
>ghci> decode 5 "фхнпелн%цзунс%рйєс"
>"прикажи своим людям"
>ghci> decode 1 "гжтжмйуэт!ггпмя"
>"веселиться вволю"
В предыдущей главе мы видели, как работает функция >foldl
и как с её помощью реализовывать всякие крутые функции. Правда, мы пока не исследовали одну связанную с >foldl
ловушку: её использование иногда может приводить к так называемым ошибкам переполнения стека, которые случаются, если программе требуется слишком много места в одном специальном разделе памяти (в сегменте стека). Проиллюстрируем проблему, воспользовавшись свёрткой с функцией >+
для суммирования списка из сотни единиц:
>ghci> foldl (+) 0 (replicate 100 1)
>100
Пока всё работает. Но что если сделать то же самое для списка, содержащего, спасибо доктору Зло, один миллион единиц?