【问题标题】:Is there a standard way to match 2 lists with a custom matching function in Haskell?是否有一种标准方法可以将 2 个列表与 Haskell 中的自定义匹配函数进行匹配?
【发布时间】:2013-11-05 16:57:16
【问题描述】:

我知道标准方法是

(Eq z) => matchLists :: [x] -> [x] -> Bool
matchLists xs ys = xs == ys

但是我对从外部传递的元素有一个特殊的匹配函数,我无法控制它。

所以我要找的是

matchLists :: (x -> x -> Bool) -> [x] -> [x] -> Bool

(胡格尔说no

您最终会得到一个带有这样签名的自定义函数吗?或者您会怎么做?

编辑:

zip 函数不能满足我的需要,因为结果列表在 2 个输入列表中具有最小长度

编辑:

你怎么看?

--matchListsWith :: (a -> a -> Bool) -> [a] -> [a] -> Bool
matchListsWith :: (a -> b -> Bool) -> [a] -> [b] -> Bool
matchListsWith _ [] [] = True
matchListsWith _ (_:_) [] = False
matchListsWith _ [] (_:_) = False
matchListsWith matcher (x:xs) (y:ys) = matcher x y && matchListsWith matcher xs ys

【问题讨论】:

  • matchListsWith 没有理由不拥有更通用的类型(a -> b -> Bool) -> [a] -> [b] -> Bool
  • @Cirdec,这很好

标签: haskell


【解决方案1】:

使用Data.Align,我们可以同时处理压缩和长度问题

matchWith :: (a -> b -> Bool) -> [a] -> [b] -> Bool
matchWith f as bs = and $ alignWith combiner as bs where
  combiner = these (const False) (const False) f

这将展开为与显式递归函数相同的代码,但使用来自Data.These 的标签来标记各种列表对齐方式。如果您推广and,它还可以推广到许多其他结构,如树或序列。

matchWith :: (Foldable f, Align f) => (a -> b -> Bool) -> f a -> f b -> Bool
matchWith f as bs = Foldable.and $ alignWith combiner as bs where
  combiner = these (const False) (const False) f

data Tree a = Tip | Branch a (Tree a) (Tree a) deriving ( Functor, Foldable )

instance Align Tree where
  nil = Tip
  align Tip Tip = Tip
  align (Branch a la ra) Tip = Branch (This a) (fmap This la) (fmap This ra)
  align Tip (Branch b lb rb) = Branch (That b) (fmap That lb) (fmap That rb)
  align (Branch a la ra) (Branch b lb rb) =
    Branch (These a b) (align la lb) (align ra rb)

所以我们有

λ> matchWith (==) Tip Tip
True
λ> matchWith (==) (Branch 3 Tip Tip) (Branch 3 Tip Tip)
True
λ> matchWith (==) (Branch 3 Tip Tip) (Branch 3 Tip (Branch 3 Tip Tip))
False

(也可以……)

instance Eq a => Eq (Tree a) where (==) = matchWith (==)

【讨论】:

    【解决方案2】:

    我认为手写的方法在这里非常好。如果您还没有使用恰好具有解决此问题的合适函数的库,那么我认为仅仅为了减少三行代码而添加另一个依赖项是不值得的。

    你仍然可以剃掉一条线:

    matchListWith :: (a -> b -> Bool) -> [a] -> [b] -> Bool
    matchListWith f (x:xs) (y:ys) = f x y && matchListWith f xs ys
    matchListWith _ []     []     = True
    matchListWith _ _      _      = False
    

    【讨论】:

      【解决方案3】:

      一种可能性:

      matchList f xs ys = and $ zipWith f xs ys
      

      【讨论】:

      • 如果列表的长度不同,这可能会导致令人惊讶的行为。 and $ zipWith (==) [1] [1,2] 计算结果为 True
      • 我也是这么想的。我不希望它是真实的。所以我想我需要继续寻找。
      猜你喜欢
      • 2013-04-11
      • 1970-01-01
      • 1970-01-01
      • 2017-04-29
      • 2018-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多