我是haskell的初学者,并尝试实现一个函数,该函数返回列表的第k个元素,而不使用内置的前缀!!
。
kth_i [] _ _ = -1
kth_i (x:xs) i k = if i == k then x else kth_i xs (i + 1) k
kth xs k = kth_i xs 0 k
此代码有效,但过于冗长。我只想使用一个功能。
编辑:我最终得到了:
let kth (x:xs) k = if k == 0 then x else if k < 0 then error "out of bounds" else kth xs (k - 1); kth [] _ = error "out of bounds"
根据@ Carsten的建议。
答案 0 :(得分:2)
所以我猜你找到了你的解决方案,但允许我为你做一点好事:
kth :: [a] -> Integer -> a
kth (x:xs) i
| i < 0 = error "out of bounds"
| i == 0 = x
| i > 0 = kth xs (i-1)
kth [] _ = error "empty list"
这使用了guards而不是if
s - 所以如果你之前没有看到那些,你应该按照链接阅读wiki
答案 1 :(得分:1)
更新:以下是使用模式匹配的解决方案。您还可以查看Haskell(!!)函数的实现,它与此代码非常相似:!!
nth::[a]->Int->a
nth [] _ = error "out of bounds"
nth (x:xs) 0 = x
nth (x:xs) n = nth xs (n-1)
答案 2 :(得分:1)
一个简单的定义,假设列表至少有k + 1
项:
kth :: Integer -> [a] -> a
kth k = head . drop k
答案 3 :(得分:1)
由于没有其他人提到这一点,我想我应该:Haskell中通常不鼓励部分函数(有时会产生错误消息或无限循环的函数)。我们倾向于处理总函数,使用Maybe
和Either
等类型表示失败。即使我们处理部分函数,我们也倾向于尝试限制难以处理的错误。最极端的版本可能是这样的:
data Kth a = NegativeIndex | ShortList | Kth a
kth :: Integral i => [a] -> i -> Kth a
kth _ k | k < 0 = NegativeIndex
kth [] _ = ShortList
kth (x : _) 0 = Kth x
kth (_ : xs) k = kth xs (k - 1)
这提供了非常丰富的信息,但使用起来非常尴尬。如果您确信所给出的指数永远不会为负数,则可以使用此指数:
kth :: Integral i => [a] -> i -> Maybe a
kth _ k | k < 0 = error "Negative index"
kth [] _ = Nothing
kth (x : _) 0 = Just x
kth (_ : xs) k = kth xs (k - 1)
请注意,如果您将此版本专门设置为Natural
Numeric.Natural
类型(在最新版本的GHC中提供),那么您可以省略< 0
测试,因为自然数字确实< em>不能是负面的!
现在坚持使用多态代码,你可以看到效率很低:kth
将在每次递归调用时检查k < 0
,即使它只能在第一次调用时发生一。您可以使用辅助函数来避免这种情况:
kth :: Integral i => [a] -> i -> Maybe a
kth xs k | k < 0 = error "Negative index"
| otherwise = kth' xs k
where
kth' [] _ = Nothing
kth' (x : _) 0 = Just x
kth' (_ : xs) k = kth' xs (k - 1)
一旦我们走了这么远,我们可能会有更多的乐趣。辅助函数与foldr
“设计模式”匹配,因此我们可以这样写:
kth' xs = foldr go (const Nothing) xs where
go x _ 0 = Just x
go _ r i = r (i - 1)
内联kth'
的此版本,我们得到
kth xs k | k < 0 = error "Negative index"
| otherwise = foldr go (const Nothing) xs k
where
go x _ 0 = Just x
go _ r i = r (i - 1)
出于优化目的,最好将(const Nothing)
替换为(`seq` Nothing)
,并将r (i - 1)
替换为r $! i - 1
,但这可能不是一个好主意。关于为什么刚才的全部细节。