【问题标题】:Haskell list of tuples vs tuple comparrisonHaskell 元组列表与元组比较
【发布时间】:2022-01-11 01:52:04
【问题描述】:

我正在玩我在 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

我更愿意返回一个普通的 Bool 但没有什么大不了的。

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

谢谢

【问题讨论】:

  • 什么意思,元组不是必需的?你有一对东西和一对列表吗?还是你有别的事?
  • @amalloy 我删除了令人困惑的行...
  • 在任何支持配对的语言中(CHaskellBasicALGOL、...):(5,3) /= (3,5)。这确实是元组的重点。那么,为什么在您的==> Just True 结果中,您希望将列表中元组右侧位置的3 与列表中元组左侧位置的3 匹配?你的名单不就是Eq a => [a]吗?如果您在C 中执行此操作,“业务/需求方案”会是什么?
  • 如果元组中的位置真的无关紧要:将tlst 中的元组展平为一个裸元素列表;同样将 tupm 中的这对扁平化为 2 元素列表;寻找扁平化tupm 是扁平化tlst 的子集。 (寻找一个列表的一个子集是一个常见问题解答。)
  • 如果唯一可能的输出是Just TrueNothing,为什么要返回Maybe Bool?为什么不分别返回True/False

标签: haskell tuples comparison


【解决方案1】:

让我们从您的代码开始。

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

既然你说你更喜欢布尔结果,我们可以切换到那个。事实上,布尔值会更好,因为在上面的代码中我们从不返回 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。如果list 中有任何满足属性p 的元素,则调用any p list 计算结果为True。我们的代码变成:

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

【讨论】:

  • 这真的很棒。一步一步的改进。我错过了一些简单的东西(除了真/假的东西)的一个例子是我读过any。阅读不会让学习陷入困境,我实际上希望有一些可能会丢失foldr 的东西,只是为了向我展示另一种选择。感谢您抽出宝贵时间。
【解决方案2】:

根据 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或者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

【讨论】:

  • 这肯定会派上用场。我确实有一些关于朝这个方向前进的想法,但从头开始,在这一点上有点超出我的范围。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-23
  • 2012-11-25
  • 1970-01-01
  • 1970-01-01
  • 2015-08-01
  • 1970-01-01
相关资源
最近更新 更多