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

时间:2019-03-07 19:44:12

标签: list haskell split tuples concat

我想合并一个由元素和字符串组成的元组的列表,每个char与该元素在一起。

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

4 个答案:

答案 0 :(得分:8)

为什么没有简单的列表理解?

列表推导通常可以完成高阶函数,而且我认为,如果您不需要更改数据,而只需“解压缩”数据,它们就很清楚了。这是一个工作示例:

concS :: [(a,[b])] -> [(a,b)]
concS ls = [(a,b) | (a,x) <- ls, b <- x]

答案 1 :(得分:3)

sequenceA非常简单:

concatsplit = concatMap sequenceA

或将其进一步推广:

concatsplit = (>>= sequenceA)

详细信息:

  • sequenceA的类型为(Applicative f, Traversable t) => t (f a) -> f (t a)。这意味着,如果您的类型在“外部”上带有Traversable,在“内部”上带有Applicative,则可以在其上调用sequenceA将其内外翻,因此Applicative位于“外部”,而Traversable位于“内部”。
  • 在这种情况下,(True, "xy")的类型为(Bool, [Char]),其类型为(,) Bool ([] Char)Traversable的{​​{1}}中有an instance((,) a)的{​​{1}}中有an instance。因此,您可以在其上调用Applicative,其结果将为[]类型或sequenceA加糖。
  • 事实证明,[] ((,) Bool Char)不仅具有有用的类型,而且它确实满足您在此需要的事情(并且实际上与[(Bool, Char)]等效)。
  • sequenceA的类型为concatsplit'concatMap的类型为Foldable t => (a -> [b]) -> t a -> [b]。当专门用于列表时,除了参数相反的顺序之外,其他内容都相同。

答案 2 :(得分:3)

其他答案显示了如何从头开始习惯地执行此操作,对此我非常喜欢。展示如何完善现有内容也可能很有趣。再次提醒您:

  using (var client = new HttpClient())
     {
                        string query;
                        using (var content = new FormUrlEncodedContent(new Dictionary<string, string>()
                        {
                            {"username", username},
                            {"password", password}
                        }))
                        {
                            query = content.ReadAsStringAsync().Result;
                        }

                        var model = new{
                            username = txtUsername.Text,
                            password = txtPassword.Text
                        };
                        var json = JsonConvert.SerializeObject(model);
                        var user = new StringContent(json, Encoding.UTF8, "application/json");

                        using (var response = await client.PostAsync(@"http://localhost/dataagent/api/user/authenticate", user))
                        {
                            if (response.IsSuccessStatusCode)
                            {
                                var result = await response.Content.ReadAsStringAsync();
                                // handle result here

                            }
                        }
}

我要不断更改的第一件事称为“ eta缩减”,它是将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

如果您真的想练习减少eta,您可能会注意到concatsplit' (a,bs) = map (\x -> (a,x)) bs 可以加前缀并将其更改为

(,)

但是我认为我也很高兴。在这一点上,这个定义很小,我很想将其内联到concatsplit' (a,bs) = map (\x -> (,) a x) bs = map ((,) a) bs 本身,得到:

concatsplit

对我来说,这似乎是一个很好的定义,我会停在那里。

可能在这里几乎被eta-减少所困扰:放下concatsplit = concatMap (\(a,bs) -> map ((,) a) bs) 几乎是正确的形状。高级用户可能会继续,并注意:

bs

因此,通过一些eta减少和其他无点技术,我们可以采用这种方式过渡:

uncurry (\a bs -> map ((,) a) bs) = \(a,bs) -> map ((,) a) bs

但是,就我个人而言,我发现它的可读性不如我在上面停下来的地方,即:

concatsplit = concatMap (uncurry (\a bs -> map ((,) a) bs))
            = concatMap (uncurry (\a -> map ((,) a)))
            = concatMap (uncurry (map . (,)))

答案 3 :(得分:1)

您还可以将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