TL;DR:解决方案在帖子末尾。
您的定义中不需要列表分解模式,只需通过列表推导将列表作为一个整体处理即可:
linsearch :: Eq a => a -> [a] -> Int
linsearch _ [] = -1
linsearch y xs = head [i | let j = length xs, i <- [0..j-1],
-- y == x -- x? what is that?
y == (xs!!i)]
现在linsearch 3 [0..9] 返回3,这是应该的。但是linsearch 3 [0..] 根本没有返回——它在尝试计算列表的长度时丢失了,这里根本不需要它!此外,放弃长度计算使我们将算法从其当前的二次形式重新安排为更好的线性形式:
linsearch :: Eq a => a -> [a] -> Int
linsearch _ [] = -1
linsearch y xs = head [i | (x,i) <- zip xs [0..], y == x]
linsearch 3 [0..] 现在成功返回3,应该如此。
linsearch 3 [0,2..] 仍然存在分歧(即永远不会返回),因为 Haskell 不知道——它也想要——搜索有序的递增 列出第一个大于我们正在搜索的元素的元素。之所以如此,是因为 [a] 是 lists 的类型,而不是 ordered 列表的类型。
我们也可以定义这样的变体,例如
linsearchOrdered :: Ord a => a -> [a] -> Int
linsearchOrdered y xs = linsearch y $ takeUntil (> y) xs
takeUntil :: (a -> Bool) -> [a] -> [a]
takeUntil p xs = foldr (\x r -> if not (p x) then x:r else [x]) [] xs
果然,它现在可以工作了:
> linsearchOrdered 3 [0,2..]
*** Exception: Prelude.head: empty list
> takeUntil (> 3) [0,2..]
[0,2,4]
it :: (Ord a, Num a, Enum a) => [a]
> linsearch 3 [0,2,4]
*** Exception: Prelude.head: empty list
等等,什么?错误来自哪里?它来自您对head 的使用:由于在[0,2,4] 中没有找到3,因此您的代码调用head [] 这是一个错误。
相反,我们可以使用take 1,并将其结果转换为Maybe a,标准用法为listToMaybe:
import Data.Maybe
linsearch :: (Eq a) => a -> [a] -> Maybe Int
linsearch y xs = listToMaybe $ take 1 [i | (x,i) <- zip xs [0..], y == x]
好的,现在它正在工作:
> linsearchOrdered 3 [0,2..]
Nothing
> linsearchOrdered 4 [0,2..]
Just 2
注意返回类型现在不同了。我们不使用特殊值,而是使用包装类型来指示成功(Just)或失败(Nothing):
data Maybe a = Nothing | Just a
如果您真的想要您的原创设计,我们可以将其编码为
linsearch y xs = head $ [i | (x,i) <- zip xs [0..], y == x] ++ [-1]
Haskell 惰性 所以head 将(安全地,现在,使用++ [-1])停在第一个匹配元素上(如果可能的话)。使用head 和takeUntil 几乎不会浪费任何重复的努力。
虽然两者都是折叠。 zip 也可以编码为折叠,因此 linsearch 也是折叠。 folds composition can be fused into one fold 通过融合 reducer 函数,将其全部转换为明确的一次性算法(如果需要)。