Haskell get character array from string? - haskell

Is it possible if given a string I could get each character composing that string?

In Haskell, strings are just (linked) lists of characters; you can find the line
type String = [Char]
somewhere in the source of every Haskell implementation. That makes tasks such as finding the first occurence of a certain character (elemIndex 'a' mystring) or calculating the frequency of each character (map (head &&& length) . group . sort) trivial.
Because of this, you can use the usual syntax for lists with strings, too. Actually, "foo" is just sugar for ['f','o','o'], which in turn is just sugar for 'f' : 'o' : 'o' : []. You can pattern match, map and fold on them as you like. For instance, if you want to get the element at position n of mystring, you could use mystring !! n, provided that 0 <= n < length mystring.

Well, the question does say he wants an array:
import Data.Array
stringToArray :: String -> Array
stringToArray s = listArray (0, length s - 1) s

The string type is just an alias for [Char] so you don't need to do anything.
Prelude> tail "Hello"
"ello"
Prelude> ['H', 'e', 'l', 'l', 'o']
"Hello"
Prelude> "Hello" !! 4
'o'

Related

Convert a string of strings of numbers into words like the old phones

Basically i have a list of lists made of strings that are only numbers like ["22","333","2"] and i want to transform it into the string "bea". Think of this like the old phones if u press 2 twice you get a b, if you press 4 once you get a j, like that. Only can use prelude functions
I tried it like this
numbers ws [] = ws
numbers ws (x:xs) = if head "2ABC" == head x
then ws ++ "2ABC" !! length x ++ numbers ws xs
else if head "3DEF" == head x
then ws ++ "3DEF" !! length x ++ numbers ws xs
....
But this gives me errors so can you help me guys?
A classic mistake is to implement too much logic into a single function. Instead of trying to do the entire decoding into the numbers function, it might be better to split up the tasks in reusable components, such that these are easy to understand, debug, and reuse.
Here as first function, we can map the digit to a String containing the characters, like:
digtoseq :: Char -> String
digtoseq '2' = "abc"
digtoseq '3' = "def"
digtoseq '4' = "ghi"
digtoseq '5' = "jkl"
digtoseq '6' = "mno"
digtoseq '7' = "pqrs"
digtoseq '8' = "tuv"
digtoseq '9' = "wxyz"
digtoseq '0' = " "
We here can add extra characters, for example the '.' to specify the sequence behind each phone key.
Now we can implement a function that makes use of digtoseq, for example digstrtoseq:
digstrtoseq :: String -> Char
digstrtoseq (x:xs) = digtoseq x !! length xs
here we thus take the length of the string, as well as the first character, and we move through the string of digtoseq x, to obtain the n-1-th element (with n the length of the input string). So for "22", we get:
Prelude> digstrtoseq "22"
'b'
Prelude> digstrtoseq "33"
'e'
Prelude> digstrtoseq "2"
'a'
so now it is only a matter of mapping this function over the string of input:
numbers :: [String] -> String
numbers = map digstrtoseq
and then we obtain:
Prelude> numbers ["22", "33", "2"]
"bea"
Note that here we made some assumptions, some of these can be improved by rewriting the functions, other are better solved by changing the type of the input:
we assume that no string has a length larger than the length of the sequence behind it, so "2222" will not occur;
we assume that the character in the string element is repeated over the entire string, so "231" will not occur;
we assume that the strings contain only valid digits, so no "~";
we assume that each string contains at least one character, so no "".
A large amount of the assumptions originate from the fact that we here use [String] as input type, which gives a lot of freedom. Yes it is possible to raise errors, or to return Nothing in that case, but it might be better to define a type like:
data Key = Two | Three | Four | Five | Six | Seven | Eight | Nine | Zero
and then as input take [(Key, Int)], since then assumptions (2) and (3) are simply "guaranteed" through the input type.
Willem beat me to it but here's what I came up with. You might want to complete the keyboard though
numToChar :: Char -> Int -> Char
numToChar '2' i = "abc" !! (i - 1)
numToChar '3' i = "def" !! (i - 1)
numToChar '4' i = "ghi" !! (i - 1)
numToChar '5' i = "jkl" !! (i - 1)
numToChar '6' i = "mno" !! (i - 1)
numToChar '7' i = "pqrs" !! (i - 1)
numToChar '8' i = "tuv" !! (i - 1)
numToChar '9' i = "wxyz" !! (i - 1)
numbers n = [numToChar (head x) (length x) | x <- n]
There are indeed more direct methods of translation or mapping. The ASCII character set can be exploited for such. This function is robust. The only limitation I can see is the length of the ASCII character set. The assumption is what we cannot type what will not be used. The first digit matters only. Anything can be used after it.
td=\(s:sx)->[toEnum (47+d+e+(length sx))::Char|d<-[fromEnum s],e<-[2*(mod d 50) + if d > 55 then 1 else 0]]
Use td any way you want.
concat $ map td ["222","33","9999","4"]
"cezg"

