[英]Haskell: concat a list of tuples with an element and a list: [(a,[b])] -> [(a,b)]
我想在元素和字符串的元组列表中连接,每个字符都包含元素。
例如:[(True,“xy”),(False,“abc”)]-> [(True,'x'),(True,'y'),(False,'a'),(False , 'b'),(假, 'C')]
我确实有一个解决方案,但我想知道是否有更好的解决方案:
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)
列表推导通常可以与更高阶的函数一样多,我认为如果你不需要更改数据但只需“解包”它,它们就非常清楚了。 这是一个有效的例子:
concS :: [(a,[b])] -> [(a,b)]
concS ls = [(a,b) | (a,x) <- ls, b <- x]
sequenceA
让它变得非常简单:
concatsplit = concatMap sequenceA
或者进一步概括:
concatsplit = (>>= sequenceA)
细节:
sequenceA
具有类型(Applicative f, Traversable t) => t (fa) -> f (ta)
。 这意味着如果你在“外部”上有一个Traversable
类型,在“inside”上有一个Applicative
,你可以调用它上面的sequenceA
来将它从里面翻出来,这样Applicative
就在“outside”上面了Traversable
在“内部”。 (True, "xy")
具有类型(Bool, [Char])
,它取消了(,) Bool ([] Char)
。 ((,) a)
有一个 Traversable
实例 ,并且[]
有一个 Applicative
实例 。 因此,你可以在它上面调用sequenceA
,结果将是[] ((,) Bool Char)
,或者带糖的[(Bool, Char)]
。 sequenceA
不仅有一个有用的类型,而且它完全符合你需要的东西(事实证明它完全等同于concatsplit'
)。 concatMap
类型为Foldable t => (a -> [b]) -> ta -> [b]
。 (>>=)
类型Monad m => ma -> (a -> mb) -> mb
。 当专门用于列表时,它们变得相同,除了它们的参数顺序相反。 其他答案显示了如何从头开始惯用这个,我非常喜欢。 展示你如何改进你已经拥有的东西可能也很有趣。 这里再次提醒一下:
concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a
concatsplit' y = map (\x -> ((fst y),x)) (snd y)
我会一直改变的第一件事被称为“埃塔减少”,这是当你把东西形状的\\x -> foo x
逼到foo
。 我们可以在concatMap
的参数中做到这一点
concatsplit a = concatMap concatsplit' a
然后在concatsplit
的争论中再次获得:
concatsplit = concatMap concatsplit'
看看concatsplit'
,我最不喜欢的是使用fst
和snd
而不是模式匹配。 使用模式匹配,它看起来像这样:
concatsplit' (a,bs) = map (\x -> (a,x)) bs
如果你真的想练习你的eta减少,你可能会注意到(,)
可以应用前缀并将其更改为
concatsplit' (a,bs) = map (\x -> (,) a x) bs
= map ((,) a) bs
但我觉得我和其他人一样快乐。 在这一点上,这个定义足够小,我很想把它内联到concatsplit
本身,得到:
concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)
这看起来对我来说是一个非常好的定义,我会在那里停下来。
你可能会被这里几乎减少的东西所困扰:它几乎是放弃bs
的正确形状。 高级用户可能会继续,注意到:
uncurry (\a bs -> map ((,) a) bs) = \(a,bs) -> map ((,) a) bs
因此,通过一些eta减少和其他无点技术,我们可以通过这种方式进行转换:
concatsplit = concatMap (uncurry (\a bs -> map ((,) a) bs))
= concatMap (uncurry (\a -> map ((,) a)))
= concatMap (uncurry (map . (,)))
但是,就个人而言,我发现这比我声明我会停在上面的地方更不易读,即:
concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)
您还可以将monad实例用于列表:
concatSplit l = l >>= \(a,x) -> x >>= \b -> return (a,b)
哪个可以简化为:
concatSplit l = l >>= \(a,x) -> map ((,) a) x
并重新格式化,以do
记号
concatSplit l = do
(a,x) <- l
map ((,) a) x
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.