haskell - Haskell:获取Snoc列表中的第N个元素
问题描述
我编写了以下函数来查找给定 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 相对于其他语言的优势。
提前致谢。
解决方案
我们可以使用“ try-and-error ”方法,首先尝试在“前缀列表”中找到该元素,如果这还不够(因为索引更大),然后我们尝试当前元素。如果这还不够,则由“家长电话”来处理这种情况。我们可以通过首先定义一个输出类型略有不同的函数来做到这一点:
nthShelp :: Int -> ListS a -> Either a Int
因此Left x
,如果它找到了元素(x
作为元素),或者它需要遍历的“剩余”元素,它就会返回Right
。, with
所以我们可以这样定义:
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
,然后Left t
从递归调用传回。
我们可以利用 istance 的事实,Either 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
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"
就时间复杂度而言,仍然是O(n)(其中n是列表的长度,不是我们要获取的索引)。没有办法用这种数据结构做得更好,因为在我们知道要返回什么元素之前,我们需要知道前缀中元素的数量。
作为附加询问:实现连接 2 个 Cons 列表的函数的最佳方法是什么?我已经在考虑一个解决方案,但它需要一个辅助函数来跟踪第 N 个位置,而且我觉得这不会充分利用 Haskell 相对于其他语言的优势。
好吧,您可以在这里看到一个串联,用第一个列表替换第二个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
我们可以为此添加一个警卫来处理这种情况:
concatS :: ListS a -> ListS a -> ListS a
concatS NilS = id
concatS l1 = go
where go NilS = l1
go (Snoc l2 x) = Snoc (go l2) x
推荐阅读
- c# - Unity 3d - 玩家在接触墙壁后不断移动
- javascript - 无法通过 Javascript 将“表单”属性分配给“输入”标签
- android - 在 Ubuntu 18 上启动 Android 模拟器时出错
- python-3.x - 为什么在单击 pycharm 中的按钮之前完成分配的操作?(语言-python)
- css - 带有 SVG 的按钮文本没有垂直居中
- javascript - 通过 JavaScript 提交小表单到 Mysql 数据库
- ruby-on-rails - Rails:如何获取连接表的枚举值?
- unity3d - 使用 PUN2 和 Unity3D 更新 VR 场景中可抓取游戏对象位置的问题
- c# - 包装 JSON 流
- r - R图仅针对指定级别按级别重新排序因子