List 恒定时间Haskell列表(时间复杂度?Haskell数据类型?)-作业相关

List 恒定时间Haskell列表(时间复杂度?Haskell数据类型?)-作业相关,list,haskell,functional-programming,time-complexity,List,Haskell,Functional Programming,Time Complexity,我目前正在Haskell学习CS课程,在理解一些材料时遇到了一些严重的问题 在我的一个作业中,我得到了两种数据类型,我被要求编写一个具有固定时间append的append函数 我被给予: data NNList a = Sing a | Append ( NNList a) ( NNList a) deriving (Eq) data CList a = Nil | NotNil ( NNList a) deriving (Eq) 我被要求写一个函数: CListAppend :: CList

我目前正在Haskell学习CS课程,在理解一些材料时遇到了一些严重的问题

在我的一个作业中,我得到了两种数据类型,我被要求编写一个具有固定时间append的append函数

我被给予:

data NNList a = Sing a | Append ( NNList a) ( NNList a) deriving (Eq)
data CList a = Nil | NotNil ( NNList a) deriving (Eq)
我被要求写一个函数:

CListAppend :: CList a -> CList a -> CList a
我不确定我在CS教育中错过了什么,但我经常发现自己被时间和空间的复杂性弄糊涂了,我怎么知道一个函数是否是
常数时间
?谁能给我提供一些关于如何处理这个问题的想法

我的尝试:

CListAppend :: CList a -> CList a -> CList a
CListAppend Nil rl = rl
CListAppend ll Nil = ll
CListAppend ll rl = NotNil $ Append ll rl

此操作将报告返回NNList而不是CList的错误。还有什么可以转换的吗

时间复杂度是一种描述相对于输入大小计算答案需要多少步骤的方法。什么构成步骤以及如何计算大小取决于问题

例如,如果您有一堆未排序的名片,则搜索特定的名片将需要与名片数量成比例的多个步骤。如果你把你的牌堆扩大一倍,你要检查的平均牌数也会增加一倍

另一方面,如果你预先分类卡片,你可以玩“高-低”游戏,每次你看一张卡片时,把卡片的大小切成两半。翻倍的大小,现在只需要一个额外的步骤时,桩寻找一个特定的卡

这些分别是线性和对数时间复杂性的例子。在您的情况下,您需要恒定的时间复杂性。这意味着无论这两个列表有多大,添加它们所需的步骤都是相同的

通常,你可以在纸上用不同的输入遍历算法,得到一个想法,但是还有更精确的方法。下面是一个使用标准
[]
列表实现的追加函数示例:

append []     bs = bs
append (a:as) bs = a : append as bs

我们将一个递归调用计数为一步追加;或者,我们可以计算
(:)
的调用次数。当第一个参数为空列表时,
[]
,则不存在递归调用。当列表包含单个元素时,
[1]
,我们计算
1:append[]bs
,因此我们有一个递归调用。现在使用
[1,2]
将输入大小加倍,并对递归调用进行计数。然后使用
[1,2,3,4]
等再次加倍。然后,您可以粗略估计输入大小中的步长是否为常数、线性、对数、指数等。

时间复杂度是一种描述相对于输入大小计算答案所需的步长的方法。什么构成步骤以及如何计算大小取决于问题

例如,如果您有一堆未排序的名片,则搜索特定的名片将需要与名片数量成比例的多个步骤。如果你把你的牌堆扩大一倍,你要检查的平均牌数也会增加一倍

另一方面,如果你预先分类卡片,你可以玩“高-低”游戏,每次你看一张卡片时,把卡片的大小切成两半。翻倍的大小,现在只需要一个额外的步骤时,桩寻找一个特定的卡

这些分别是线性和对数时间复杂性的例子。在您的情况下,您需要恒定的时间复杂性。这意味着无论这两个列表有多大,添加它们所需的步骤都是相同的

通常,你可以在纸上用不同的输入遍历算法,得到一个想法,但是还有更精确的方法。下面是一个使用标准
[]
列表实现的追加函数示例:

append []     bs = bs
append (a:as) bs = a : append as bs

我们将一个递归调用计数为一步追加;或者,我们可以计算
(:)
的调用次数。当第一个参数为空列表时,
[]
,则不存在递归调用。当列表包含单个元素时,
[1]
,我们计算
1:append[]bs
,因此我们有一个递归调用。现在使用
[1,2]
将输入大小加倍,并对递归调用进行计数。然后使用
[1,2,3,4]
等再次加倍。然后,您可以粗略估计输入大小中的步数是否为常数、线性、对数、指数等。

最后一个定义是错误的。 如果
ll
NotNil x
rl
NotNil y
,则您的定义应为

CListAppend (NotNil x) (NotNil y) = NotNil ( Append x y )
但是,您正在对类型为
CList a
的值应用Append


这个解也是常数时间。追加构造函数不进行任何处理。模式匹配也是在固定时间内进行的。

最后一个定义是错误的。 如果
ll
NotNil x
rl
NotNil y
,则您的定义应为

CListAppend (NotNil x) (NotNil y) = NotNil ( Append x y )
但是,您正在对类型为
CList a
的值应用Append


这个解也是常数时间。追加构造函数不进行任何处理。模式匹配也是在固定时间内进行的。

您不希望函数中执行的工作在输入的大小上发生显著变化。在您的情况下,您可能不希望在常量时间解决方案中使用递归。提示:您必须返回
CList
CList
的构造函数是什么?@hammar,我如何在Haskell中真正
构造一些东西?它当然不是
CList$Append ll rl
或类似的东西。@user1043625:数据构造函数位于
data CList a=…
=
的右侧,因此,在这种情况下,它们是
Nil
NotNil
。在恒定时间内执行某项操作意味着操作独立于其输入的大小(函数总是花费相同的时间量,即恒定)。这是你的尝试看起来很有希望,当然除了类型错误。A.