Изучай Haskell во имя добра! (Липовача) - страница 70

.

Шифр Цезаря – это простой метод кодирования сообщения путём сдвига каждого символа на фиксированное количество позиций алфавита. На самом деле мы реализуем некую вариацию шифра Цезаря, поскольку не будем ограничиваться только алфавитом, а возьмём весь диапазон символов 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

Пока всё работает. Но что если сделать то же самое для списка, содержащего, спасибо доктору Зло, один миллион единиц?