Haskell 元组列表与元组比较

Haskell list of tuples vs tuple comparrison

我正在尝试 Haskell 中的第一个不平凡的(在我看来)尝试。我可能会问关于每个部分的问题,以比较我与 C 类语言建立长期关系的尝试与您作为经验丰富的函数式程序员可能会做的事情。幸运的是,Haskell 很难退回到直接的 c 到 haskell 代码转换。你必须学习如何正确地做事 - 我想。

对于这部分,我有一个 [2uple] 和一个 2uple。我想知道 2uple 中的任何项目是否在 [2uple] 中的任何项目中。

hasmatch tlst tupm = foldr (\tup acc -> 
                                let tmatch (a,b) (c,d) = a==c || b==c || a==d || b==d in
                                if tmatch tup tupm
                                then (Just True)    -- short out
                                else acc            -- do nothing, keep looping
                          ) Nothing tlst

-- hasmatch [(1,2), (3,4), (5,6)] (5,3) ==> Just True
-- hasmatch [(1,2), (9,4), (7,6)] (5,3) ==> Nothing

我更喜欢 return 一个普通的 Bool,但没什么大不了的。

我敢肯定还有另一种方法我需要花上一段时间才能理解。那很好。正在寻找接受者。

谢谢

让我们从您的代码开始。

hasmatch tlst tupm =
   foldr (\tup acc -> 
          let tmatch (a,b) (c,d) = a==c || b==c || a==d || b==d in
          if tmatch tup tupm
          then (Just True)    -- short out
          else acc            -- do nothing, keep looping
         ) Nothing tlst

既然你说你更喜欢布尔结果,我们可以切换到那个。事实上,布尔值会更好,因为在上面的代码中我们从来没有 return Just False.

hasmatch tlst tupm =
   foldr (\tup acc -> 
          let tmatch (a,b) (c,d) = a==c || b==c || a==d || b==d in
          if tmatch tup tupm
          then True    -- short out
          else acc     -- do nothing, keep looping
         ) False tlst

现在,if x then True else y 就是 x || y

hasmatch tlst tupm =
   foldr (\tup acc -> 
          let tmatch (a,b) (c,d) = a==c || b==c || a==d || b==d in
          tmatch tup tupm || acc
         ) False tlst

如果您想知道,当我们找到匹配项时,|| 不会计算 acc。这是与 C 短路相同的惰性语义。与 C 相比,主要区别在于布尔变量 acc 可以表示 未计算的 布尔结果,而在 C 中它始终是完全计算的布尔值。

现在,tmatch 使用相同的第二个参数,tupm,所以我们可以内联它,并尽早进行模式匹配:

hasmatch tlst (c,d) =
   foldr (\tup acc -> 
          let tmatch (a,b) = a==c || b==c || a==d || b==d in
          tmatch tup || acc
         ) False tlst

我们甚至可以将 let 移到外面以提高可读性。

hasmatch tlst (c,d) =
   let tmatch (a,b) = a==c || b==c || a==d || b==d 
   in foldr (\tup acc -> tmatch tup || acc) False tlst

我们也可以在这里使用where

hasmatch tlst (c,d) = foldr (\tup acc -> tmatch tup || acc) False tlst
   where tmatch (a,b) = a==c || b==c || a==d || b==d

最后,我们可以利用一个库函数:any。调用 any p list 计算结果为 True 当且仅当 list 中有任何元素满足 属性 p。我们的代码变为:

hasmatch tlst (c,d) = any tmatch tlst
   where tmatch (a,b) = a==c || b==c || a==d || b==d

就是这样。

请注意,上述方法还有其他替代方法。可以将元组列表 [(a,b), (c,d), ... 转换为所有组件的列表 [a,b,c,d,...,然后使用它。

hasmatch tlst (c,d) = any tmatch [ x | (a,b) <- tlst, x <- [a,b]]
   where tmatch x = x==c || x==d

根据 chi 的回答,您最好定义具有这种相等性的数据类型?

{-# Language DerivingStrategies       #-}
{-# Language InstanceSigs             #-}
{-# Language StandaloneKindSignatures #-}

import Data.Kind (Type)

type AnyTuple :: Type -> Type
data AnyTuple a = a :×: a
  deriving stock Show

instance Eq a => Eq (AnyTuple a) where
  (==) :: AnyTuple a -> AnyTuple a -> Bool
  a1:×:b1 == a2:×:b2 = or $ liftA2 (==) [a1, b1] [a2, b2]

那么你要找的函数就是一个标准的库函数,比如elem or find

>> 5:×:3 `elem` [1:×:2, 3:×:4, 5:×:6]
True
>> 5:×:3 `elem` [1:×:2, 9:×:4, 7:×:6]
False

>> find (== 5:×:3) [1:×:2, 3:×:4, 5:×:6]
Just (3 :×: 4)
>> find (== 5:×:3) [1:×:2, 9:×:4, 7:×:6]
Nothing