【问题标题】:Replacing explicit recursion with higher-order functions用高阶函数替换显式递归
【发布时间】:2013-10-10 15:55:54
【问题描述】:

我有以下代码,旨在获取 a 的列表和 b 的列表,并返回所有配对 [(a, b)],这样

  • 每个a 和每个b 在每个配对中只出现一次。
  • 每一对 (a, b) 满足某些条件 cond,即 cond :: a -> b -> Bool

例如,列表 [1, 2] [x,y,z] 的结果应该是

[[(1, x), (2, y)]
 [(1, x), (2, z)]
 [(1, y), (2, x)]
 [(1, y), (2, z)]
 [(1, z), (2, x)]
 [(1, z), (2, y)]]

这是一些(有些抽象的)代码,它们通过显式递归来完成这项工作,但我想用折叠或类似的东西替换它。有什么建议吗?

someFn :: [a] -> [b] -> [ [(a, b)] ]
someFn [] _ = []
someFn (a : as) bs = [ [(a,b)] ++ rest | b <- bs, rest <- someFn as (bs \\ [b]), cond a b]

【问题讨论】:

  • [(a, b)] 是对列表,因此您的函数返回的是对列表而不是对列表。
  • “每个 a 和每个 b 在每个配对中只出现一次” - 好吧,这可能是因为英语不是我的第一语言,但这对于任何一对(元组)来说都是如此(a ,b)? a 最多出现一次,b 最多出现一次。
  • 你的目标不是很清楚。你想用折叠替换它还是问如何使someFn 更高阶?如果是后者,高阶就什么? cond 谓词?
  • foo x y with = filter (uncurry with) [(a,b) | a
  • 我看到你正在处理第二个列表中的重复项,但是第一个呢?

标签: haskell recursion


【解决方案1】:

从您的解释中我可以理解的是,您想根据两个列表的乘积的某些条件进行过滤。使用列表推导很容易得到列表的乘积,然后过滤器函数会将乘积减少为仅满足给定条件的对

foo :: [a] -> [b] -> (a -> b -> Bool)-> [(a,b)]
foo x y with = filter (uncurry with) [(a,b) | a <- x, b <- y] 

[根据编辑更新]

这会产生你想要的列表(希望如此)

bar :: [a] -> [b] -> [[(a,b)]]
bar xs ys = map (zip xs) $ permutations ys

根据给定条件进行过滤

biz :: (a -> b -> Bool) -> [[(a,b)]] -> [[(a,b)]]
biz = map . filter . uncurry

【讨论】:

  • 我对原始问题进行了一些修改,希望能更清楚地说明问题。原始输出类型 [ [(a,b)] ] 是正确的,是处理重复问题所必需的。
【解决方案2】:

您可以使用foldr 重构您的代码,如下所示:

delete :: Int -> [a] -> [a]
delete index xs = let (ys, _:zs) = splitAt index xs in ys ++ zs

ifoldr :: (Int -> a -> b -> b) -> b -> [a] -> b
ifoldr f acc xs = foldr (\(a, b) c -> f a b c) acc $ zip [0..] xs

someFn :: (a -> b -> Bool) -> [a] -> [b] -> [[(a,b)]]
someFn _ [] _ = [[]]
someFn cond (a:as) bs = ifoldr (\index b acc -> if cond a b
    then concat [map ((a,b):) . someFn cond as $ delete index bs, acc]
    else acc) [] bs

注意边缘情况是someFn _ [] _ = [[]],这符合函数someFn :: (a -&gt; b -&gt; Bool) -&gt; [a] -&gt; [b] -&gt; [[(a,b)]]的类型定义。

您可以按如下方式使用someFn

someFn (\a b -> True) [1,2] "xyz"

-- [[(1,'x'),(2,'y')],
--  [(1,'x'),(2,'z')],
--  [(1,'y'),(2,'x')],
--  [(1,'y'),(2,'z')],
--  [(1,'z'),(2,'x')],
--  [(1,'z'),(2,'y')]]

someFn (\a b -> case (a,b) of (1,'x') -> False
                              (2,'y') -> False
                              otherwise -> True) [1,2] "xyz"

-- [[(1,'y'),(2,'x')],
--  [(1,'y'),(2,'z')],
--  [(1,'z'),(2,'x')]]

希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 2021-03-13
    • 2014-01-22
    • 2016-02-22
    • 2014-08-16
    • 2021-10-24
    • 2020-04-04
    • 1970-01-01
    • 2021-11-01
    • 2018-06-24
    相关资源
    最近更新 更多