How to find occurrences of char in an input string in Haskell

I am trying to write a function that will take a String and a Char and output the indexes where the char occurs in the string.
stringCount str ch =
Input : "haskell is hard" `h`
Output:[0,11]
Input : "haskell is hard" `a`
Output:[1,12]
Please help me I'm struggling to understand Haskell.
There are many ways to do this, but since you mention you're a Haskell beginner, a list comprehension may be easiest to understand (I'm assuming this is homework, so you have to implement it yourself, not use elemIndices):
stringCount str ch = [ y | (x, y) <- zip str [0..], x == ch ]
stringCount "haskell is hard" 'a'
-- [1,12]
stringCount "haskell is hard" 'h'
-- [0,11]
Here we zip, the string str with the infinite list starting from 0, producing the tuples ('h', 0), ('a', 1), ('s', 2), etc. We then only select the tuples where the character (bound to x) equals the argument ch and return the index (bound to y) for each of them.
If you wanted to keep your current argument order but use elementIndices you can use the following:
stringCount' = flip elemIndices
stringCount' "haskell is hard" 'h'
-- [0,11]
Here is a simpler but less sophisticated solution that the one post by karakfa:
stringCount :: String -> Char -> Integer -> [Integer]
stringCount [] c _ = []
stringCount (x:xs) c pos | x == c = pos:(stringCount xs c (pos+1))
| otherwise = stringCount xs c (pos+1)
The idea is that you go through the string char by char using recursion and then compare the actual caracter (head at the moment) with the char passed as argument. To keep track of the position I am using a counter called pos, and increment it for each recursion call.
you can use elemIndex to walk through the list, or simply write your own
indexOf x = map fst . filter (\(_,s) -> s==x) . zip [0..]
indexOf 'a' "haskell is hard"
[1,12]
or with findIndices
import Data.List(findIndices)
findIndices (\x -> x=='a') "haskell is hard"
[1,12]

ordering in modified/deranged alphabet in haskell

I have a string containing the whole alphabet and then I want a different string from it by choosing a keyword and adding it to an exact position and then ordering the rest of the characters of the alphabet.
For example with a keyword gamer and a starting position given by 'l'
the result should be ->
"abcdefghijklmnopqrstuvwxyz"
"opqstuvwxyzgamerbcdfhijkln"
I have seen this is used in the modified caesar cipher but I haven't found a solution for this part of the algorithm.
=================================================================================
Now what I have been able to write is the following :
import List
derange key char = (fst(divided char)\\key) ++ key ++ (snd(divided char)\\key)
abc = ['a'..'z']
divided char = break (char==) abc
and the given output for an example is ->
Main> derange "ploting" 'g'
"abcdefplotinghjkmqrsuvwxyz"
So from the position of 'g' I wrote the keyword "ploting" and the rest of the alphabet is added without duplicate characters.
But still there is a little problem because the rest of the alphabet should continue from the first possible character after the keyword. So instead the right output should be
"uvwxyzplotingabcdefhjkmqrs".
Change
So I worked on the code, to obtain the result I wanted. At first I tried it with concrete given values for the keyword and the character.
What I have come to is the following :
`concat[drop (length['a'..'z'] - index('a','z') 'l') "gamerbcdfhijklnopqstuvwxyz",take (length['a'..'z'] - index('a','z') 'l') "gamerbcdfhijklnopqstuvwxyz"]`
And the result is
"opqstuvwxyzgamerbcdfhijkln"
,just what I wanted.
But then I tried to generalize the code so I wrote this.
import List
derange key char = concat [drop (length abc - index('a','z') char) def,
take (length abc - index('a','z') char) def]
where abc = ['a'..'z']
def = key ++ (key\\abc)
But now the output is
Main> derange "gamer" 'l'
"gamer"
and I don't know why. Any suggestions?
Found the problem, just had to swap key and abc within the list difference. Now I get the desired output.
Your question is a little unclear on what the expected behaviour of derange is supposed to be. This seems to match your description
import Data.List ((\\))
-- returns the 0-based index of c in ['a' .. 'z']
index :: Char -> Int
index c = fromEnum c - 97
derange :: Char -> String -> String
derange key str =
drop (i - length str) (tail bs ++ str ++ as)
where (as, bs) = splitAt i $ ['a' .. 'z'] \\ str
i = index key
sample output,
λ. derange 'g' "ploting"
"jkmqrsuvwxyzplotingabcdef"

