我有这样的功能:
[(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
?
答案 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
现在我们只需要实现f
和g
:
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
进入哪个位置,可以使用g
或g'
。
然后,您的最终实现将类似于(将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
中进行排序。