What to use instead of a list comprehension - list

I'm just getting started with Haskell and finished a nice exercise to make a Caesar cipher.
One of the first steps was to make a function that will take a letter and turn it into a number. I know that chr and ord can do this already but part of the exercise was to write your own.
let2num c = head [ b | (a,b) <- zip ['a'..'z'] [0..25], a==c]
I'm new to the Haskell syntax and one of the first things I learned was list comprehensions, so that has become my hammer. I'm very curious though, what is another (likely better) way to write this function?
If you're curious the rest of the cipher is in a gist.
EDIT
I'm also interested in other ways to translate back from numbers to letters.
num2let d = head [ a | (a,b) <- zip ['a'..'z'] [0..25], b==(d `mod` 26)]

My solution:
import Data.List
let2num c = let (Just n) = elemIndex c ['a'..'z'] in n
Or:
import Data.List
import Data.Maybe
let2num c = fromJust $ elemIndex c ['a'..'z']
Or in pointless style:
import Data.List
import Data.Maybe
let2num = fromJust . (flip elemIndex) ['a'..'z']
The function elemIndex returns the index of the first element in the given list which is equal (by ==) to the query element, or Nothing if there is no such element.
The Maybe type encapsulates an optional value. A value of type Maybe a either contains a value of type a (represented as Just a), or it is empty (represented as Nothing). Using Maybe is a good way to deal with errors or exceptional cases without resorting to drastic measures such as error.
The function fromJust extracts the element out of a Just.

The reverse process:
num2let = (!!) ['a'..'z']
!! is a List index (subscript) operator, starting from 0. It is an instance of the more general Data.List.genericIndex, which takes an index of any integral type.
(!!) is partially applied here, which means it still needs one argument of type Int to yield the result (a value from the list whose index equals to Int value you pass to num2let).

“Caesar simply replaced each letter in the message by the letter three places further down the alphabet, wrapping around at the end of the alphabet.” We can simply write it in Haskell. In fact we can avoid let2num and num2let altogether.
So let's start with defining a table to map plain text alphabet to the cipher text alphabet:
cipher = let abc = ['a'..'z']
code = drop 3 abc ++ take 3 abc
in zip abc code
It will look like
[('a','d'),('b','e'),('c','f'),('d','g'), ... ]
Now we can encrypt a symbol, if we simply lookup the letter in this dictionary:
ghci> lookup 'a' cipher
Just 'd'
lookup returns a Maybe Char value, we need to convert it to simply a Char, and for this I use maybe function, using '?' for symbols which were not found in the cipher, and id (identity function = no changes) to found symbols:
ghci> maybe '?' id (lookup 'a' cipher)
'd'
Now we can write an encrypt function to encode just one symbol, it will leave missing characters, like a space, unencrypted:
encrypt c = maybe c id (lookup c cipher)
To encrypt an entire string:
ghci> map encrypt "haskell is fun"
"kdvnhoo lv ixq"
So we can put it all together:
encrypt c = maybe c id (lookup c cipher)
where
cipher = let abc = ['a'..'z']
code = drop 3 abc ++ take 3 abc
in zip abc code

For completeness, I think somebody should mention that list comprehensions are just a shortcut for writing stuff in the list monad. Your code transcribed is, roughly, this:
let2num c = head $ do (a,b) <- zip ['a'..'z'] [0..25]
if a == c then [b] else []
Not a very interesting example, but there you go.
Also, de-sugaring the do syntax, this is the same:
let2num c = head $ zip ['a'..'z'] [0..25] >>= \(a,b) -> if a == c then [b] else []

I'm not sure why you are opposed to the ord solution. List-based solutions perform unnecessary work (traversing a list). And they still are desugared into invocation of the enumFromTo, a method of the Enum class which allows to convert between Ints and Chars in the same way as ord/chr do. This is the lowest-level interface provided for Char type, so you hardly can "write your own" here (apart from doing boxing/unboxing yourself, but this is not a big joy).

I would go with the following:
import Data.Char
caesar :: Int -> Char -> Char
caesar n c = if isAlpha c
then chr (ord 'a' + (ord c - ord 'a' + n) `mod` 26)
else c
and map (caesar n) over the string with n the desired offset.

Related

What is the idiomatic Haskell way to check if one list contains all the values in another list?

