ordering in modified/deranged alphabet in haskell - 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"

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 do you filter a list of strings by a string in haskell?

I have a string containing letters I want to be sure are in the words in the list. Running it however results in it still leaving behind words that contain the undesired letters.
Here's my function:
import Data.List
filterWords :: String -> [String]
filterWords str =
let strs = words str
letters = concat . words . nub $ "poultry outwits ants"
predicate = dropWhile (`elem` letters) ['a' .. 'z']
in dropWhile (any (`elem` predicate)) strs
What do I need to change to make this work?
To make it clear, I want to filter away any words that contain letters not in "poultry outwits ants", meaning a word like "years" would be dropped because despite containing 'y', 'a', 'r', and 's' which all satisfy the predicate, it also contains 'e' which doesn't.
A good way to filter a list of things (e.g. words) is to use the filter function. What you need to provide is a predicate which tells whether a string should be included or not. You commented that you want to include those strings which consists of letters in "poultry outwits ants", so that would be
filterWords :: String -> [String]
filterWords str = filter acceptableWord (words str)
where
acceptableWord = all (`elem` "poultry outwits ants")
Now, in another comment you wrote that
Some of the words I get have more copies of the same letter than there are in the original.
So I suspect what you really want is to figure out which words can be formed out of the letters in "poultry outwits ants".
To do this, you could count how often each character appears in the given word (and in the mgic string poultry outwits ants) and then verify that not only each letter in the word appears in the magic string but also that the letter doesn't appear more often than in the magic string.
I'd start by defining a function which calculates 'character frequency table', i.e. it counts how often each character occurs in the given string:
freq :: String -> [(Char, Int)]
freq = map (\s -> (head s, length s)) . group . sort
Furthermore, I'd define a function which tells whether one frequency table x is a "subset" of another table y, i.e. it verifies that each character in x also appears in y, but it doesn't occur more often:
subset :: [(Char, Int)] -> [(Char, Int)] -> Bool
subset x y = all f x
where
f (ch, occ) = case lookup ch y of
Just occ' -> occ <= occ'
Nothing -> False
You can then use this to define acceptableWord such that it only accepts words whose frequency table is a subset of the frequency table of the magic string, so we get:
filterWords :: String -> [String]
filterWords str = filter acceptableWord (words str)
where
acceptableWord w = subset (freq w) (freq "poultry outwits ants")

Haskell char quotes

I've started to learn haskell for real recently, and I'm doing some exercises from wikibooks.
I'm doing exercise with RLE encoding, and I've come with solution like this:
import Data.List
rle :: String -> [(Int,Char)]
rle [] = []
rle xs = zip lengths chars
where
groups = group xs
lengths = map length groups
chars = map head groups
rle_toString :: [(Int, Char)] -> String
rle_toString [] = []
rle_toString (x:xs) = show (fst x ) ++ show (snd x) ++ rle_toString xs`
Not a very elegant solution, but it almost works. The problem is, that I get output like this: "7'a'8'b'7'j'6'q'3'i'7'q'1'p'1'a'16'z'2'n'". The single quotes with chars are not vetry elegant. How can I achieve output like: "7a8b7j6q3i7q1p1a16z2n"?
show is used to print values as they appear in Haskell source code, and thus puts single quotes around characters (and double quotes around strings, and so on). Use [snd x] instead to show just the character.
In Haskell, String is just shorthand for List of Char [Char]. For example, the String "Foo" can also be written like this: ['F','o','o']. So, to convert a single character to a string, just put in in brackets: [char].
The problem is your use of show on a character. show 'a' == "'a'".
The solution is to realize that strings are just lists of characters, so if c is a character, then the one-character string that contains c is just [c].

How to delete "Just" in Maybe String or Maybe Int in Haskell

i am new to haskell and in need of a little help with a problem. I have a XML file and i want to parse two things in it. One is a pair of numbers, and the second is just a letter. I use two functions to parse each element, and the result of that functions has to be "Maybe String", or else it won't work.
The problem is, that when i display the result it allways has a "Just" attached to it. So if i want to display something like (1,2) and B
I will have (Just "1", Just "2") and Just "B"
For the numbers i got the solution. I have made this function to work with the list i get from the parse of the pairs:
pair:: [(Maybe String, Maybe String)] -> [(Int, Int)]
pair [] = []
pair ((Just x, Just y) : ls) = (read x, read y) : pair ls
It works fine. But i tried to do the same for the list with the letters and it doesn't work.
here is my code:
letter :: [Maybe String] -> [Char]
letter [] = []
letter (Just x : ls) = read x : letter ls
When i run this, i get the list of pairs right and this error
myfile.hs: Prelude.read: no parse
Why is this happening? Is the read function the problem?
The read parser for Char expects the character to be surrounded in single quotes, i.e. you need a string like "'M'".
Prelude> read "M" :: Char
*** Exception: Prelude.read: no parse
Prelude> read "'M'" :: Char
'M'
You probably want to do something like
letter :: [Maybe String] -> [Char]
letter [] = []
letter (Just [x] : ls) = x : letter ls
However, this of course only works if you always get just Just values and no Nothing values and all strings must have a length of one or the pattern match will fail.
Change your definition of letter to:
letter :: [Maybe String] -> [Char]
letter [] = []
letter (Just x : ls) = (head x) : letter ls
Explanation: read will try to read the content of string as an Haskell Char literal. Since Char literals in Haskell are terminated by quotes, your strings would need to include them (i.e: read "'B'" :: Char works, read "B" :: Char does not).

Haskell get character array from string?

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'

Resources