按最大元素过滤元组列表

时间:2014-07-23 21:56:47

标签: haskell

userDatabase是一个由元组组成的列表,例如(id,username,password)。如何使用最大ID过滤列表?我现在所做的只是做了什么,但我认为我把它包含在某些背景下。

newUser :: UserDatabase -> UserDatabase
newUser usrdb = filter (\(usrid,_,_) -> usrid == (maximum [x | (x, _, _) <- userdb])) userdb

提前致谢!

3 个答案:

答案 0 :(得分:3)

所有的usrid值都是不同的吗?在这种情况下,只有一个最大值,因此您可以执行以下操作:

type User = (Int, String, String)
type UserDatabase = [User]

fst3 (x, _y, _z) = x

userHigh :: UserDatabase -> User
userHigh = maximumBy (comparing fst3)

如果ID不明显,您可能会有一些联系,因此您想要返回一个列表

usersHigh :: UserDatabase -> [User]
usersHigh = concat . take 1
          . groupBy (on (==) fst3)
          . sortBy (flip $ comparing fst3)

答案 1 :(得分:1)

未经测试,但应该让你去:

maximumBy :: Ord b => (a -> b) -> [a] -> Maybe a
maximumBy extract [] = Nothing
maximumBy extract (x:xs) = go x xs
    where go candidate [] = Just candidate
          go candidate (x:xs)
              | extract x > extract candidate = go x xs
              | otherwise = go candidate xs

编辑:哦,bhelkir指出这个功能已经存在于库中。无论如何,有几点:

  1. 如果您尝试从列表中只获取一个项目,则不是filter操作,而是更像findminimum / maximum操作。
  2. 你使用它的方式是这样的:maximumBy (\(usrid,_,_) -> usrid) usersmaximumBy的第一个参数是一个从列表元素中提取比较字段的函数。

答案 2 :(得分:0)

我有一个类型[(X, (R, R))]的列表(元组列表,其中2 nd 元素也是一个元组),其中XR是一些任意类型,R是类型类Ord的成员。此类列表的示例:[(0, (0,1)), (1, (1,1)), (2, (0,0)), (3, (1,0))]。想象一下,我想找到这个列表的所有元素,其中第二个元素的第一个元素(即内部元组的左元素)是最大的。但我不想要一个这样的元素,我想要所有元素,其中内部对的左元素是最大的。因此,我在上面的列表中调用的函数应该返回[(1, (1,1)), (3, (1,0))]maximumBy不会起作用,因为它只会返回一个元素。所以我写了maximumBy的变体,它返回多个最大值:

maximumByM :: (a -> a -> Ordering) -> [a] -> [a]
maximumByM c (x:xs) = maximumByM' c xs [x]
  where maximumByM' _ [] acc = acc
        maximumByM' c (x:xs) acc@(a:_) = case x `c` a of
          LT -> maximumByM' c xs acc
          EQ -> maximumByM' c xs (x:acc)
          GT -> maximumByM' c xs [x]

后缀-M表示“多个”。此多值函数返回所有最大值的列表(根据自定义比较):

maximumByM compare [1,2,3] -- [3]
maximumByM compare [1,2,3,1,2,3] -- [3,3]
maximumByM (comparing fst) [(5,1), (5,2), (6,1), (6,2)] -- [(6,2),(6,1)]
maximumByM (comparing snd) [(5,1), (5,2), (6,1), (6,2)] -- [(6,2),(5,2)]

要实施minimumByM,您需要做的就是flip比较器,因为compare 1 2 == (flip compare) 2 1

maximumByM (flip $ comparing fst) [(5,1), (5,2), (6,1), (6,2)] -- [(5,2),(5,1)]
-- the below 2 are equivalent due to Haskell's currying
minimumByM = maximumByM . flip
minimumByM c xs = maximumByM (flip c) xs

不幸的是maximumByM以相反的顺序返回列表。我可以在代码中添加reverse,但是这个双重工作?明确的递归?为什么不使用right fold实现相同的功能:

maximumByM :: (a -> a -> Ordering) -> [a] -> [a]
maximumByM _ [] = []
maximumByM c (x:xs) = foldr f [x] xs
  where f = (\e acc@(a:_) -> case c e a of
              LT -> acc
              EQ -> e:acc
              GT -> [e])

现在它以与原始列表中找到的元素相同的顺序返回列表:

maximumByM (comparing fst) [(5,1), (5,2), (6,1), (6,2)] -- [(6,1),(6,2)]

当然,您可以使用Boyd Stephen Smith Jr的方法,将groupBysortBy结合起来(sortBy需要flip,因为它从小分类大):

maximumByM :: (a -> a -> Ordering) -> [a] -> [a]
maximumByM _ [] = []
maximumByM c xs = head $ groupBy ((==EQ) .: c) $ sortBy (flip c) xs
  where (.:) = (.).(.) -- x .: y = \a b -> x(y a b)

您不需要撤销列表,因为sortBy将保留其认为相等的元素的顺序:

maximumByM (comparing fst) [(5,1), (5,2), (6,1), (6,2)] -- [(6,1),(6,2)]