My question is similar to How can i use haskell to check if a list contains the values in a tuple, but in my case, the list with values which must all be in my original list is the alphabet. It feels really messy to have 26 calls to elem, all ANDed together.
Is there any more concise way to check if a given list contains all of the elements in another list, where the other list is 26 items long?
I like the answer by user1984, but it does leave implicit a bit the way you would handle non-alphabetic characters. Here's one that's almost as simple, but doesn't need extra code to handle non-alphabetic characters. The basic idea is to go the other direction; their answer builds up a set, this one tears one down by deleting one character at a time. It is also O(n), like theirs.
import qualified Data.Set as S
isPangram :: String -> Bool
isPangram = S.null . foldr S.delete (S.fromList ['a'..'z'])
If you want case insensitivity, you can import Data.Char and change S.delete to (S.delete . toLower).
This is based on Daniel Wagner's answer, but it finishes early if all the letters are found, whereas his walks the whole list, then works backwards deleting elements, then gives an answer when it gets back to the beginning.
import qualified Data.Set as S
import Data.Set (Set)
letters :: Set Char
letters = S.fromList ['a'..'z']
isPangram :: String -> Bool
isPangram xs0 = go xs0 letters
where
go :: String -> Set Char -> Bool
go [] letters = S.null letters
go (c : cs) letters =
S.null letters || go cs (S.delete c letters)
You may be wondering why I match on the string first and check on letters under the match, instead of the other way around. Well, that's only because it's fun to rewrite this using foldr:
isPangram :: String -> Bool
isPangram xs0 = foldr step S.null xs0 letters
where
step :: String -> Set Char -> Bool
step c r letters =
S.null letters || r (S.delete c letters)
For something like the alphabet, Data.Set is not actually the most efficient way. It's better to use something like IntSet, which is much more compact and significantly faster.
import qualified Data.IntSet as S
import Data.IntSet (IntSet)
letters :: IntSet
letters = S.fromList (map fromEnum ['a'..'z'])
isPangram :: String -> Bool
isPangram xs0 = foldr step S.null xs0 letters
where
step :: String -> IntSet -> Bool
step c r letters =
S.null letters || r (S.delete (fromEnum c) letters)
My understanding is that you are trying to check whether a string is a pangram, meaning it contains all the [insert your preferred language] alphabet.
The following code uses Data.Set. A set data structure can't contain duplicate elements by definition. We use this property of a set to count the number of elements that are in it after we added all the character from the string to it. If it's equal to the number of the alphabet we are working with, it means that the string contains all the characters of the alphabet, so it's a pangram.
Note that this code does not take care of the difference between lower and upper case letters and also counts white space and punctuation. This would give you wrong answers if there are any of these cases. With some tweaking you can make it work though.
The time complexity of this solution is n * log 26 which is just n in Big O, as Daniel Wagner pointed out. The reason is that while Haskell's default set is a balanced tree, the number of elements in the set never goes above 26, so it becomes linear. Using a hashing implementation of the set may result in some optimization for very large strings. The space complexity is constant since the additional space we are using never exceeds 26 characters.
import qualified Data.Set as S
isPangram :: [Char] -> Bool
isPangram s = length (S.fromList s) == 26
To answer your question literally,
allPresent :: Eq a => [a] -> [a] -> Bool
allPresent alphabet xs = foldr (\a r -> elem a xs && r) True alphabet
is a concise and inefficient code that will call elem as many times as there are letters in your alphabet and will "AND" the results.
For example, for a three-letter alphabet a, b, and c, it will be equivalent to an &&-chain of three calls to elem and a True:
allPresent [a, b, c] xs
==
elem a xs && elem b xs && elem c xs && True
It will stop as soon as the first missing letter will be detected, returning False. But it will potentially comb through the whole of xs anew for each letter of the alphabet, which is inefficient.
If you are infact looking for a pangram, you could try this solution which I came up with.
import Data.Char ( toLower )
isPangram :: String -> Bool
isPangram text = all isInText ['a'..'z'] where
isInText x = x `elem` map toLower text

Getting elements from a list by an index list

