I want to concat in a list of tuples of elements and Strings, each char with the element.
For example: [(True, "xy"), (False, "abc")] -> [(True,'x'),(True,'y'),(False,'a'), (False,'b'),(False,'c')]
I do have a solution, but im wondering if there is a better one:
concatsplit :: [(a,[b])] -> [(a,b)]
concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a
concatsplit' :: (a,[b]) -> [(a,b)]
concatsplit' y = map (\x -> ((fst y),x)) (snd y)
List comprehensions can often do as much as higher order functions, and I think that if you don't need to change the data but only "unpack" it, they are pretty clear. Here's a working example:
concS :: [(a,[b])] -> [(a,b)]
concS ls = [(a,b) | (a,x) <- ls, b <- x]
sequenceA
makes it really easy:
concatsplit = concatMap sequenceA
Or generalize it even further:
concatsplit = (>>= sequenceA)
Details:
sequenceA
has type (Applicative f, Traversable t) => t (fa) -> f (ta)
. This means that if you have a type with a Traversable
on the "outside" and an Applicative
on the "inside", you can call sequenceA
on it to turn it inside-out, so that the Applicative
is on the "outside" and the Traversable
is on the "inside". (True, "xy")
has type (Bool, [Char])
, which desugars to (,) Bool ([] Char)
. There's an instance of Traversable
for ((,) a)
, and there's an instance of Applicative
for []
. Thus, you can call sequenceA
on it, and the result will be of type [] ((,) Bool Char)
, or [(Bool, Char)]
with sugar. sequenceA
have a useful type, but it does exactly the thing you need here (and turns out to be exactly equivalent to concatsplit'
). concatMap
has type Foldable t => (a -> [b]) -> ta -> [b]
. (>>=)
has type Monad m => ma -> (a -> mb) -> mb
. When specialized to lists, these become the same, except with their arguments in the opposite order. The other answers show how to do this idiomatically from scratch, which I like a lot. It might also be interesting to show how you might polish what you've already got. Here it is again as a reminder:
concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a
concatsplit' y = map (\x -> ((fst y),x)) (snd y)
The first thing I'd consistently change is called "eta reduction", and it is when you turn something of the shape \\x -> foo x
into just foo
. We can do this in the argument to concatMap
to get
concatsplit a = concatMap concatsplit' a
and then again in the argument to concatsplit
to get:
concatsplit = concatMap concatsplit'
Looking at concatsplit'
, the thing I like least is the use of fst
and snd
instead of pattern matching. With pattern matching, it looks like this:
concatsplit' (a,bs) = map (\x -> (a,x)) bs
If you really wanted to practice your eta reduction, you might notice that (,)
can be applied prefix and change this to
concatsplit' (a,bs) = map (\x -> (,) a x) bs
= map ((,) a) bs
but I think I'm just as happy one way or the other. At this point, this definition is small enough that I'd be tempted to inline it into concatsplit
itself, getting:
concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)
This looks like a pretty good definition to me, and I'd stop there.
You might be bugged by the almost-eta-reduction here: it's almost the right shape to drop the bs
. An advanced user might go on, noticing that:
uncurry (\a bs -> map ((,) a) bs) = \(a,bs) -> map ((,) a) bs
Hence with some eta reduction and other point-free techniques, we could transition this way:
concatsplit = concatMap (uncurry (\a bs -> map ((,) a) bs))
= concatMap (uncurry (\a -> map ((,) a)))
= concatMap (uncurry (map . (,)))
But, personally, I find this less readable than where I declared I would stop above, namely:
concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)
You can also use the monad instance for lists:
concatSplit l = l >>= \(a,x) -> x >>= \b -> return (a,b)
Which can be simplified to:
concatSplit l = l >>= \(a,x) -> map ((,) a) x
And reformatted to do
notation
concatSplit l = do
(a,x) <- l
map ((,) a) x
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.