我正在尝试学习Haskell,我想编写一个递归函数,并且不使用任何库函数。函数
nth ::Integer -> [a ] -> Maybe a
获取索引n和元素列表,并返回列表的第n个元素(如果索引有效),否则返回Nothing 索引无效。
我的代码:
nth :: Integer -> [a] -> Maybe a
nth a [] = Nothing
nth a (x:xs) |a == 1 = Just x
|fromIntegral (length xs) < a = Nothing
|a==0 = Nothing
| otherwise = nth (a-1) xs
我想对我的代码进行此测试:
spec = do
describe "nth" $ do
it "for valid indexes it behaves like (!!)" $
property $ \n xs -> n < 0 || (fromInteger n) >= length (xs::[Integer]) || Lists.nth n xs == Just (xs!!(fromInteger n))
it "for negative indexes it returns Nothing" $
property $ \n xs -> n >= 0 || Lists.nth n (xs::[Integer]) == Nothing
it "for too large indexes it returns Nothing" $
property $ \n xs -> (fromInteger n) < length xs || Lists.nth n (xs::[Integer]) == Nothing
但是每次我进行测试时都会出错
for valid indexes it behaves like (!!) FAILED [1]
for negative indexes it returns Nothing
+++ OK, passed 100 tests.
for too large indexes it returns Nothing FAILED [2]
1) Lists.nth for valid indexes it behaves like (!!)
Falsified (after 5 tests and 5 shrinks):
0
[0]
To rerun use: --match "/Lists/nth/for valid indexes it behaves like (!!)/"
./ListsSpec.hs:23:9:
2) Lists.nth for too large indexes it returns Nothing
Falsified (after 38 tests):
1
[0]
答案 0 :(得分:2)
您的功能存在一些问题。第一种情况(类似于(!!)
)失败的原因是,(!!) :: Int -> [a] -> a
使用基于零的索引,而您的函数似乎可以使用基于一的索引。这意味着您将因此需要减少赋予该函数的索引。
此外,在函数中,您还比较了n
和fromIntegral (length xs)
。由于xs
是列表的 tail ,因此检查是不正确的,因为在某些情况下,它将永远不考虑最后一个元素。确实:
Prelude> nth 2 [0, 2]
Nothing
此外,每个迭代中使用length
通常不是一个好主意。 length
在 O(n)中运行,这意味着您的算法现在在 O(n 2 )中运行,因此列表增加,这很容易就会花费大量时间。
更短更优雅的解决方法可能是:
nth :: Integral i => i -> [a] -> Maybe a
nth 1 (x:_) = Just x
nth i (_:xs) | i < 1 = Nothing
| otherwise = nth (i-1) xs
nth _ [] = Nothing
因此,这里有四种情况:万一索引是1
并且列表是非空的,我们返回包装在Just
中的列表的开头。如果索引不是1且小于1,则索引太小,因此我们返回Nothing
(严格来说这种情况是没有必要的)。如果i
大于1,则称为nth (i-1) xs
。最后,如果我们到达列表的末尾(或者列表首先是空的),我们也将返回Nothing
。
现在为了测试这一点,我们需要重写以下三种情况:
describe "nth" $ do
it "for valid indexes it behaves like (!!)" $
property $ \n xs -> n <= 0 || n > length (xs :: [Integer]) || Lists.nth n xs == Just (xs !! (n-1))
it "for negative indexes it returns Nothing" $
property $ \n xs -> n > 0 || Lists.nth n (xs :: [Integer]) == Nothing
it "for too large indexes it returns Nothing" $
property $ \n xs -> n <= length xs || Lists.nth n (xs :: [Integer]) == Nothing
因此,第一个排除n <= 0
(负或零索引)以及n > length xs
,从而检查该值是否为Just (xs !! (n-1))
。
在第二种情况下,排除大于零的值,并检查是否所有剩余索引都映射到Nothing
上。
最后,最后一个属性检查是否对大于length xs
的值,我们也一无所获。
请注意,这里nth
使用基于1的索引。我将其作为使它从零开始的练习。