I have two lists in Haskell.
Original list containing string values:
["Hello", "HELLO", "", "WORLD", "xxx", "world"]
Index list containing integer values where the strings are all upper case in the original list:
[1,3]
I incremented all the values in the index list with a function I created and make index2 list, overall it looks like this:
My code:
import Data.List
import Data.Char
import Data.Maybe
main = do
contents <- readFile "h.txt"
let original = lines (contents)
let allUpper = lines (map toUpper contents)
let onlyUpper = filter(/="") (intersect original allUpper)
let upperIndex = findIndices ('elem' onlyUpper) original
let index2 = (map increment upperIndex)
print index2
increment :: Int -> Int
increment x = x+1
I have managed to came this far with the help of yours. However, since I am a beginner I do not seem to understand how iteration over lists works.
The thing I want to accomplish is to check whether the corresponding index values (in index2) are empty or not in the original list, if they are empty, I want to remove them in index2.
Filtering empty elements
The thing I want to accomplish is to check whether the corresponding
index values (in index2) are empty or not in the original list, if
they are empty, I want to remove them in index2.
The code already filters out empty elements! Look at the following line:
let onlyUpper = filter(/="") (intersect original allUpper)
This line does two things:
it keeps only elements which are constituted only with uppercase letters(intersect original allUpper),
it filters out empty elements (filter(/="")).
If by empty elements you mean strings which contains only space characters or nothing, you can use instead:
filter (all isSpace)
Iterating over lists
I do not seem to understand how iteration over lists works.
In Haskell, lists are single chained lists: each element contains a value and a reference to the next value.
Therefore lists are not indexed: the !! operator have to go through each element to access a specific element making lists completely inefficient when dealing with direct access.
When you’re submitting a list to a function, you simply give it the first element.
With these considerations, when you work on lists, you have to avoid accessing elements via their index.
The idea is to create functions which do their job on simple values and mapping them to list of elements. Take a look at the toUpper function:
toUpper :: Char -> Char
It takes a Char and returns its uppercased version (also a Char).
Haskell does not have a toUpper function which works on String, you have to use something like map or <$> to apply toUpper to a list of char (a String):
map toUpper "ab" -- "AB"
toUpper <$> "ab" -- "AB"
The idea is to have functions which does only one specific thing. Upercasing and iterating over a list are two different things. Does the toUpper function need to know the index of the element it will uppercase? No!
Iterating over a list with index
You may ask: but what if my function REALLY need to consider the index of the elements? (ie: for filtering out even or odd elements).
You have two way of considering it:
a List is not the type you need to work with. Maybe Data.Map, Data.IntMap or Data.Vector are better suited for the task (see these modules for more information),
you need to use an intermediate type which will hold the index.
For example:
let string = "abcde"
let indexedString = zip [1..] string
print indexedString -- [(1, 'a'), (2, 'b), (3, 'c), (4, 'd), (5, 'e)]
Note that this also solves your need of an increment function since the index is started at whatever value you want.
To go back to the original string, you write:
map snd indexedString -- "abcde"
You need to use the fst and snd functions to work with the intermediate type, or to use pattern matching:
filter (\x -> snd x == 'b') indexedString -- [(2, 'b')]
map (\(i,s) -> (i, toUpper s)) indexedString -- [(1,'A'),(2,'B'),(3,'C'),(4,'D'),(5,'E')]
Taking the index into account:
let string = "abcde"
indexedString = zip [1..] string
upperEven (i, c) | even i = (i, toUpper c)
| otherwise = (i, c)
print $ map upperEven indexedString -- [(1,'a'),(2,'B'),(3,'c'),(4,'D'),(5,'e')]
print $ map snd $ map upperEven indexedString -- "aBcDe"
Notes
The increment function already exists in Haskell, it’s called succ (it is also a more generic function which works on every types supporting the Enum class like Int, Char…)
Why not use words :: String -> [String] on the contents you get from a file? Using lines :: String -> [String] would be an alternative if you had one word per line.
Then if i get your problem right, you could write the following to solve your problem:
import Data.List (findIndices)
import Data.Char (isUpper)
allUpperOneBasedIndices :: String -> [Int]
allUpperOneBasedIndices = map succ . findIndices (all isUpper) . words

Haskell and manipulating a list of tuples

