测试我的代码时大索引问题

时间:2019-05-05 13:39:21

标签: list haskell testing

我正在尝试学习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]

1 个答案:

答案 0 :(得分:2)

您的功能存在一些问题。第一种情况(类似于(!!))失败的原因是,(!!) :: Int -> [a] -> a使用基于零的索引,而您的函数似乎可以使用基于一的索引。这意味着您将因此需要减少赋予该函数的索引。

此外,在函数中,您还比较了nfromIntegral (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的索引。我将其作为使它从零开始的练习。