我试图实现一个从末尾返回k元素的递归函数。
这是我的尝试:
kElementFromEnd :: Int -> [x] -> x
kElementFromEnd _ [] = error "cannot request k item from empty list"
kElementFromEnd k [x]
| k < 0 = error "k must be non negative"
| k == 0 = last [x]
| otherwise = kElementFromEnd (k-1) (init [x])
这是我收到的错误:
*Main> kElementFromEnd 2 [1,2,3]
*** Exception: EX2.hs:(4,1)-(8,54): Non-exhaustive patterns in function kElementFromEnd
我真的不明白为什么haskell无法匹配这种模式。发生了什么事,我不理解?
由于
答案 0 :(得分:6)
您只匹配空列表([]
)和单个元素列表([x]
)。我认为您的意思是替换[x]
,这是一个与单个元素列表匹配的模式,并将该单个值分配给x
,只需xs
,一个匹配任何列表的模式还没有匹配。这看起来像
kElementFromEnd :: Int -> [x] -> x
-- This pattern matches the empty list
kElementFromEnd _ [] = error "cannot request k item from empty list"
-- This pattern is just a name, so it matches everything else
-- i.e. non-empty lists
kElementFromEnd k xs
| k < 0 = error "k must be non negative"
| k == 0 = last xs
| otherwise = kElementFromEnd (k-1) (init xs)
它可以作为
> kElementFromEnd 0 [1..5]
5
> kElementFromEnd 4 [1..5]
1
> map (\i -> kElementFromEnd i [1..10]) [0..9]
[10,9,8,7,6,5,4,3,2,1]
答案 1 :(得分:4)
写这样的功能绝对是一个很好的练习。首先,GHC可以帮助您捕获这样的错误。特别是-fwarn-incomplete-patterns
如果你的模式不完整会发出警告,防止出现令人讨厌的运行时错误。使用-Werror
,您也可以将其设为错误,以确保您不会错过警告。使用-Wall -Werror
进行编译以消除所有可能的警告并不是一种不好的做法。
其次,为了让练习更难:由于init
是 O(n),你的函数实现是 O(kn)(或者更精确 O(min(k,n)n)。对于非常小的 k 它并不重要,但如果k≈n ,您将获得二次性能。所以我建议您尝试找到一个实现
扰流:
kElementFromEnd k xs = last $ zipWith const xs (drop k xs)
此外,优雅地失败通常会更好,因为调用error
意味着您的程序立即存在。所以另一个改进是制作类型签名
kElementFromEnd :: Int -> [x] -> Maybe x