Is there a function in Haskell where say if you supplied a Char, and a List of 13 Pairs of Chars (all different i.e. every letter of the alphabet was used once and only once) it would return you the Char which is paired with your inputted Char i.e. if I inputted the following:
pairedChar Q [(A,Z),(B,Y),(C,X),(D,W),(E,V),(F,U),(G,T),(H,S),(I,R),(J,Q),(K,P),(L,O),(M,N)]
I would like it to return J?
If there isn't a function like that I was thinking maybe of doing it with unzip to get a pair of lists but wasn't sure what to do with the lists after I get them?
The lookup function does this; not just for the kind of pairs you describe but for any list of two-element tuples. A list of such tuples is known as an association list, by the way.
It returns a Maybe, because there might beono match.
lookup :: Eq a => a -> [(a, b)] -> Maybe b
lookup key assocs
looks up a key in an association list
This answer builds on itsbruce's answer by still using lookup, but also first massaging the input list to include each pair twice, once for each ordering of elements.
Let's assume that your list is called pairs:
pairs :: [(Char, Char)]
pairs = [('A', 'Z'), ('B', 'Y'), ..., ('M', 'N')]
Then all you need to do is duplicate each pair and swap the elements:
import Data.Tuple (swap)
allPairs :: [(Char, Char)]
allPairs = pairs ++ map swap pairs
-- allPairs = [('A', 'Z') ... ('M', 'N'), ('Z', 'A'), ... ('N', 'M')]
... where swap is a function from Data.Tuple that takes the two elements of a tuple and swaps them. It's defined like this:
swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)
Now you can do a lookup on the allPairs list:
pairedChar :: Char -> Maybe Char
pairedChar c = lookup c allPairs
If you want each duplicate pair to be adjacent to each other in the list, then you can also write allPairs like this:
allPairs = do
(x, y) <- pairs
[(x, y), (y, x)]
Now it will contain this ordering:
allPairs = [('A', Z'), ('Z', 'A'), ('B', 'Y'), ('Y', 'B') ... ('M', 'N'), ('N', 'M')]
You could just make a longer list.
alphabet = "ABCDEFGHIJKLMNOPQRTSUVXYZ"
pairs = zip alphabet (reverse alphabet)
theOtherChar k = lookup k pairs --does the job for you now.
Related
I am new haskell. I want always check the first element from the string and compare the string with Char value from the list. So firstly I will compare "H" with 'E', then "H" with 'F', then "H" with 'G', and last "H" with 'H'. And if "H" = 'H' so I want add to my output string value of list element, in my case for 'H' it is "GG". But If all four are not equal so I want add to list current character in my case "A".
So I want my output to look like this ["GG","A","GG","A"]
Do you know some solution ?
My code:
findc :: String -> [(Char, String)] -> [String]
findc str list =
let filtered = [ if inputChar `elem` [(str !! num)] then decodedValue else if inputChar /= (str !! num) then [(str !! num)] else [] | num<-[0..(length str)-1],(inputChar,decodedValue) <- list]
in filtered
input:
findc "HAHA" [('E', "AB"), ('F', "CD"), ('G', "EF"), ('H', "GG")]
bad output:
["H","H","H","GG","A","A","A","A","H","H","H","GG","A","A","A","A"]
Your function is using a list comprehension with effectively nested loops. It loops over the indices of the string and then over the elements in the list of tuples you've passed in. Both contain 4 elements, so it's natural that your list comprehension iterates 16 times and you end up with 16 elements in your incorrect output.
A simpler, correct approach
[(Char, String)] describes a lookup table.
Fortunately, Haskell gives us the lookup function.
:ghci> :t lookup
lookup :: Eq a => a -> [(a, b)] -> Maybe b
ghci> tbl = [('E', "AB"), ('F', "CD"), ('G', "EF"), ('H', "GG")]
ghci> lookup 'H' tbl
Just "GG"
ghci> lookup 'A' tbl
Nothing
Now, you just need to map this to your string in a list comprehension, handling the Just case differently from the Nothing case.
ghci> [case lookup ch tbl of {Just s -> ...; Nothing -> ...} | ch <- "HAHA"]
["GG","A","GG","A"]
Using Data.Maybe.maybe
Unsurprisingly the Data.Maybe library has fucntions for dealing with Maybe values like those returned by lookup.
case expr1 of { Just x -> expr2; Nothing -> expr3 }
Can be expressed as:
maybe expr3 (\x -> expr2) expr1
So we can write:
ghci> [maybe ... ... $ lookup ch tbl | ch <- "HAHA"]
["GG","A","GG","A"]
I have a list with the type `[(Int, Char, Int)]'. E.g:
[(1, 'x', 1), (1, 'y', 2), (2, 'x', 1)]
The first Int is the number of times the Char appears and the second Int is to differentiate the same char from each other. For example, there could be x1 and x2.
I want to join elements of that list that have the same 2nd and 3rd element. In the case of the list above, it would become [(3, 'x', 1), (1, 'y', 2)] (the first and third tuples from the initial list were added together).
I've looked into zipWith and list comprehensions, but none of them seem to work. Is there any other way that I'm not thinking about that might work here?
The two functions you want to use are Data.List.sortBy and Data.List.groupBy.
If we sort by comparing the second and third elements of each tuple, we get the entries in the list sorted by variable and exponent. This is accomplished by passing a lambda which uses pattern macthing to extract and compare just those elements.
import Data.List
lst = [(1, 'x', 1), (1, 'y', 2), (2, 'x', 1)]
lst' = sortBy (\(_, a, b) (_, a', b') -> (a,b) `compare` (a',b')) lst
-- [(1,'x',1), (2,'x',1), (1,'y',2)]
Now we need to group based on the second and third values. groupBy will not work the way you need on an unsorted list, so don't skip that step.
The lambda being passed to groupBy here should look very familiar from the previous example.
lst'' = groupBy (\(_, a, b) (_, a', b') -> a == a' && b == b') lst'
-- [[(1,'x',1), (2,'x',1)], [(1,'y',2)]]
Now summing the first elements of the tuples and incorporating the other information is trivial with list comprehensions.
We get the variable and exponent info from the first element in the list and bind those to x and y respectively, then sum up the first coefficients and build a new tuple.
[let (_,x,y) = lst!!0 in (sum [c | (c,_,_) <- lst], x, y) | lst <- lst'', not (null lst)]
-- [(3,'x',1), (1,'y',2)]
First of all, I would suggest working with more meaningful domain types. A 3-tuple of built-in types could mean a lot of different things. By defining a new type and naming the components, you make everything clearer and prevent mistakes like getting the two Ints mixed up:
type Power = Int
type Coefficient = Int
data Exp var = Exp var Power deriving (Show, Eq, Ord)
data Term var = Term Coefficient (Exp var) deriving Show
What you're doing looks a lot to me like combining terms in polynomials, so I've defined types that make sense in that context. You may prefer different names, or a different structure, if you're actually doing something else.
Now you're looking for a function of type [Term Char] -> [Term Char], which groups together like Exps. Generally Data.Map.fromListWith is a great tool for grouping list items together by a key:
import qualified Data.Map as M
combine :: Ord a => [Term a] -> M.Map (Exp a) Coefficient
combine = M.fromListWith (+) . map toTuple
where toTuple (Term coef exp) = (exp, coef)
Then all that's left is to re-inflate the Map we've extracted to a list again:
simplify :: Ord a => [Term a] -> [Term a]
simplify = map fromTuple . M.assocs . combine
where fromTuple (exp, coef) = Term coef exp
And indeed, we get the grouping you hoped for:
*Main> simplify [Term 1 (Exp 'x' 1), Term 1 (Exp 'y' 2), Term 2 (Exp 'x' 1)]
[Term 3 (Exp 'x' 1),Term 1 (Exp 'y' 2)]
How can I use lenses to obtain keys from multiple levels of nesting?
Consider the following types
data Outer = Outer { _outerMap :: Map String Inner }
data Inner = Inner { _innerMap :: Map Char Int }
makeLenses ''Outer
makeLenses ''Inner
and assume the following example value
example :: Outer
example = Outer $ Map.fromList
[ ("A", Inner $ Map.fromList [ ('a', 1), ('b', 2), ('c', 3) ])
, ("B", Inner $ Map.fromList [ ('a', 4), ('b', 6), ('c', 8) ])
, ("C", Inner $ Map.fromList [ ('a', 5), ('b', 7), ('c', 9) ])
]
Using lenses I can flatten example to a [Int] and filter the odd numbers as follows:
>>> example^..outerMap.folded.innerMap.folded.filtered odd
[1,3,5,7,9]
I can annotate the values with the inner key as follows:
>>> example^#..outerMap.folded.innerMap.ifolded.filtered odd
[('a',1),('c',3),('a',5),('b',7),('c',9)]
But how can I use lenses to annotate the values with both the outer and inner keys, to get the following result?
>>> _whatHere example
[(("A",'a'),1),(("A",'c'),3),(("C",'a'),5),(("C",'b'),7),(("C",'c'),9)]
The following attempt still only returns the inner keys:
>>> example^#..outerMap.ifolded.innerMap.ifolded.filtered odd
[('a',1),('c',3),('a',5),('b',7),('c',9)]
And the following attempt doesn't type-check
>>> example^..outerMap.ifolded.withIndex.alongside id (innerMap.ifolded.filtered odd.withIndex)
error:
• No instance for (Applicative
(Control.Lens.Internal.Getter.AlongsideRight
(Const (Data.Monoid.Endo [([Char], (Char, Int))])) [Char]))
An implementation without lenses might look something like this:
nolens :: Outer -> [((String, Char), Int)]
nolens =
filter (odd . snd)
. foldMap (\(k, i) -> (map (first (k, )) . Map.toList . _innerMap) i)
. Map.toList
. _outerMap
Use (<.>). It's just like (.), except it preserves the indices on both the left and the right. (.) itself (and its alias (.>)) preserves only the index of the RHS, unless the RHS is itself index-preserving, in which case the index comes from the LHS. The mnemonic is that the arrows point to the indices you'd like to save.
>>> example^#..outerMap.ifolded<.>innerMap.ifolded.filtered odd
[(("A",'a'),1),(("A",'c'),3),(("C",'a'),5),(("C",'b'),7),(("C",'c'),9)]
I'm writing a function of the following type:
match :: [(String,a)] -> Maybe (String, a, a)
I want the function to traverse the list of tuples, and determine if there are any tuples in which the first element (a string) is the same. If so, I want to return a tuple containing that string, as well as the second element in each of those matching tuples. If there are no matching tuples, return "Nothing". If there is more than one matching, return the first one it finds.
For example:
match [("x", 3), ("y", 4), ("z", "5"), ("x", 6)] = ("x", 3, 6)
match [("x", 3), ("y", 4), ("z", "5")] = Nothing
I'm thinking:
match (x:xs) = if (fst x) = (fst xs) return (fst x, snd x, snd xs)
--if no matches, return Nothing
Thank you for any help!
What if there are three or four tuples with "x"? You can't have variable-length tuples. Maybe you want to return a list:
match :: [(String, a)] -> Maybe (String, [a])
What if there are several tuples that match? Do you want them all, or just the first? If you want them all then you should return a list of matches.
match :: [(String, a)] -> [(String, [a])]
If you think of it like this, then you can see that grouping all the "x" pairs together, and "y" pairs, and so on, would be a good start. You can do this by using
sortBy (comparing fst) xs
comparing takes a function and two values, applies the function to each, and compares the results. sortBy uses its first argument as a comparison function, so sortBy (comparing fst) sorts your list into order by the first element in each tuple.
Then you can use groupBy to collect the elements together.
Edit:
groupBy has the type
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
So you need to write a function equalFirst to give it as a parameter. So then
groupBy equalFirst $ sortBy (comparing fst) xs
will give you
[[("x", 3), ("x", 6)], [("y", 4)], [("z", 5)]]
Which is a list of lists. Each sub-list contains tuples with the same letter.
Now you can write a function which takes one of these sublists and converts it into the result you want. Then apply it to the list of lists using map.
I've been trying to solve this, but I just can't figure it out. So, I've a list with tuples, for example:
[("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]
and what I want to get is a list with also tuples where, if the name is the same, the numbers of those tuples should be added and, if not, that tuple must be part of the final list too, exemplifying:
[("Mary",25), ("John", 55), ("Bradley", 30)]
I don't know if I explained myself really well, but I think you'll probably understand with the examples.
I've tried this, but it doesn't work:
test ((a,b):[]) = [(a,b)]
test ((a,b):(c,d):xs) | a == c = (a,b+d):test((a,b):xs)
| otherwise = (c,d):test((a,b):xs)
Doing this sort of thing is always awkward with lists, because of their sequential nature--they don't really lend themselves to operations like "find matching items" or "compute a new list by combining specific combinations of list elements" or other things that are by nature non-sequential.
If you step back for a moment, what you really want to do here is, for each distinct String in the list, find all the numbers associated to it and add them up. This sounds more suited to a key-value style data structure, for which the most standard in Haskell is found in Data.Map, which gives you a key-value map for any value type and any ordered key type (that is, an instance of Ord).
So, to build a Map from your list, you can use the fromList function in Data.Map... which, conveniently, expects input in the form of a list of key-value tuples. So you could do this...
import qualified Data.Map as M
nameMap = M.fromList [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]
...but that's no good, because inserting them directly will overwrite the numbers instead of adding them. You can use M.fromListWith to specify how to combine values when inserting a duplicate key--in the general case, it's common to use this to build a list of values for each key, or similar things.
But in your case we can skip straight to the desired result:
nameMap = M.fromListWith (+) [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]
This will insert directly if it finds a new name, otherwise it will add the values (the numbers) on a duplicate. You can turn it back into a list of tuples if you like, using M.toList:
namesList = M.toList $ M.fromListWith (+) [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]
Which gives us a final result of [("Bradley",30),("John",55),("Mary",25)].
But if you want to do more stuff with the collection of names/numbers, it might make more sense to keep it as a Map until you're done.
Here's another way using lists:
import Data.List
answer :: [(String, Int)] -> [(String, Int)]
answer = map (foo . unzip) . groupBy (\x y -> fst x == fst y) . sort
where foo (names, vals) = (head names, sum vals)
It's a fairly straightforward approach.
First, the dot (.) represents function composition which allows us to pass values from one function to the next, that is, the output of one becomes the input of the next, and so on. We start by applying sort which will automatically move the names next to one another in the list. Next we use groupBy to put each pair with similar names into a single list. We end up with a list of lists, each containing pairs with similar names:
[[("Bradley",30)], [("John",10),("John",45)], [("Mary",10),("Mary", 15)]]
Given such a list, how would you handle each sublist?
That is, how would you handle a list containing all the same names?
Obviously we wish to shrink them down into a single pair, which contains the name and the sum of the values. To accomplish this, I chose the function (foo . unzip), but there are many other ways to go about it. unzip takes a list of pairs and creates a single pair. The pair contains 2 lists, the first with all the names, the second with all the values. This pair is then passed to foo by way of function composition, as discussed earlier. foo picks it apart using a pattern, and then applies head to the names, returning only a single name (they're all the same), and applying sum to the list of values. sum is another standard list function that sums the values in a list, naturally.
However, this (foo . unzip) only applies to a single list of pairs, yet we have a list of lists. This is where map comes in. map will apply our (foo . unzip) function to each list in the list, or more generally, each element in the list. We end up with a list containing the results of applying (foo . unzip) to each sublist.
I would recommend looking at all the list functions used in Data.List.
I think the reason your potential solution did not work, is that it will only group elements together if they occur sequentially with the same key in the list. So instead, I'm going to use a map (often called a dictionary if you've used other languages) to remember which keys we've seen and keep the totals. First we need to import the functions we need.
import Data.Map hiding (foldl, foldl', foldr)
import Data.List (foldl')
Now we can just fold along the list, and for each key value pair update our map accordingly.
sumGroups :: (Ord k, Num n) => [(k, n)] -> Map k n
sumGroups list = foldl' (\m (k, n) -> alter (Just . maybe n (+ n)) k m) empty list
So, foldl' walks along the list with a function. It calls the function with each element (here the pair (k, n)), and another argument, the accumulator. This is our map, which starts out as empty. For each element, we alter the map, using a function from Maybe n -> Maybe n. This reflects the fact the map may not already have anything in it under the key k - so we deal with both cases. If there's no previous value, we just return n, otherwise we add n to the previous value. This gives us a map at the end which should contain the sums of the groups. Calling the toList function on the result should give you the list you want.
Testing this in ghci gives:
$ ghci
GHCi, version 7.6.1: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> import Data.Map hiding (foldl, foldl', foldr)
Prelude Data.Map> import Data.List (foldl')
Prelude Data.Map Data.List> let sumGroups list = foldl' (\m (k, n) -> alter (Just . maybe n (+ n)) k m) empty list
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Prelude Data.Map Data.List> toList $ sumGroups $ [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]
[("Bradley",30),("John",55),("Mary",25)]
Prelude Data.Map Data.List>
The groups come out in sorted order as a bonus, because internally map uses a form of binary tree, and so it's relatively trivial to traverse in order and output a sorted (well, sorted by key anyway) list.
Here are my two cents. Using just the Haskell Prelude.
test tup = sumAll
where
collect ys [] = ys
collect ys (x:xs) =
if (fst x) `notElem` ys
then collect (fst x : ys) xs
else collect ys xs
collectAllNames = collect [] tup
sumOne [] n x = (x, n)
sumOne (y:ys) n x =
if fst y == x
then sumOne ys (n + snd y) x
else sumOne ys n x
sumAll = map (sumOne tup 0) collectAllNames
This method traverses the original list several times.
Collect builds a temporary list holding just the names, skipping name repetitions.
sumOne takes a name, checks what names in the list matches, and adds their numbers. It returns the name as well as the sum.