'Haskell comparing two lists' lengths but one of them is infinite?
I want to write a function that checks if the first list is longer than the second list and one of them can be infinite. However I can't find a working solution.
isLonger :: [a] -> [b] -> Bool
isLonger a b
| listLength a > listLength b = True
|otherwise = False
listLength :: [a] -> Integer
listLength = foldr (flip $ const . (+1)) 0
Solution 1:[1]
@dfeuer's solution wins on cleverness, but in terms of a more typical solution...
A reasonable approach mentioned in the comments is to process both lists in parallel and figure out which one "runs out" first -- that's the shorter list. A simple solution is to recurse while both lists are non-empty:
isLonger :: [a] -> [b] -> Bool
isLonger (x:xs) (y:ys) = isLonger xs ys
and return an answer as soon as one of the lists becomes empty:
isLonger [] _ = False -- list a <= list b
isLonger _ [] = True -- list a > list b
If both lists run out at the same time (equal length), the first pattern will match, so that will determine the answer in case of a tie.
Solution 2:[2]
Plain old natural numbers will not do the trick, because you can't calculate the natural number length of an infinite list in finite time. However, lazy natural numbers can do it.
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
You can do it even more compactly using lists to represent lazy natural numbers.
isLonger :: [a] -> [b] -> Bool
isLonger = (>) `on` (() <$)
Of course, if both lists are infinite, you are doomed to an infinite loop no matter what you do.
If you are worried about incompletely defined lists as well as infinite ones, you can be a little lazier using a custom Ord
instance for 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
Now if the first list is empty, isLonger
will return False
even if the second list is undefined.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | K. A. Buhr |
Solution 2 |