Haskell 比较两个列表的长度,但其中一个是无限的?

3t8*_*93w 26 haskell functional-programming

我想编写一个函数来检查第一个列表是否比第二个列表长,并且其中一个列表可以是无限的。但是我找不到可行的解决方案。

isLonger :: [a] -> [b] -> Bool
isLonger a b 
        | listLength a > listLength b = True
        |otherwise = False

listLength :: [a] -> Integer
listLength = foldr (flip $ const . (+1)) 0
Run Code Online (Sandbox Code Playgroud)

K. *_*uhr 44

@dfeuer 的解决方案赢得了聪明,但就更典型的解决方案而言......

评论中提到的一种合理方法是并行处理两个列表,并找出哪个列表先“用完”——这是较短的列表。一个简单的解决方案是在两个列表都非空时进行递归:

isLonger :: [a] -> [b] -> Bool
isLonger (x:xs) (y:ys) = isLonger xs ys
Run Code Online (Sandbox Code Playgroud)

并在其中一个列表变空时立即返回答案:

isLonger []     _  = False   -- list a <= list b
isLonger _      [] = True    -- list a > list b
Run Code Online (Sandbox Code Playgroud)

如果两个列表同时用完(长度相等),则第一个模式将匹配,以便在平局时确定答案。


dfe*_*uer 21

普通的旧自然数无法解决这个问题,因为您无法在有限的时间内计算出无限列表的自然数长度。然而,惰性自然数可以做到这一点。

import Data.Function (on)

data Nat = Z | S Nat
  deriving (Eq, Ord)

len :: [a] -> Nat
len = foldr (const S) Z

isLonger :: [a] -> [b] -> Bool
isLonger = (>) `on` len
Run Code Online (Sandbox Code Playgroud)

您可以使用列表来表示惰性自然数,从而更紧凑地完成此操作。

isLonger :: [a] -> [b] -> Bool
isLonger = (>) `on` (() <$)
Run Code Online (Sandbox Code Playgroud)

当然,如果两个列表都是无限的,那么无论你做什么都注定会陷入无限循环。


如果您担心不完整定义的列表以及无限的列表,您可以使用自定义Ord实例来懒一点Nat

instance Ord Nat where
  compare Z Z = EQ
  compare Z (S _) = LT
  compare (S _) Z = GT
  compare (S x) (S y) = compare x y

  Z <= _ = True  -- lazy in the second argument
  S _ <= Z = False
  S x <= S y = x <= y

  x >= y = y <= x
  x > y = not (x <= y)
  x < y = y > x
Run Code Online (Sandbox Code Playgroud)

现在,如果第一个列表为空,即使第二个列表未定义isLonger也会返回。False

  • @templatetypedef 表示“map (const ())”。 (3认同)
  • 我对你最后的解决方案路线很感兴趣,但我对 Haskell 还不够熟悉,无法将其分开。你能详细解释一下 `(() &lt;$)` 是什么意思吗? (2认同)