Haskell自定义atIndex函数越界判断失效问题求助
解决Haskell atIndex函数的越界判断失效问题
首先咱们来拆解问题出在哪:当你传入的索引超过列表长度时,atIndexAcc会一直递归遍历列表,直到列表被拆成空列表。但你只给atIndexAcc定义了(h:t)(非空列表)的匹配模式,完全没处理空列表的情况——这就直接触发了非穷尽模式异常。你写的counter > arr_size判断根本没机会执行,因为列表只有5个元素时,counter从0涨到4就已经遍历完所有元素了,再递归的话t是空列表,(h:t)模式匹配不上,直接报错。
接下来给你两种修改方案,让越界判断正常生效:
方案1:在递归前提前拦截无效索引
既然有效索引的范围是0 <= index < 列表长度,咱们可以在atIndex的主逻辑里先做这个判断,不用进入递归就直接抛出错误:
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 = let arr_size = lengthOf arr in if index < 0 then error "atIndex: Index cannot be smaller than 0!" else if index >= arr_size -- 提前判断索引是否超出上限 then error "atIndex: Out of array range!" else atIndexAcc arr index 0 arr_size where atIndexAcc :: (Ord t, Eq t, Num t) => [t] -> t -> t -> t -> t atIndexAcc (h:t) index counter _ = if counter == index then h else atIndexAcc t index (counter + 1) _
这个方案的好处是避免不必要的递归,直接在入口处拦截无效索引,逻辑更直观。
方案2:给atIndexAcc添加空列表模式处理
如果你希望在递归过程中处理越界情况,可以给atIndexAcc加上空列表的匹配模式,当列表遍历完还没找到对应索引时,直接抛出错误:
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 [] _ _ _ = error "atIndexAcc: Out of array range!" -- 新增空列表处理 atIndexAcc (h:t) index counter _ = if counter == index then h else atIndexAcc t index (counter + 1) _
这里移除了原来的counter > arr_size判断,因为当列表为空时,就说明已经遍历完所有元素但没找到对应索引,必然是越界了。
额外小提示
你自己实现的lengthOf功能是对的,但Haskell标准库已经有length函数(返回Int类型),如果不需要自定义逻辑的话,可以用fromIntegral . length来转换类型,简化代码:
lengthOf :: Num t => [t] -> t lengthOf = fromIntegral . length
内容的提问来源于stack exchange,提问作者Zaphiel
相关产品推荐
相关产品推荐