Haskell couldn't match expected type with actual type

So I'm trying to use frequency analysis to decipher a code.
import Data.Char
import Data.List
import Data.Function
import qualified Data.Map as DMap
codedMsg = "V'Z GELVAT GB GRNPU GUR PNIRZRA GB CYNL FPENOOYR. VG'F HCUVYY JBEX. GUR BAYL JBEQ GURL XABJ VF 'HAU', NAQ GURL QBA'G XABJ UBJ GB FCRYY VG."
mostFreqLtr = ["E", "T", "A", "O", "I", "N", "S", "H", "R", "D", "L", "C", "U", "M", "W", "F", "G", "Y", "P", "B", "V", "K", "X", "J", "Q", "Z"]
--weed out non alphabetical characters from the list
alphaSort lst
| null lst = []
| isAlpha (head lst) = (head lst) : alphaSort (tail lst)
| otherwise = alphaSort (tail lst)
--sort the list by characters
msgSort [] = []
msgSort lst = sortBy (compare `on` ord) lst
--group each character into it's own list
grp [] = []
grp lst = group lst
--sort the list into most frequent character first
lSort [] = []
lSort lst = reverse (sortBy (compare `on` length) lst)
--change the list into one instance of each character
oneChar [] = []
oneChar lst = take 1 (head lst) : oneChar (tail lst)
--Pairing letters and creating a map of tuples containing frequency related characters
msg = zip (oneChar $ lSort $ grp $ msgSort $ alphaSort $ map toUpper $ codedMsg) mostFreqLtr
msg2 = DMap.fromList msg
--replace coded list with analyzed list
replaceChars lst
| null lst = []
| isAlpha (head lst) = DMap.lookup (head lst) msg2 : replaceChars (tail lst)
| otherwise = (head lst) : replaceChars (tail lst)
result = replaceChars codedMsg
I keep getting this error:
Couldn't match expected type `Char' with actual type `[Char]'
Expected type: DMap.Map Char a0
Actual type: DMap.Map [Char] [Char]
In the second argument of `DMap.lookup', namely `msg2'
In the first argument of `(:)', namely
`DMap.lookup (head lst) msg2'
Write type signatures on all your top-level functions. Then you will find that
oneChar :: [[a]] -> [[a]]
while, from the use, I surmise you intended
oneChar :: [[Char]] -> [Char]
Instead of take 1, you should have used head, or you should've concated the result to obtain a list of Chars.
As is, the map msg2 that you construct has [Char] as keys, but you try to use it as if it had Chars as keys.
hmm - I sat down and thought a bit about your code
please use type signatures it helps a lot thinking about your code - and the compiler can optimize too
give names that are a bit more meaningful
mostFreqLtr -> freqTable_EN (it makes clear you're deciphering English text)
alphaSort -> filterAlpha (it is misleading as you're filtering non letter elements and not sorting anything
msgSort -> sort (as it is the same i think)
use pattern matching rather than head and tail
i.e. lst ... head lst ... tail lst -> lst#(c:cs)
lst then is referrable as itself and c is its head and cs its tails (single elements are often referred as single letters, lists as their quasiplurals with appended s
Code:
import Prelude hiding (lookup)
import Data.Char ( isAlpha
, toUpper)
import Data.List ( group
, sort
, sortBy)
import Data.Function (on)
import Data.Map ( fromList
, lookup
, Map)
import Data.Maybe (mapMaybe)
only import necessary bits of code
codedMsg :: String
codedMsg = "V'Z GELVAT GB GRNPU GUR PNIRZRA GB CYNL FPENOOYR." ++
"VG'F HCUVYY JBEX. GUR BAYL JBEQ GURL XABJ VF 'HAU'," ++
"NAQ GURL QBA'G XABJ UBJ GB FCRYY VG."
freqTable_EN :: [Char]
freqTable_EN = ['E', 'T', 'A', 'O', 'I', 'N', 'S', 'H', 'R'] ++
['D', 'L', 'C', 'U', 'M', 'W', 'F', 'G', 'Y'] ++
['P', 'B', 'V', 'K', 'X', 'J', 'Q', 'Z']
do not use too long lines - it makes code less readable, the last name freqTable_EN is quite unusual but in this case I feel free to deviate from standard as it is better readable. I also use [Char] instead of String (which is equivalent) to make it more clear that it is a table of letters.
-- weed out non alphabetical characters from the list
filterAlpha :: String -> String
filterAlpha = filter isAlpha
-- sort a list by length
sortByLength :: [[a]] -> [[a]]
sortByLength = sortBy (compare `on` length)
-- sort the list into most frequent character first
sortByFreq :: [[a]] -> [[a]]
sortByFreq = reverse . sortByLength
such comments are not necessary with good function names
-- change the list into one instance of each character
reduceGroups :: [[a]] -> [a]
reduceGroups lst = map head lst
you can also leave the lst thing the compiler is smart enough to get all information from the type signature so the last line could also be reduceGroups = map head
-- Pairing coded message with frequency table
pairs :: [(Char, Char)]
pairs = nonAlphaPairs ++ zip freqSortedMsg freqTable_EN
where cleanedMsg = (filterAlpha . map toUpper) codedMsg
freqSortedMsg = (reduceGroups . sortByFreq . group . sort) cleanedMsg
nonAlphaPairs = map (\x ->(x,x)) $ filter (not . isAlpha) codedMsg
(\x -> (x,x)) is a lambda expression it simply transforms a single character in a pair as they're deciphered by themselves
-- and creating a map for decryption
cipher :: Map Char Char
cipher = fromList pairs
-- replace encoded text by our cipher
decipher :: String -> String
decipher = mapMaybe (uplook cipher)
where uplook = flip lookup
result :: String
result = decipher codedMsg
main :: IO ()
main = print result
the last line makes your result printed - as we want to read the message ;-)
Feel free to ask if something is unclear.
PS.: I really like your coded message - though not even one letter is found by frequency analysis. I just guessed your ciphering algorithm. (g? for vim users),
I think you have to use a longer text.

How to convert a list of (Char,Int) to a string with the given number of repeated chars?

How can I convert [(char,Int)] to a String of the Int in the second component gives the number of repetitions of the character in the first component? For example the input [(a,9),(b,10)] should give ["aaaaaaaaa","bbbbbbbbbb"] as output.
Hugs> map (\(c,n) -> replicate n c) [('a',9), ('b',10)]
["aaaaaaaaa","bbbbbbbbbb"]
or
map (uncurry $ flip replicate)
This can be assembled from just a few functions in the Prelude. Since your input is a list of tuples, the return value becomes a list of strings.
repChars :: (Char, Int) -> String
repChars (c,n) = replicate n c
Prelude> map repChars [('a',9),('b',10)]
["aaaaaaaaa","bbbbbbbbbb"]
Or if you want to do it as a point-free one-liner:
repCharList = map (uncurry (flip replicate))
Is this homework? If so, please use the homework tag.
I'm assuming the input is supposed to be [('a', 9), ('b', 10)] since without the 's it would only make sense if a and b were previously defined, which you did not mention.
In that case you can use replicate to create a list which contains a given element a given number of times (note that the string "aaaaaaaaaa" is a list containing the element 'a' 9 times). To do that for every tuple in the list, you can use map on the list. Now you have a list containing the strings for each character. To turn that into a single string separated by commas, you can use intercalate, which takes a separator and a list of lists and returns a single li.
The facetious and horrible answer:
Prelude> let replignore ((_,x):[]) = [replicate x 'b']; replignore ((_,x):xs) = replicate x 'a' : replignore xs
Prelude> replignore [(a,9),(b,10)]
<interactive>:1:13: Not in scope: `a'
<interactive>:1:19: Not in scope: `b'
Prelude> let a = undefined
Prelude> let b = undefined
Prelude> replignore [(a,9),(b,10)]
["aaaaaaaaa","bbbbbbbbbb"]
But it didn't quite fit the specs since it includes the quotation marks in the answer. ;)
My point is, you need quotes around your Char and String literals.

Resources