1

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)
Jane
  • 47
  • 8

1 Answers1

1

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
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • why does shiftFactor = map let2int work? map takes two arguments and let2int takes 1 so the argument lists do not match up? – Jane Oct 27 '20 at 13:50
  • @Jane: because in functional programming one an pass functions as paramerters, and return functions as a result. In Haskell every function takes *exactly* one parameter. – Willem Van Onsem Oct 27 '20 at 13:54
  • I still do not understand why It works compared to the declaration of map? Is this code like a 'shortened' version? – Jane Oct 28 '20 at 10:15
  • @Jane: map has type `(a -> b) -> ([a] -> [b])`. So in Haskell every function takes exactly *one* parameter, and might produce a function that takes the next parameter. If you thus write `map (+1) [1,4,2,5]`, then `map (+1)` produces a new function that will take a list, and increment each element. This thus means that `map let2int` is a function that takes a list of `Char`s, and produces a list of `Int`s. – Willem Van Onsem Oct 28 '20 at 10:19
  • okay that makes sense so I could write shiftFactor s = map let2int s – Jane Oct 28 '20 at 10:51
  • @Jane: yes, but the one without `s` is often considered more clean: https://wiki.haskell.org/Pointfree – Willem Van Onsem Oct 28 '20 at 10:52