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
vote

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 Chars 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) Chars.

           
         
         

Lista de respuestas

2
 
vote
vote
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
 
vote

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') 
 
 
 
 

Relacionados problema

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...

3  Vigenere cuadrado cypher descifrado  ( Vigenere square cypher decryption ) 
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...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos