Reputation: 343
New to Haskell and the language has been fun so far. I am hoping for a good hint rather than an answer as I am enjoying the mind-altering that is Haskell.
Question: I have a list of strings and I would like to transpose them.
let x = ["hello", "world"]
would become
["hw", "eo", "lr", "ll", "od"]
What I have so far is this:
transposeString :: [a] -> [a]
transposeString ([]:_) = []
transposeString x = (map head x) : transposeString (map tail x)
I definitely know there is something wrong with the type signature. My rational is that
Let y = ["wow", "top"]
map head y
returns "wt" so recursing this on the rest of the list would work?
Thank you in advance for any hints.
Upvotes: 1
Views: 1712
Reputation: 3346
Do remember String
is [Char]
"abc" == 'a' : 'b' : 'c' : []
From here you can use traversable nature of lists:
transpose :: [[a]] -> [[a]]
transpose mat = getZipList $ sequenceA $ map ZipList mat
test' = transpose [[1,2,3],[4,5,6],[7,8,9]] -- == [[1,4,7],[2,5,8],[3,6,9]]
test'' = transpose ["abc", "deg", "klm"] -- == ["adk","bel","cgm"]
You can also check default implementation of transponse
in haskell doc
https://hackage.haskell.org/package/base-4.12.0.0/docs/src/Data.OldList.html#transpose
transpose :: [[a]] -> [[a]]
transpose [] = []
transpose ([] : xss) = transpose xss
transpose ((x:xs) : xss) = (x : [h | (h:_) <- xss]) : transpose (xs : [ t | (_:t) <- xss])
Upvotes: 0
Reputation: 477180
Mind that you do not have to provide a type signature: the Haskell compiler can derive one. If you put your implementation in a file:
transposeString ([]:_) = []
transposeString x = (map head x) : transposeString (map tail x)
and query the type with :t
in ghci
, it returns:
*Main> :t transposeString
transposeString :: [[b]] -> [[b]]
This makes perfect sense:
[[b]]
is a list of lists of b
elements; andmap head x
means that elements of x
must be a list ([b]
) since we perform a mapping, we have to nest the list one additional level so [[b]]
. The same for tail
.As far as I know, your implementation is correctly. You can specialize it by saying that [b] ~ String
, thus adding a type signature for String
s:
transposeString :: [String] -> [String]
transposeString ([]:_) = []
transposeString x = (map head x) : transposeString (map tail x)
which again makes sense because String ~ [Char]
thus b ~ Char
. But there is not much point in specializing a functions type: you better always use the most generic type signature. In this case [[b]] -> [[b]]
.
Upvotes: 4
Reputation: 2753
One point of note. Your type signature for tranposeString
allows to accept a flat list as an argument. Tranposing a [String]
works because [String]
s are really just [[Char]]
s, but what happens when you try to call transposeString
on an [Int]
? After all, the type signature allows for it.
On a side note, I ask you, given your current function, what would happen if your called transposeString []
?
Upvotes: 2