0

I want to have a function with signature :: [(Int,Char)] -> [String] which takes each Char and combines them into a list of Strings according to the Integer value in the corresponding pair...

ie.

[(0,'A'),(1,'B'),(2,'C'),(3,'D'),(0,'E'),(1,'F'),(2,'G'),(3,'H'),(0,'I'),(1,'J'),(2,'K'),(3,'L'),(0,'M'),(1,'N'),(2,'O'),(3,'P')]

Should generate the list: ["MIEA","NJFB","OKGC","PLHD"]

I've tried:

toList :: [(Int,Char)] -> Int -> [String]
toList [] a = []
toList (x:xs) a = [snd(x) | n <-[0..(a-1)], fst(x) == n]:toList xs a

but that just gives me:

["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P"]

Any suggestions would be greatly appreciated.

sclv
  • 38,665
  • 7
  • 99
  • 204
J Kawala
  • 3
  • 2
  • What int are you passing in to `toList`? – sclv Feb 03 '13 at 21:05
  • oops, sorry. An integer that corresponds to the number of elements in the input list... The number of Char in the original list of Strings. – J Kawala Feb 03 '13 at 21:10
  • It seems to me that the Int parameter is unnecessary. If it is the length of the `[(Int, Char)]`, then simply use the `length` function instead. – Code-Apprentice Feb 03 '13 at 21:13

2 Answers2

3

You need to sort input by the index, then group all pairs with the same index. Something like the next:

map (reverse . map snd) $ groupBy ((==) `on` fst) $ sortBy (compare `on` fst) $ input

Without $ operator:

import Data.List
import Data.Function

toList :: [(Int, Char)] -> [String]
toList input = map (reverse . map snd) grouped
  where
  sorted = sortBy (compare `on` fst) input    -- sorted by the index
  grouped = groupBy ((==) `on` fst) sorted    -- grouped by the index
Yuras
  • 13,856
  • 1
  • 45
  • 58
  • I apologize if this is a silly question, but I'm unfamiliar with the '$' code... I've tried this: convertToList :: [(Int,Char)] -> Int -> [String] convertToList [] a = [] convertToList aPic a = map(reverse.map snd(aPic)) $ groupBy ((==) `on` fst(aPic) $ sortBy (compare `on` fst(aPic)) – J Kawala Feb 03 '13 at 21:17
  • re '$' I recommend the next reading: http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign – Yuras Feb 03 '13 at 21:26
  • OK, I 'Hoogle'd $ and think I know what it does... but am I correct in adding (name of list of pairs) to the fst and snd parts of your suggested code solution? The reason I ask is I get a 'parse' error when I try to compile. I am SO new to Haskell. Thank you for your help. – J Kawala Feb 03 '13 at 21:27
  • You are right, the snippets are not self-containing. For the one-liner you need to add `$ `, for the second version you need to add few imports for `on` and `groupBy` (should be easy to hoogle them) – Yuras Feb 03 '13 at 21:34
  • @Yuras It should be easy for you to add them to your answer. – AndrewC Feb 03 '13 at 21:50
  • Easier (for me to think about): `map reverse . Map.elems . Map.fromListWith (++) . second (:[])` -- assuming the standard bits of toolkit `import qualified Data.Map as Map` and `import Control.Arrow (second)` – luqui Feb 04 '13 at 02:27
1

Now that my assignment due date has passed I can include my whole question... the function was asked to rotate a simulated 'picture' through 90 degrees clockwise. The solution I came up with (using simple Haskell code as this is was our first assignment) is as follows. While it wworks for simple 'square' lists of strings it does not appear to work for more rectangular lists... not sure why yet.

{- The function rotate90 rotates a "picture" (List of Strings) through
   90 degrees clockwise and prints the list to the standard output device
   using the printPicture function defined in the Thompson Picture.hs file.
-}

-- the following String list is a sample used to test implementation:
pic :: [String]
pic = [ "ABCD", "EFGH", "IJKL", "MNOP", "QRST" ]

rotate90 :: [String] -> IO ()
rotate90 aPic   = printPicture(convertToPic (listPairs aPic (maxOfList(widthOfLines aPic))     ((maxOfList(widthOfLines aPic))*(length(aPic)))) ([0..((maxOfList(widthOfLines aPic))-1)]))

-- takes a picture, the width and the number of Chars in a rectangle
-- bounding the picture (area) and converts it to a list of pairs with
-- an address and Char as per the cypher algorithm:
-- address = floor of (area -1)/ width
listPairs :: [String] -> Int -> Int -> [(Int,Char)]
listPairs [] w area = []
listPairs aPic w area   = [ (((n-1)`mod`w),(aPic!!(floor(fromIntegral(n-1)/(fromIntegral(w))))!!((n-1)`mod`w))) | n <- [1..area] ]

-- takes a list of tuples (pairs) and an Int which is the width of the input picture
-- it retunrs a list of Strings that correspond to the groupings of the pairs.
convertToPic :: [(Int,Char)] -> [Int] -> [String]
convertToPic [] (y:ys)  = []
convertToPic somePair []    = []
convertToPic somePair (y:ys)= reverse([ snd(x) | x <- somePair, ((fst(x)) == y)]):(convertToPic somePair ys)

-- takes a list of Int and returns the maximum value from the list as an Int
maxOfList :: [Int] -> Int
maxOfList []        = 0
maxOfList (x:xs)        = max x (maxOfList xs)

-- takes a list of type String and returns a list of Int of the length of each string
widthOfLines :: [String] -> [Int]
widthOfLines []     = []
widthOfLines (x:xs)     = (length x):(widthOfLines xs)

-- definition from Thompson (textbook) picture.hs file
printPicture :: [String] -> IO ()
printPicture = putStr . concat . map (++"\n")