首页 > 解决方案 > 根据条件对列表进行分区的功能 - Haskell

问题描述

我正在学习 Haskell,我正在做的一个练习是创建一个函数,该函数根据函数的返回值将列表划分为三个列表,以便为等于 1 的函数值获得第一个子列表,函数值的第二个子列表为 2,第三个子列表包含其他所有内容。

函数声明读取

partition3 :: (a -> Int) -> [a] -> ([a], [a], [a])
partition3 _ [] = []

作为一个入门示例,请考虑

partition3 (\x -> mod x 5) [1..10] = ([1,6], [2,7], [3,4,5,8,9,10])

现在,我已经编写了函数filterAny, isSuitable, removeIf, isNotSuitable。完整代码如下:

filterAny :: [a -> Bool] -> [a] -> [a]
filterAny _ [] = []
filterAny ps (x:xs)
  | isSuitable ps x = x : filterAny ps xs
  | otherwise = filterAny ps xs

isSuitable :: [a -> Bool] -> a -> Bool
isSuitable [] _ = False
isSuitable (p:ps) v
  | p v       = True
  | otherwise = isSuitable ps v

removeIf :: [a -> Bool] -> [a] -> [a]
removeIf _ [] = []
removeIf ps (x:xs)
  | isNotSuitable ps x = x : removeIf ps xs
  | otherwise = removeIf ps xs

isNotSuitable :: [a -> Bool] -> a -> Bool
isNotSuitable [] _ = True
isNotSuitable (p:ps) v
  | p v       = False
  | otherwise = isNotSuitable ps v

使用这些函数,我们可以获得所需的输出partition3

filterAny [(\x -> mod x 5 == 1)] [1..10] = [1,6]
filterAny [(\x -> mod x 5 == 2)] [1..10] = [2,7]
removeIf [even, (\x -> x > 5)] [1..10] = [3,4,5,8,9,10]

不幸的是,我仍然没有想出如何编写这个函数来输出([1,6], [2,7], [3,4,5,8,9,10])

标签: haskell

解决方案


考虑“创建”类型值的一种高级方法(a,b,c)是考虑必须“构造”它的方式,例如构造函数。在这种情况下,(a,b,c)有一个构造函数,(_,_,_). 因此,您需要考虑要放入第一个插槽的东西,要放入第二个插槽的东西,以及要放入第三个插槽的东西……您就完成了。

所以让我们这样概述我们的功能:

partition3 :: (a -> Int) -> [a] -> ([a], [a], [a])
partition3 f xs = (_, _, _)

我们知道我们的最终答案是形式(_,_,_),其中“空白”包含我们想要放入元组的每个项目。让我们给每个空白一个有用的名字。

partition3 :: (a -> Int) -> [a] -> ([a], [a], [a])
partition3 f xs = (equalsOne, equalsTwo, theRest)
  where
    equalsOne = ???
    equalsTwo = ???
    theRest   = ???

现在您只需要equalsOne成为与您想要的属性相匹配的项目xs,等等。属性equalsOne是“一个值x匹配,如果当您应用f它时,它给出了1”。所以让我们用filterAny

partition3 :: (a -> Int) -> [a] -> ([a], [a], [a])
partition3 f xs = (equalsOne, equalsTwo, theRest)
  where
    equalsOne = filterAny [\x -> f x == 1] xs
    equalsTwo = ???
    theRest   = ???

在这里,我们过滤xs任何项目,如果你申请f它,给出1.

你能想出方法来实现equalsTwotheRest,沿着同样的路线吗?


推荐阅读