I have the code which create Data.Map:
import qualified Data.Map as Map
shift_string :: [Char] -> Int -> [Char]
shift_string s num = (drop num s) ++ (take num s)
ascii :: [Char]
ascii = ['a' .. 'z']
shifted_ascii :: Int -> [Char]
shifted_ascii n = shift_string ascii n
trans_table :: Int -> Map.Map Char Char
trans_table n = Map.fromList(zip ascii $ shifted_ascii n)
The 'trans_table' function returns the Map where one Char map to another map.
I can create the function to get one Char and return another one based on this Map:
translate_char :: Char -> Int -> Maybe Char
translate_char c n = Map.lookup c $ trans_table n
Now I want to 'translate' each symbol in the map. Something like this:
encode message key = map translate_char message
This code doesn't work as translate_function must have only one parameter. I need something like global variable to store the Map in it and lookup values from it in my map-function. But I don't know how to rewrite my code.
p.s. I guess I can just add 'key' to each char in my string but I am searching common solution.
I don't know whats key in your encode function but you can use something like
translate_char :: Int -> Char -> Maybe Char
translate_char n c = Map.lookup c $ trans_table n
encode :: Int -> String -> String
encode n s = catMaybes $ map (translate_char n) s
here n determines the number of characters you are rotating. I switched the order of parameters to avoid using flip.
Related
I want to write a program that encrypts a text using XOR-cipher. That's what I have for now:
-- XORres 2 strings
stringXor :: String -> String -> String
stringXor s t = map chr $ zipWith xor (map ord s) (map ord t)
-- encryption and decryption
encDec :: String -> String -> String
encDec text key = stringXor (take (length text) (cycle key)) text
But the output of encDec "this is a test" "k" is
"\US\ETX\STX\CANK\STX\CANK\nK\US\SO\CAN\US"
while I was expecting something like 1f0302184b02184b0a4b1f0e181f4b.
What could be the problem here? I've searched similar questions but that wasn't very helpful.
stringXor s t = map chr $ zipWith xor (map ord s) (map ord t)
You map chr to the results of XOR. This results in characters for each ASCII value. To show the results as hexadecimal, you will need to find a different function to replace chr or write one yourself.
Side note: In cryptography, it is common to use Base64 notation to encode binary data instead of hex because it uses less characters (and therefore less memory or network bandwidth) to represent the same binary sequence.
You are getting 1f0302184b02184b0a4b1f0e181f4b:
> "\x1f\x03\x02\x18\x4b\x02\x18\x4b\x0a\x4b\x1f\x0e\x18\x1f\x4b"
"\US\ETX\STX\CANK\STX\CANK\nK\US\SO\CAN\USK"
...which is exactly the output you saw from encDec (up to what appears to be a simple copy-paste error in your expected output).
The problem is with the chr :: Int -> Char. This function converts an Int to the corresponding character, but not a hexadecimal representation of that number.
You can for example define a utility function with intToDigit :: Int -> Char:
import Data.Char(intToDigit)
toHex2 :: Int -> String
toHex2 h = map intToDigit [d, m]
where (d, m) = divMod h 16
Then we can implement the function as:
stringXor :: String -> String -> String
stringXor s t = concatMap toHex2 (zipWith xor (map ord s) (map ord t))
Or as #DanielWagner says:
import Data.Function(on)
stringXor :: String -> String -> String
stringXor s t = concatMap toHex2 (zipWith (xor `on` ord) s t)
This then gives us:
Prelude Data.Char Data.Bits> encDec "this is a test" "k"
"1f0302184b02184b0a4b1f0e181f"
Note that you do not need to use length here, in fact it is safer without length, you can just use cycle:
encDec :: String -> String -> String
encDec text key = stringXor (cycle key) text
I am making a program that replaces stuff using the Esperanto X-System to Esperanto, so I need it to transform "cx" to "ĉ", "sx" to "ŝ", "gx" to "g", "jx" to "ĵ", and "ux" to "ŭ", and the same for uppercase letters.
Currently it converts "a" to "b", and "c" to "d". The method I am currently using will only work for replacing single character, not multiple characters. So how do I replace multiple characters (like "cx") instead of a single one (like "a")?
replaceChar :: Char -> Char
replaceChar char = case char of
'a' -> 'b'
'c' -> 'd'
_ -> char
xSistemo :: String -> String
xSistemo = map replaceChar
So currently "cats" will transform to "dbts".
As #AJFarmar pointed out, you are probably implementing Esperanto's X-system [wiki]. Here all items that are translated are digraphs that end with x, the x is not used in esperato itself. We can for example use explicit recursion for this:
xSistemo :: String -> String
xSistemo (x:'x':xs) = replaceChar x : xSistemo xs
xSistemo (x:xs) = x : xSistemo xs
xSistemo [] = []
where we have a function replaceChar :: Char -> Char, like:
replaceChar :: Char -> Char
replaceChar 's' = 'ŝ'
-- ...
This then yields:
Prelude> xSistemo "sxi"
"\349i"
Prelude> putStrLn (xSistemo "sxi")
ŝi
A generic method:
The problem looks similar to question 48571481.
So you could try to leverage the power of Haskell regular expressions.
Borrowing from question 48571481, you can use foldl to loop thru the various partial substitutions.
This code seems to work:
-- for stackoverflow question 57548358
-- about Esperanto diacritical characters
import qualified Text.Regex as R
esperantize :: [(String,String)] -> String -> String
esperantize substList st =
let substRegex = R.subRegex
replaceAllIn = foldl (\acc (k, v) -> substRegex (R.mkRegex k) acc v)
in
replaceAllIn st substList
esperSubstList1 = [("cx","ĉ"), ("sx","ŝ"), ("jx","ĵ"), ("ux","ŭ")]
esperantize1 :: String -> String
esperantize1 = esperantize esperSubstList1 -- just bind first argument
main = do
let sta = "abcxrsxdfuxoojxii"
putStrLn $ "st.a = " ++ sta
let ste = esperantize1 sta
putStrLn $ "st.e = " ++ ste
Program output:
st.a = abcxrsxdfuxoojxii
st.e = abĉrŝdfŭooĵii
We can shorten the code, and also optimize it a little bit by keeping the Regex objects around, like this:
import qualified Text.Regex as R
esperSubstList1_raw = [("cx","ĉ"), ("sx","ŝ"), ("jx","ĵ"), ("ux","ŭ")]
-- try to "compile" the substitution list into regex things as far as possible:
esperSubstList1 = map (\(sa, se) -> (R.mkRegex sa, se)) esperSubstList1_raw
-- use 'flip' as we want the input string to be the rightmost argument for
-- currying purposes:
applySubstitutionList :: [(R.Regex,String)] -> String -> String
applySubstitutionList = flip $ foldl (\acc (re, v) -> R.subRegex re acc v)
esperantize1 :: String -> String
esperantize1 = applySubstitutionList esperSubstList1 -- just bind first argument
main = do
let sta = "abcxrsxdfuxoojxiicxtt"
putStrLn $ "st.a = " ++ sta
let ste = esperantize1 sta
putStrLn $ "st.e = " ++ ste
I write a code to convert a string to be a list of Intger:
convert::String -> Maybe [Int]
convert (c:cs)
isDigit c = Just(c: (convert cs):[])
otherwise = Nothing
and it shows an error...
test.hs:15:26: error:
parse error on input ‘=’
Perhaps you need a 'let' in a 'do' block?
e.g. 'let x = 5' instead of 'x = 5'
Why is that so...
While there are other compile errors in your code, the reason you're getting the error message about the parse error is because you are not including the pipe character used in guards.
convert (c:cs)
| isDigit c = Just(c: (convert cs):[])
| otherwise = Nothing
There were several errors in your code. You need to try to convert each character, which gives a Maybe Int. Then you loop on the string using mapM inside the Maybe monad :
import Data.Char
convertChar :: Char -> Maybe Int
convertChar c = if isDigit c then Just $ ord c else Nothing
convert :: String -> Maybe [Int]
convert = mapM convertChar
Another way of looking at V. Semeria's answer is to use sequence (from the Traversable type class) with map:
import Data.Char -- for ord
convertChar :: Char -> Maybe Int
convertChar c = if isDigit c then Just $ ord c - 48 else Nothing
convert :: String -> Maybe [Int]
-- Start with String -> [Maybe Int], then convert
-- the [Maybe Int] to Maybe [Int]
convert = sequence . map convertChar
This is a common pattern, so Traversable also provides traverse, which in this case is equivalent to sequence . map.
convert :: Traversable t => t Char -> Maybe (t Int)
convert = traverse converChar
Some examples (most of the instances of Traversable available by default aren't very interesting, but various tree types can have instances).
> convert "123"
Just [1,2,3]
> convert (Just '1')
Just (Just 1)
> convert (True, '2')
Just (True, 2)
> convert (Right '1')
Just (Right 1)
I am a beginner with haskell and i wonder how i can count the characters in a file in haskell. From this book i wrote the count of any character in any string, but i wonder if i can do the same function with files. The code is similar like following;
count :: Char -> String -> Int
count x xs = length [x'|x'<-xs, x==x']
Any help would be very great. Thanks!
Edit: I am very new to haskell so this may be a very stupid question :)
By reusing your count function. You can map it over a readFile with returns you a file's content as a string:
count :: Eq a => a -> [a] -> Int
count x xs = length [x' | x' <- xs, x==x']
cntFile :: Char -> FilePath -> IO Int
cntFile c f = count c <$> readFile f
main :: IO ()
main = do
cnt <- cntFile 'c' "test.hs"
print cnt
The operator <$> is just the infix notation for fmap, which does to IO and any other Functor what map does to lists.
The longer alternative for beginner would probably be:
cntFile' :: Char -> FilePath -> IO Int
cntFile' c f = do
content <- readFile f
let cnt = count c content
return cnt
Modification may well be just an addition of 3 to the Char ascii value.
I have gone through several books and can't find a solution off the shelf.
(Returning the Char list can be to a different list variable.)
import Data.Char
shiftAscii :: String -> String
shiftAscii xs = map (chr.(+3).ord) xs
would do what you ask.
It works because map edits each character in the string using the supplied function.
ord converts the Char to its Int value
(+3) shifts the (ascii) by 3
chr converts back to a Char,
so chr.(+3).ord is those three strung together with function composition .
To be more flexible, you could write
shiftAsciiBy :: Int -> String -> String
shiftAsciiBy n = map (chr.(+ n).ord)
notice that shifting the ascii doesn't respect alphabet boundaries, so if you were needing this to do rot13 encoding or similar simple shift, you'd be better off with a hand-rolled shift function that only edits the alphabet
addAscii :: Int -> Char -> Char
addAscii n c | isUpper c = chr $ ((ord c - ord 'A' + n) `mod` 26) + ord 'A'
| isLower c = chr $ ((ord c - ord 'a' + n) `mod` 26) + ord 'a'
| otherwise = c
for example
['A'..'z']
"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz"
and we shift just the alphabet ascii:
map (addAscii 5) ['A'..'z']
"FGHIJKLMNOPQRSTUVWXYZABCDE[\\]^_`fghijklmnopqrstuvwxyzabcde"