# Ejercicio de cifrado de Vigenere en Haskell -- beginner campo con algorithm campo con haskell campo con vigenere-cipher camp codereview Relacionados El problema

## Vigenere cipher exercise in Haskell

3

### problema

Español

Esta es mi implementación usando el terrible ` !! : `

` ` import Data.Char (chr, ord, toUpper)   -- A bit of self documentation help type Key = String type Msg = String   key :: Key key = "TSTING" msg :: Msg msg = "I'm not even mad... This is impressive!"   -- | Checks if character is valid for encoding isValid :: Char -> Bool isValid c = let cUp = toUpper c :: Char              in 'A' <= cUp && cUp <= 'Z'   -- | Given 'key' & 'msg' generate a list of [Maybe Int] indices -- to map 'msg' from 'key', skipping invalid characters toIdx :: Key -> Msg -> [Maybe Int] toIdx k m = map (flip mod keyN <\$>) \$ toIdx_ 0 m   where keyN = length k :: Int         toIdx_ :: Int -> Msg -> [Maybe Int]         toIdx_ _ "" = []         toIdx_ acc (c:cs)           | isValid c = Just acc : toIdx_ (acc + 1) cs           | otherwise = Nothing : toIdx_ acc cs   -- | Given 'key' & 'msg' generate a list of numbers representing -- the amount to shift 'msg' characters based on 'key' toShifts :: Key -> Msg -> [Int] toShifts k m = map toKey (toIdx k m)   where kUp = map toUpper k :: Key         toKey :: Maybe Int -> Int         toKey Nothing  = 0         toKey (Just x) = ord (kUp!!x) - ord 'A'   -- | Given 'by' & 'c', shift the Char 'c' by amount 'by'. 'by' can be both -- positive & negative as well as 0. shift :: Int -> Char -> Char shift by c   | isValid c && c >= 'a' = shift_ \$ ord 'a'   | isValid c && c >= 'A' = shift_ \$ ord 'A'   | otherwise = c   where cONorm    = ord (toUpper c) - ord 'A' :: Int         azN       = ord 'Z' - ord 'A' :: Int         shift_ :: Int -> Char         shift_ aO = chr \$ aO + mod (by + cONorm) azN   -- Encode & decode a message using the given key. vigenere, unVigenere :: Key -> Msg -> Msg vigenere   k m = zipWith shift (toShifts k m) m unVigenere k m = zipWith shift (map negate \$ toShifts k m) m   ``

Descubrí que la cosa más "molesta" al proceder de fondo, como ` Python, es poder hacer un seguimiento de las cosas, por ejemplo, al descubrir cómo convertir caracteres válidos en posiciones utilizables para Luego se mapa con el key . ¡Esa cosa me tomó medio día para averiguar! `

¿Cómo lo harías? ¿O hay alguna forma "estándar" de trabajar con este tipo de cosas? Me refiero particularmente a ` toIdx & amp; toIdx_ `, tuvo que usar ` toIdx_ ` Para acumular los índices con ` acc en esta lista de Maybe Int para poder asignar correctamente válido Char ` s con ` import Data.Char (chr, ord, toUpper) -- A bit of self documentation help type Key = String type Msg = String key :: Key key = "TSTING" msg :: Msg msg = "I'm not even mad... This is impressive!" -- | Checks if character is valid for encoding isValid :: Char -> Bool isValid c = let cUp = toUpper c :: Char in 'A' <= cUp && cUp <= 'Z' -- | Given 'key' & 'msg' generate a list of [Maybe Int] indices -- to map 'msg' from 'key', skipping invalid characters toIdx :: Key -> Msg -> [Maybe Int] toIdx k m = map (flip mod keyN <\$>) \$ toIdx_ 0 m where keyN = length k :: Int toIdx_ :: Int -> Msg -> [Maybe Int] toIdx_ _ "" = [] toIdx_ acc (c:cs) | isValid c = Just acc : toIdx_ (acc + 1) cs | otherwise = Nothing : toIdx_ acc cs -- | Given 'key' & 'msg' generate a list of numbers representing -- the amount to shift 'msg' characters based on 'key' toShifts :: Key -> Msg -> [Int] toShifts k m = map toKey (toIdx k m) where kUp = map toUpper k :: Key toKey :: Maybe Int -> Int toKey Nothing = 0 toKey (Just x) = ord (kUp!!x) - ord 'A' -- | Given 'by' & 'c', shift the Char 'c' by amount 'by'. 'by' can be both -- positive & negative as well as 0. shift :: Int -> Char -> Char shift by c | isValid c && c >= 'a' = shift_ \$ ord 'a' | isValid c && c >= 'A' = shift_ \$ ord 'A' | otherwise = c where cONorm = ord (toUpper c) - ord 'A' :: Int azN = ord 'Z' - ord 'A' :: Int shift_ :: Int -> Char shift_ aO = chr \$ aO + mod (by + cONorm) azN -- Encode & decode a message using the given key. vigenere, unVigenere :: Key -> Msg -> Msg vigenere k m = zipWith shift (toShifts k m) m unVigenere k m = zipWith shift (map negate \$ toShifts k m) m 0 . `

Por supuesto, no lo tendría de otra manera, pero tendría un algoritmo que toma cualquier entrada ` 998877665555443311 ` y crea un ` 9988777766555443312 ` con mayúsculas preservadas / minúsculas y no válido (fuera del alfabeto ASCII) ` import Data.Char (chr, ord, toUpper) -- A bit of self documentation help type Key = String type Msg = String key :: Key key = "TSTING" msg :: Msg msg = "I'm not even mad... This is impressive!" -- | Checks if character is valid for encoding isValid :: Char -> Bool isValid c = let cUp = toUpper c :: Char in 'A' <= cUp && cUp <= 'Z' -- | Given 'key' & 'msg' generate a list of [Maybe Int] indices -- to map 'msg' from 'key', skipping invalid characters toIdx :: Key -> Msg -> [Maybe Int] toIdx k m = map (flip mod keyN <\$>) \$ toIdx_ 0 m where keyN = length k :: Int toIdx_ :: Int -> Msg -> [Maybe Int] toIdx_ _ "" = [] toIdx_ acc (c:cs) | isValid c = Just acc : toIdx_ (acc + 1) cs | otherwise = Nothing : toIdx_ acc cs -- | Given 'key' & 'msg' generate a list of numbers representing -- the amount to shift 'msg' characters based on 'key' toShifts :: Key -> Msg -> [Int] toShifts k m = map toKey (toIdx k m) where kUp = map toUpper k :: Key toKey :: Maybe Int -> Int toKey Nothing = 0 toKey (Just x) = ord (kUp!!x) - ord 'A' -- | Given 'by' & 'c', shift the Char 'c' by amount 'by'. 'by' can be both -- positive & negative as well as 0. shift :: Int -> Char -> Char shift by c | isValid c && c >= 'a' = shift_ \$ ord 'a' | isValid c && c >= 'A' = shift_ \$ ord 'A' | otherwise = c where cONorm = ord (toUpper c) - ord 'A' :: Int azN = ord 'Z' - ord 'A' :: Int shift_ :: Int -> Char shift_ aO = chr \$ aO + mod (by + cONorm) azN -- Encode & decode a message using the given key. vigenere, unVigenere :: Key -> Msg -> Msg vigenere k m = zipWith shift (toShifts k m) m unVigenere k m = zipWith shift (map negate \$ toShifts k m) m 3 ` s.

Original en ingles

This is my implementation using the dreadful `!!`:

