哈斯克尔列表中的第K个元素

时间:2016-03-06 19:44:23

标签: haskell

我是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的建议。

4 个答案:

答案 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中通常不鼓励部分函数(有时会产生错误消息或无限循环的函数)。我们倾向于处理总函数,使用MaybeEither等类型表示失败。即使我们处理部分函数,​​我们也倾向于尝试限制难以处理的错误。最极端的版本可能是这样的:

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,但这可能不是一个好主意。关于为什么刚才的全部细节。