Шифр Цезаря в Haskell


Я придумал этот простой реализации шифра Цезаря. Она принимает в качестве аргумента целое число и файл, чтобы произвести зашифрованный текст Вот так:

./caesar 4 < text.raw

Вот код:

import System.Environment
import Data.List
import Data.Maybe

alnum = ['A' .. 'Z'] ++ ['a' .. 'z'] ++ ['0' .. '9']
anlen = length alnum

rotChar :: Int -> Char -> Char
rotChar r c
    | elem c alnum = alnum !! (mod (r + fromJust (elemIndex c alnum)) anlen)
    | otherwise = c

rotStr :: Int -> [Char] -> [Char]
rotStr 0 s = s
rotStr r s = (map (rotChar (mod (anlen + r) anlen)) s)

main :: IO ()
main = do
    args <- getArgs
    contents <- getContents
    putStrLn $ rotStr (read $ head args) contents

Я новичок в Haskell и я хотел бы некоторые мнения на:

  • глобальные "переменные"
  • использование скобок
  • парсинг аргумента

Может ли это быть улучшено? Любые другие советы будут оценены.



409
4
задан 16 марта 2018 в 01:03 Источник Поделиться
Комментарии
1 ответ

Глобальные "переменные"

Я бы не беспокоиться о глобальных переменных. Предполагая, что этот код будет правильно размещен внутри своего модуля, тем, вероятно, не будет в любом случае глобальные.

Использование скобок

Некоторые скобки излишни. Например, все тело второй rotStr формулы заключается в круглые скобки и те могут быть удалены.
Что касается других случаев, вы, вероятно, может избежать скобок (и немного легче переваривать кодекс) при определении подвыражения в let или where пункт.

Парсинг аргумента

Я не уверен, что вы имеете в виду. Если вы имеете в виду getArgs часть, которая выглядит нормально.


Некоторые замечания по поводу кода:


  • В rotStr 0 s = s линия является излишним. Вы можете удалить его и все будет работать нормально. Я предполагаю, что это просто оптимизация, так что все нормально.

  • Это может быть не очевидным для небольших входных данных, но выполнение кода может быть улучшена. Такие вещи, как elemIndex c alnum и alnum !! бежать в линейное время в размер alnum. Это означает, что выражение elem c alnum = alnum !! (mod (r + fromJust (elemIndex c alnum)) anlen) пойдем по списку в 2 раза. Опять же, это не имеет большого значения, но вы могли бы использовать Data.MapS для подстановки для того, чтобы получить лучшую производительность.

Вот возможная реализация через MapС:

import Data.List
import Data.Maybe
import qualified Data.Map as M (Map, empty, insert, lookup)

alnum = ['A' .. 'Z'] ++ ['a' .. 'z'] ++ ['0' .. '9']
anlen = length alnum

indices = foldr add M.empty $ zip alnum [0..]
where add (c, i) m = M.insert c i m

charsByIndex = foldr add M.empty $ zip alnum [0..]
where add (c, i) m = M.insert i c m

rotChar :: Int -> Char -> Char
rotChar r c = case M.lookup c indices of
Just i -> fromJust $ M.lookup (newIndex i) charsByIndex
Nothing -> c
where newIndex i = (i+r) `mod` anlen

rotStr :: Int -> String -> String
rotStr r s = map (rotChar r) s

Также обратите внимание, что последняя строка может быть сокращен до:

rotStr = map . rotChar

3
ответ дан 16 марта 2018 в 02:03 Источник Поделиться