``import Data.Char (chr, ord, toUpper)   -- A bit of self documentation help type Key = String type Msg = String   key :: Key key = "TSTING" msg :: Msg msg = "I'm not even mad... This is impressive!"   -- | Checks if character is valid for encoding isValid :: Char -> Bool isValid c = let cUp = toUpper c :: Char              in 'A' <= cUp && cUp <= 'Z'   -- | Given 'key' & 'msg' generate a list of [Maybe Int] indices -- to map 'msg' from 'key', skipping invalid characters toIdx :: Key -> Msg -> [Maybe Int] toIdx k m = map (flip mod keyN <\$>) \$ toIdx_ 0 m   where keyN = length k :: Int         toIdx_ :: Int -> Msg -> [Maybe Int]         toIdx_ _ "" = []         toIdx_ acc (c:cs)           | isValid c = Just acc : toIdx_ (acc + 1) cs           | otherwise = Nothing : toIdx_ acc cs   -- | Given 'key' & 'msg' generate a list of numbers representing -- the amount to shift 'msg' characters based on 'key' toShifts :: Key -> Msg -> [Int] toShifts k m = map toKey (toIdx k m)   where kUp = map toUpper k :: Key         toKey :: Maybe Int -> Int         toKey Nothing  = 0         toKey (Just x) = ord (kUp!!x) - ord 'A'   -- | Given 'by' & 'c', shift the Char 'c' by amount 'by'. 'by' can be both -- positive & negative as well as 0. shift :: Int -> Char -> Char shift by c   | isValid c && c >= 'a' = shift_ \$ ord 'a'   | isValid c && c >= 'A' = shift_ \$ ord 'A'   | otherwise = c   where cONorm    = ord (toUpper c) - ord 'A' :: Int         azN       = ord 'Z' - ord 'A' :: Int         shift_ :: Int -> Char         shift_ aO = chr \$ aO + mod (by + cONorm) azN   -- Encode & decode a message using the given key. vigenere, unVigenere :: Key -> Msg -> Msg vigenere   k m = zipWith shift (toShifts k m) m unVigenere k m = zipWith shift (map negate \$ toShifts k m) m ``

I found that the most "annoying" thing when coming from background such as `Python` is to be able to keep track of things, for example when figuring out how to convert valid characters into usable positions to be then mapped with the `key`. That thing took me half a day to figure out!

How would you do it? Or is there some "standard" way of working with these sort of things? I'm referring particularly to `toIdx` & `toIdx_`, had to use `toIdx_` to accumulate the indices with `acc` in this list of `Maybe Int` in order to correctly map valid `Char`s with `key`.

Of course, I wouldn't have it any other way but have an algorithm which takes any `String` input and creates an encoded `String` with preserved upper/lower-case and non-valid (out of ASCII alphabet) `Char`s.

## Lista de respuestas

2

La mejor respuesta

estás bastante cerca. El problema está dentro de ` static4 `. ` static5 ` lo hará siempre consumir los primeros elementos de ambas listas. Es por eso que necesita ` static6 ` para comenzar.

Sin embargo, ¿qué pasa si nos deshaciéramos de ` static7 ` por un momento y uso de un patrón que coinciden en ` static8 `?

` ` static9  ``

¿Dónde ` public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 0 es una función apropiada (izquierda como ejercicio). En realidad, eso es todo. Eso es todo lo que es necesario para public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 1 `, aparte de ` public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 2 `. Bueno, estoy mintiendo: la llave se agotará en algún momento. Es por eso que usa ` public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 3 : `

` ` public sealed class DriveInfo {     public DriveInfo(string name, string serialNumber, ulong freeSpace)     {         this.Name = name;         this.SerialNumber = serialNumber;         this.FreeSpace = freeSpace;     }      public string Name { get; }      public string SerialNumber { get; }      public ulong FreeSpace { get; } } 4 ` `

` 99887766555443325 ` Convierte una lista regular en uno infinito, por ejemplo. ` public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 6 ` (Ejercicio: Intente implementar el ciclo). Por lo tanto, convierte la llave en uno infinito.

Puede implementar ` public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 7 De la misma manera, solo que necesita public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 8 . Un par de funciones, a saber, public sealed class DriveInfo { public DriveInfo(string name, string serialNumber, ulong freeSpace) { this.Name = name; this.SerialNumber = serialNumber; this.FreeSpace = freeSpace; } public string Name { get; } public string SerialNumber { get; } public ulong FreeSpace { get; } } 9 y return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 0 será útil para eso (dejado como ejercicio). `

Heck, incluso podemos implementar ambas funciones de la misma manera y usar ` return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 11 ` en lugar de coincidir con patrones para caracteres no compatibles:

` ` return new DriveInfo(     (string)volume["Name"],     (string)drive["SerialNumber"],     (ulong)volume["FreeSpace"]); 2 ` `

Ahora, ` return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 33 ` y ` return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 4 son extremadamente simples: `

` ` return new DriveInfo(     (string)volume["Name"],     (string)drive["SerialNumber"],     (ulong)volume["FreeSpace"]); 5  ``

