Search code examples
haskellfunctional-programmingcaesar-cipher

Caesar Cipher with a key of a word Haskell


I am coding a caesar ciper where the key is inputted as a word and the numerical values from that word are the shift factors (the number pattern will repeat if not long enough). I am only applying this to upper case letters. There is either something wrong with my encrypt function or my shiftFactor function as it is outputting encrypt "ABC"ABC" as "ABCBCDCDE" instead of just "ACE". I am new to haskell so when i traced through the logic I couldnt find an obivious issue.

encrypt :: String -> String -> String
encrypt key xs = [shift sf x| x<-xs, sf<-shiftFactorArray]
    where shiftFactorArray = shiftFactor(key)

shiftFactor :: String -> [Int]
shiftFactor key = [let2int(x)|x<-key]

where the function I also called are defined by

shift :: Int -> Char -> Char
shift n c | isUpper c = int2let ((let2int c + n) `mod` 26)
          | otherwise = c

let2int :: Char -> Int
let2int c = ord c - ord 'A'

int2let :: Int -> Char
int2let n = chr (ord 'A' + n)

Solution

  • By using x <- xs, sf <- shiftFactorArray, you will iterate over every element of ss and over every element of shiftFactorArray. You need something that iterates over the two lists in parallel. You can make use of zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] for that.

    In order to repeat the key each time, you can use cycle :: [a] -> [a]:

    encrypt :: String -> String -> String
    encrypt key xs = zipWith shift (cycle (shiftFactor key)) xs
    
    shiftFactor :: String -> [Int]
    shiftFactor = map let2int

    This then gives us:

    Prelude Data.Char> encrypt "ABC" "ABCFOOBAR"
    "ACEFPQBBT"
    

    We can make encrypt point-free, by making use of function composition:

    encrypt :: String -> String -> String
    encrypt = zipWith shift . cycle . map let2int