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