y estamos terminados. Por cierto, puede usar ` return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 6 para deshacerse de return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 7 . Pero eso, de nuevo, se deja como un ejercicio. `

# Otros comentarios

Las anotaciones de tipo en su ` return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 8 son necesarias. Me desharía de ellos, ya que pueden salir de la sincronización con su función de nivel superior (pero al menos obtendrás un error de tipo). `

` return new DriveInfo( (string)volume["Name"], (string)drive["SerialNumber"], (ulong)volume["FreeSpace"]); 9 se puede escribir como letter0 , si está preparado para un desafío. `

Su complejidad general se deriva de ` letter1 `. Es una gran función, pero solo si se usa para el trabajo correcto. No todo es un clavo, solo porque tienes un martillo a la mano. En lugar de tratar de obtener todo lo demás en su lugar para usar una función específica, pregúntese qué quiere hacer, y si hay una manera fácil. Desafortunadamente, no hay ` letter22 `, como función en la biblioteca estándar (a menos que cuente los acumuladores de estado, como ` letter3 ). `

You're rather close. The issue lies within `zipWith`. `zipWith` will always consume the first elements of both lists. That's why you need `(toShifts k m)` to begin with.

However, what if we get rid of `zipWith` for a moment and use pattern matching in `vigenere`?

``vigenere' :: Key -> Msg -> Msg vigenere' ks     (' ':ms) = ' '        : vigenere ks ms vigenere' (k:ks) (m  :ms) = shiftC k m : vigenere ks ms vigenere' _      []       = [] ``

where `shiftC :: Char -> Char -> Char` is an appropriate function (left as an exercise). Actually, that's it. That's all that is necessary for `vigenere`, apart from `shiftC`. Well, I'm lying: the key will run out at some point. That's why you use `cycle :: [a] -> [a]`:

``vigenere :: Key -> Msg -> Msg vigenere ks ms = vigenere' (cycle ks) ms ``

`cycle` turns a regular list in an infinite one, e.g. `cycle [1,2,3] == [1,2,3,1,2,3,1,2,3,xe2x80xa6]` (exercise: try to implement cycle). Therefore, it turns your key into an infinite one.

You can implement `unVigenere'` the same way, only that you need `unShiftC :: Char -> Char -> Char`. A pair of functions, namely `toInt :: Char -> Int` and `fromInt :: Int -> Char` will come in handy for that (left as an exercise).

Heck, we can even implement both functions the same way and use `isValid` instead of pattern matching for unsupported characters:

``cryptZip' :: (Char -> Char -> Char) -> Key -> Msg -> Msg cryptZip' _ _      []       = [] cryptZip' f (k:ks) (m  :ms) =   | isValid m = f k m : cryptZip' f ks     ms   | otherwise = m     : cryptZip' f (k:ks) ms  -- | Combines the key and the message with the given function. -- Invalid characters are left as-is. The function shall return -- only valid characters. cryptZip :: (Char -> Char -> Char) -> Key -> Msg -> Msg cryptZip f ks ms = cryptZip' f (cycle ks) ms ``

Now, `vigenere` and `unVigenere` are extremely simple:

``vigenere   = cryptZip shiftC unVigenere = cryptZip unShiftC ``

And we're done. By the way, you can use `(Char -> Char -> Maybe Char)` to get rid of `isValid`. But that, again, is left as an exercise.

# Further remarks

The type annotations in your `where` bindings are not necessary. I would get rid of them, since they may get out of sync with your top-level function (but you will at least get a type-error).

`cryptZip` can be written as `(a -> a -> Maybe a) -> [a] -> [a] -> [a]`, if you're up for a challenge.

Your overall complexity stemmed from `zipWith`. It's a great function, but only if it's used for the right job. Not everything is a nail, just because you have a hammer at hand. Instead of trying to get everything else in place to use a specific function, ask yourself what you want to do, and whether there's an easy way. Unfortunately, there is no `cryptZip`-like function in the standard library (unless you count stateful accumulators such as `mapAccumL`).

3

Para deshacerse de ` letter4 ` aquí, puede usarlo anteriormente y antes hasta que nunca genere un ` 99887766555443345 `.

` ` letter6 ` `

Por supuesto, casi no hay una necesidad de separar todos estos pasos.

` ` letter7 ` `

To get rid of `!!` here, you can use it earlier and earlier until you never even generate an `Int`.

``-- | Given 'key' & 'msg' generate a list of [Maybe Int] indices -- to map 'msg' from 'key', skipping invalid characters toIdx :: Key -> Msg -> [Maybe Char] toIdx k m = toIdx_ (cycle \$ map toUpper k) m   where toIdx_ :: Key -> Msg -> [Maybe Char]         toIdx_ _ "" = []         toIdx_ key@(k:ey) (c:cs)           | isValid c = Just k : toIdx_ ey cs           | otherwise = Nothing : toIdx_ key cs   -- | Given 'key' & 'msg' generate a list of numbers representing -- the amount to shift 'msg' characters based on 'key' toShifts :: Key -> Msg -> [Int] toShifts k m = map toKey (toIdx k m)   where toKey :: Maybe Char -> Int         toKey Nothing  = 0         toKey (Just x) = ord x - ord 'A' ``

Of course, there is hardly a need to separate all these steps.

``base :: Char -> Maybe Char base c   | 'a' <= c && c <= 'z' = Just 'a'   | 'A' <= c && c <= 'Z' = Just 'A'   | otherwise = Nothing  vigenere, unVigenere :: String -> String -> String [vigenere, unVigenere] = (`map` [(+), (-)]) \$ \direction k ->   (.) snd \$ (`mapAccumL` cycle k) \$ \key@(k:ey) c -> case base c of     Nothing -> (key, c)     Just a -> (,) ey \$ chr \$ ord a +       mod ((ord c - ord a) `direction` (ord (toUpper k) - ord 'A')) (ord 'Z' - ord 'A') ``

9  Vigenere Decyphir en C ++  ( Vigenere decypher in c )
He escrito un declador de Vigenere y me gustaría tener algunas opiniones antes de intentar refactorizarlo en más estilo OOP con clases y demás. gameKey()8 ...

4  Programa CS50 Vigenere  ( Cs50 vigenere program )
Escribí un programa para un problema en el curso CS50 de Harvard, Vigenere (semana 2). Aquí hay una Descripción de lo que el programa necesita hacer : D...

5  Vigenère cifrado en Ruby  ( Vigen%c3%a8re cipher in ruby )
lo que estoy tratando de hacer: implementar el cifrado de Vigenère en Ruby. Ya tengo una versión de trabajo, pero quiero asegurarme de que sea eficiente y b...

2  Cryptor c vigenere  ( C vigenere encryptor )
Este código está probado y funciona correctamente, estoy buscando algunos comentarios sobre las siguientes funciones. Bastante recto y simple, pero si se dese...

2  Cifrado y descifrado utilizando cambios alfabéticos  ( Encryption and decryption using alphabetic shifts )
Mi primera pieza de código cifra texto moviendo cada letra en la cadena 5 letras en el alfabeto. encrypt = input('Enter text to encrypt : ') encrypt = encr...

Estoy en el proceso de aprendizaje de Python y programó este ejercicio para descifrar el Plaza de Vigenere Cypher como práctica. Por favor comente las mej...

4  Caja de herramientas clásica de criptografía: CEESAR, VIGENERE y CIPHERS DE ADFGVX  ( Classic cryptography toolbox caesar vigenere and adfgvx ciphers )
Escribí un programa en Haskell que tiene como objetivo dejar que el usuario cifre, descifrar y crack / cryptanalyse pre-electromecánica era cifras en Haskell....

2  Vigenère cifrado como filtro IO  ( Vigen%c3%a8re cipher as an io filter )
Implementé una clase de codificación y decodificación de Vigenere. Idealmente, el programa debería poder tomar cualquier archivo de abritry, lea en una matriz...

5  Cracking Vigenere y Caesar Texto cifrado en Python  ( Cracking vigenere and caesar ciphered text in python )
He escrito un par de programas en Python que se puede usar para cifrar, descifrar y crack Caesar y Vigenere cifró texto. Soy bastante nuevo en Python y escrib...

6  Cipher de Vigenere - ¡Hacer código más conciso?  ( Vigenere cipher make code more concise )
Soy un principiante en la programación en general, comenzó hace aproximadamente un mes y, por ejemplo, todavía estoy tratando de hacerlo totalmente tallado de...