Ok so have been faced with a problem where basically I have a been told to make a multiset, or a list of tuples. (Char,Int) and then I have to write a function that takes a item and inserts it into this list, but if there is already a matching tuple in the list it increases the Int.
i.e. i had a list [(p,2),(w,3)] and i get another w it should give [(p,2),(w,4)]
How would you go about it, i've tried
listAdd :: Char->Int->ListOfT -> ListOfT
listAdd c i l
|length l == 0 =(c,i):l
|fst l == c = (c,i+1):l
but this gives loads of errors, i need to remove the list element at that point and replace it with with (c,i+1), so how do i remove from the list and how to i get i+1? also how do you make a loop which will go through all the elements in a list?
And i can't use any of the import Data stuff
I know this is asking a ton but any help would be great thanks.
Neo
Okay can this code be fiddled with so it can be used tto make tuples of any items not just chars. so i could load it up and make a list of tuples with stirngs instead, close it then load it up again and make a list of tuples of ints?
ok I think your idea is not bad you just have to get the details straight.
The loop you asked about is usually either done with recursion (as a list is a recursive structure that's a great idea) or with some higher order functions like map, filter, foldr, ... that will hide the recursion from you (you could say they abstract away the repeating stuff) - anway in this case I think the easiest way is just to go with what you started and use the direct recursion.
Here is a simple version (you maybe want to extent) that does the basic stuff:
listAdd :: Char -> [(Char,Int)] -> [(Char,Int)]
listAdd c [] = [(c,1)]
listAdd c ((c',i):xs)
| c' == c = (c,i+1):xs
| otherwise = (c',i) : listAdd c xs
as you can see the first case is very similar to what you had: if the dictionary (the second argument) is the empty list than you just add a new tuple with the char to insert and the number 1
if not then you check if the first element in the dictionary has the same character (c' here), if yes then you increase the count and if not you let this element stand as it is and recursively search through the rest of the dictionary.
Also note that you can use pattern matching here to not only deconstruct the dictionary into head::tail form but also deconstruct the head into (..,..) tuple parts as well.
If you want you can use a # in there to and get the second case a bit more concise:
listAdd :: Char -> [(Char,Int)] -> [(Char,Int)]
listAdd c [] = [(c,1)]
listAdd c (x#(c',i):xs)
| c' == c = (c,i+1):xs
| otherwise = x : listAdd c xs
PS: in case you wondered why I did not use your Int argument? Because I don't know what you want to do with it if there is already a value - here is a version where I just add it to it (seems resonable):
listAdd :: Char -> Int -> [(Char,Int)] -> [(Char,Int)]
listAdd c i [] = [(c,i)]
listAdd c i (x#(c',i'):xs)
| c' == c = (c,i+i'):xs
| otherwise = x : listAdd c i xs
List manipulations with just recursive functions can be indeed hard for beginners to grok, but in this case they should fit the problem nicely.
Let's start with a bit better signature and a helper.
type MyList = [(Char, Int)]
listAdd :: Char -> MyList -> MyList
listAdd p l = listAdd' p [] l
Notice that I've changed the signature to accept just Char; we don't need to supply the initial count, since if there are no such elements currently on the list, we'll just set it to 1 when adding a new element.
Okay, that's the basic skeleton. The helper is there just to make it easier to store the "already processed" part of the list. Let's look at it:
listAdd' :: Char -> MyList -> MyList -> MyList
First, we add the recursion end condition:
listAdd' p left [] = left ++ [(p, 1)]
This means that if we haven't found the element to replace earlier, we can just add it at the end.
listAdd' p left (x:right) = if p == fst x
then left ++ [(fst x, snd x + 1)] ++ right
else listAdd' p (left ++ [x]) right
Okay, so now we split up the "right" part to the first element of it and the rest. Let's look at the if:
if we managed to find the element, we can end the computation by appending the rest of the list to the modified element and what we had previously
if it's still not it, we proceed with recursion.
As an additional remark at the end, you could easily change Char to Eq a => a to allow your function to work on any type that can be directly compared, Char included.

Haskell: Best way to turn a list of tuples into a haskell function?

i have a list of triples of strings like this:
mylist = [("a1","a2","a3"), ("b1","b2","b3"), ("c1","c2","c3")]
The actual strings are arbitrary, except that the list represents a binary function that maps the first two strings to the third. What is a computationally fast way to implement a function
f :: String -> String -> String
so that
f "a1" "a2" == "a3"
f "b1" "b2" == "b3"
f "c1" "c2" == "c3"
And also, is there a general way to do this with a list of tuples?
So far, i only came up with
f a b = lookUp (a,b) [ ((x,y),z) | (x,y,z) <- mylist ]
Thanks,
André
This is usually done with a map (see Data.Map, that link also explains how it works and how to build your own map).
You'll likely need to use a ([Char],[Char]) as key, so your data could be represented this way:
[(("a1","a2"),"a3"), (("b1","b2"),"b3"), (("c1","c2"),"c3")]
It's a bit unclear what you want this function to do. If it is an arbitrary mapping of two inputs to one output, then go with peoro's suggestion of using Data.Map. If the computation has some systematic meaning, then try to discover the algorithm behind it.
import Data.Char
incStr [c,x] | isNumber x = [c, intToDigit (digitToInt x + 1)]
ghci> incStr "a1"
"a2"

Haskell int list to String

I would like to know if there is a simple way to turn [5,2,10] into "52a".
Where its not just to this case, I want to associate any number >9 with the corresponding letter.
Thanks in advance.
You want to do something to each element of a list in order to get a new list. In other words, you want to apply a function (that you will have to define yourself) to each element. This is what the map function from the Prelude is for.
To convert between integers and individual characters, you could use the chr and ord functions from the Data.Char module.
So,
map (\i -> if i < 10 then chr (i + ord '0') else chr (i - 10 + ord 'a'))
is a function of type [Int] -> String that does what you want (no error checking included, though).
Slower but more elegant:
f = map ((['0'..'9'] ++ ['a'..'z']) !!)
If your numbers are 0-15 use map intToDigit from Data.Char.