Haskell:获取Snoc列表中的第N个元素

时间:2018-08-18 11:38:32

标签: haskell

我编写了以下函数,以在给定的Snoc列表中找到第N个元素(反向缺点),但我认为这不是解决该问题的最佳方法。我想知道您是否愿意分享一些见解,代码或建议,以寻求不同的解决方案?您可能会考虑的一种方法可以更有效地完成工作:

这是我的职能:

data ListS a = NilS 
              |Snoc (ListS a) a deriving Show

len :: ListS a -> Int
len NilS       = 0
len (Snoc a b) = 1 + len(a) 

nthElementS :: Int -> ListS a -> a
nthElementS _ NilS = error "Empty List"
nthElementS n s    = if n < 0 || n >= len(s) then error "Invalid Index" 
                     else nthAux ((len(s)-1)-n) s where
                          nthAux 0 (Snoc a b) = b
                          nthAux m (Snoc a b) = nthAux (m-1) a

一些例子:

Main> nthElementS 0 (Snoc (Snoc (Snoc (Snoc (Snoc NilS 1) 2) 3) 4) 5)
1

Main> nthElementS 2 (Snoc (Snoc (Snoc (Snoc (Snoc NilS 1) 2) 3) 4) 5) 
3

作为附加查询:实现将2个Snoc列表串联的功能的最佳方法是什么?我已经在考虑一种解决方案,但是它需要一个辅助功能来跟踪第N个位置,再次,我觉得这不会充分利用Haskell相对于其他语言的优势。

谢谢。

1 个答案:

答案 0 :(得分:4)

我们可以使用“ 尝试和错误”方法,首先尝试在“前缀列表”中查找该元素,如果这还不够的话(因为索引较大) ,然后尝试当前元素。如果这还不够,则由“家长电话”来处理这种情况。为此,我们可以先定义一个输出类型稍有不同的函数:

nthShelp :: Int -> ListS a -> Either a Int

因此,如果找到了元素(Left x是元素),则返回x,或者找到所需的“剩余”元素Right n , with n`穿过。

因此我们可以使用以下方式定义它:

nthShelp :: Int -> ListS a -> Either a Int
nthShelp n NilS = Right n
nthShelp n (Snoc hs t) = progress nhs
    where nhs = nthElementS n hs
          progress lx@(Left _) = lx
          progress (Right 0) = Left t
          progress (Right n) = Right (n-1)

因此,我们首先在列表的开头进行递归调用,然后通过递减索引直到索引达到零来解决递归调用,在这种情况下,我们返回相应的Left t和那个{{1} },然后从递归调用中传回。

我们可以利用Left tEither a等式这一事实,并将其写得更有效,例如:

Monad

然后我们可以用nthShelp :: Int -> ListS a -> Either a Int nthShelp n NilS = Right n nthShelp n (Snoc hs t) = nthElementS n hs >>= progress where progress 0 = Left t progress n = Right (n-1)来写nthElementS

nthShelp

就时间复杂度而言,它仍然是 O(n)(其中 n 是列表的 length ,而不是我们的索引想获得)。无法用这种数据结构做得更好,因为我们需要知道前缀中的元素数量,然后才能知道返回哪个元素。

  

作为附加查询:实现将2个Cons列表串联的功能的最佳方法是什么?我已经在考虑一种解决方案,但是它需要一个辅助功能来跟踪第N个位置,再次,我觉得这不会充分利用Haskell相对于其他语言的优势。

好吧,您可以在这里看到一个串联,是将 second 列表的(通常是很深)嵌套的nthElementS :: Int -> ListS a -> a nthElementS n l | n < 0 = error "Index too small" | Left a <- nthShelp n l = a | otherwise = error "Index too large" 替换为第一个列表。因此,如果我们串联NlS,则为concatS l1 NilS,如果我们串联l1,则为concatS l1 (Snoc NilS x)。因此我们可以将其递归定义为:

Snic l1 x)

上述方法的缺点是,如果第一个列表为concatS :: ListS a -> ListS a -> ListS a concatS l1 NilS = l1 concatS l1 (Snoc l2 x) = Snoc (concatS l1 l2) x (太空),它仍然可以在 O(n)中工作。我们可以为此添加保护措施,以解决这种情况:

NilS