Haskell函数,该函数接收具有不同类型的列表,并且仅返回整数列表

时间:2019-04-23 17:14:59

标签: haskell types

我有这样的功能:

[(String, [Int], Int)]

我想要一个只返回[Int]列表的函数。

输入示例:

[("R1",[6,10,14],6),("R2",[8,10,14],8),("R3",[11,15,24],11)]

输出示例(我想要的输出行为):

[6,8,10,11,14,15,24]

我一直在考虑创建输入函数,例如:

function :: [(String, [Int], Int)]
function = [("R1",[6,10,14],6),("R2",[8,10,14],8),("R3",[11,15,24],11)]

另一个转换成我想要的东西,像这样:

convert :: [(String, [Int], Int)] -> [Int]
convert [] = []
...

如何开发功能convert

1 个答案:

答案 0 :(得分:1)

我想做的第一件事是布置我认为需要的所有类型签名:

-- Something to use with fmap to get rid of the Strings
f :: (String, [Int], Int) -> ([Int], Int)

-- Something to cons the list with the Int
g :: ([Int], Int) -> [Int]

-- Our final goal:
convert :: [(String, [Int], Int)] -> [Int]

现在,我想考虑如何将它们组合在一起以获得最终结果:

fmap g . fmap f :: [(String, [Int], Int)] -> [[Int]]

感谢functor laws,您实际上可以将其重写为:

fmap (g . f) :: [(String, [Int], Int)] -> [[Int]]

那非常接近,但是我们有两个嵌套列表,而不是一个。幸运的是列表是monoid,这意味着我们可以使用mconcat :: [a] -> a对其进行折叠。这有点令人困惑,因为mconcat对单等a值列表进行操作。在我们的案例中,mconcat的更具体的签名是:

mconcat :: [[Int]] -> [Int]

内部列表是monoid mappending所在的地方。

无论如何,允许我们做的是这样写:

mconcat . fmap (g . f) :: [(String, [Int], Int)] -> [Int]

我们正试图实现的功能到底是什么?

convert :: [(String, [Int], Int)] -> [Int]
convert = mconcat . fmap g . fmap f
    where f = undefined
          g = undefined

现在我们只需要实现fg

f :: (String, [Int], Int) -> ([Int], Int)
f (_, xs, x) = (xs, x)

g :: ([Int], Int) -> [Int] 
g (xs, x) = x:xs

g' :: ([Int], Int) -> [Int] 
g' (xs, x) = xs ++ [x]

根据您希望x进入哪个位置,可以使用gg'

然后,您的最终实现将类似于(将sort添加到合成中以匹配示例结果)

convert :: [(String, [Int], Int)] -> [Int]
convert = sort . mconcat . fmap g . fmap f
    where f (_, xs, x) = (xs, x)
          g (xs, x) = x:xs

回头看看您的问题,似乎三元组的第三个元素始终是第二个元素的成员?如果是这样,此功能将变得更加简单:

convert :: [(String, [Int], Int)] -> [Int]
convert = sort . mconcat . fmap f
    where f (_, xs, ) = xs

如果由于最后的排序步骤而担心性能,则可以使用foldr而不是mconcat将其与串联结合起来,然后在a -> b -> b中进行排序。