简体   繁体   English

Haskell:使用元素和列表连接元组列表:[(a,[b])] - > [(a,b)]

[英]Haskell: concat a list of tuples with an element and a list: [(a,[b])] -> [(a,b)]

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')] 例如:[(True,“xy”),(False,“abc”)]􏰁-> [(True,'x'),(True,'y'),(False,'a'),(False , 'b'),(假, '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)

Why not a simple list comprehension? 为什么不简单列表理解?

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: sequenceA让它变得非常简单:

concatsplit = concatMap sequenceA

Or generalize it even further: 或者进一步概括:

concatsplit = (>>= sequenceA)

Details: 细节:

  • sequenceA has type (Applicative f, Traversable t) => t (fa) -> f (ta) . sequenceA具有类型(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". 这意味着如果你在“外部”上有一个Traversable类型,在“inside”上有一个Applicative ,你可以调用它上面的sequenceA来将它从里面翻出来,这样Applicative就在“outside”上面了Traversable在“内部”。
  • In this case, (True, "xy") has type (Bool, [Char]) , which desugars to (,) Bool ([] Char) . 在这种情况下, (True, "xy")具有类型(Bool, [Char]) ,它取消了(,) Bool ([] Char) There's an instance of Traversable for ((,) a) , and there's an instance of Applicative for [] . ((,) a)一个 Traversable 实例 ,并且[]一个 Applicative 实例 Thus, you can call sequenceA on it, and the result will be of type [] ((,) Bool Char) , or [(Bool, Char)] with sugar. 因此,你可以在它上面调用sequenceA ,结果将是[] ((,) Bool Char) ,或者带糖的[(Bool, Char)]
  • As it turns out, not only does sequenceA have a useful type, but it does exactly the thing you need here (and turns out to be exactly equivalent to concatsplit' ). 事实证明, sequenceA不仅有一个有用的类型,而且它完全符合你需要的东西(事实证明它完全等同于concatsplit' )。
  • concatMap has type Foldable t => (a -> [b]) -> ta -> [b] . concatMap类型为Foldable t => (a -> [b]) -> ta -> [b] (>>=) has type Monad m => ma -> (a -> mb) -> mb . (>>=)类型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 . 我会一直改变的第一件事被称为“埃塔减少”,这是当你把东西形状的\\x -> foo x逼到foo We can do this in the argument to concatMap to get 我们可以在concatMap的参数中做到这一点

concatsplit a = concatMap concatsplit' a

and then again in the argument to concatsplit to get: 然后在concatsplit的争论中再次获得:

concatsplit = concatMap concatsplit'

Looking at concatsplit' , the thing I like least is the use of fst and snd instead of pattern matching. 看看concatsplit' ,我最不喜欢的是使用fstsnd而不是模式匹配。 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 如果你真的想练习你的eta减少,你可能会注意到(,)可以应用前缀并将其更改为

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本身,得到:

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 . 可能会被这里几乎减少的东西所困扰:它几乎是放弃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: 因此,通过一些eta减少和其他无点技术,我们可以通过这种方式进行转换:

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: 您还可以将monad实例用于列表:

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 并重新格式化,以do记号

concatSplit l = do
  (a,x) <- l
  map ((,) a) x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM