首页 > 解决方案 > Haskell - 在自定义 atIndex 函数上处理列表超出范围时出错

问题描述

我在 1 天前开始使用 Haskell,我试图做一些例子。我遇到了一个无法解决的问题。

问题是我的if比较不起作用。程序只是忽略该语句并继续执行else语句。(在代码上注明为注释)
代码:

lengthOf :: Num t => [t] -> t
lengthOf []  = 0
lengthOf arr = lengthOfAcc arr 1
    where
        lengthOfAcc :: Num t => [t] -> t -> t
        lengthOfAcc (_:t) counter = 
            if null t
                then counter
            else
                lengthOfAcc t (counter + 1)

atIndex :: (Ord t, Num t) => [t] -> t -> t
atIndex [] _      = error "atIndex: Empty list passed!"
atIndex arr index =
    if index < 0
        then error "atIndex: Index cannot be smaller than 0!"
    else
        atIndexAcc arr index 0 (lengthOf arr)

    where
        atIndexAcc :: (Ord t, Eq t, Num t) => [t] -> t -> t -> t -> t
        atIndexAcc (h:t) index counter arr_size = 
            if counter == index
                then h
            else
                if counter > arr_size -- This is not working. I don't know why.
                    then error "atIndexAcc: Out of array range!"
                else
                    atIndexAcc t index (counter + 1) arr_size


控制台输出:

*EB_Functions> lengthOf [5, 10, 15, 20, 25]
5
*EB_Functions> atIndex [5, 10, 15, 20, 25] 1
10
*EB_Functions> atIndex [5, 10, 15, 20, 25] 3
20
*EB_Functions> atIndex [5, 10, 15, 20, 25] 7
*** Exception: EB_Quick_F.hs:(71,9)-(78,61): Non-exhaustive patterns in function atIndexAcc

如你看到的。当counter超过arr_size. 谢谢你的帮助!

标签: haskell

解决方案


让我们看看这扩展成什么。

atIndex [5, 10, 15, 20, 25] 7
atIndexAcc [5, 10, 15, 20, 25] 7 0 (lengthOf [5, 10, 15, 20, 25])
-- counter is strictly evaluated for both index and arr_size checks
-- arr_size is evaluated to perform counter > arr_size check
atIndexAcc [5, 10, 15, 20, 25] 7 0 5
atIndexAcc [10, 15, 20, 25] 7 1 5
atIndexAcc [15, 20, 25] 7 2 5
atIndexAcc [20, 25] 7 3 5
atIndexAcc [25] 7 4 5
atIndexAcc [] 7 5 5

那就是它停止的时候——因为[]不匹配(h:t),你会得到非详尽的模式。您需要一个atIndexAcc可以接受空列表的版本。请注意,您比较了counter > arr_size,这也不会发生,因为您在计数时正在遍历列表;仅当列表用尽时它们才相等。

另外,取列表的长度意味着遍历整个列表;这没有必要对其进行索引,并且会强制整个列表存在于内存中。这不适用于无限列表,这可能是由于 Haskell 的惰性评估。


推荐阅读