Haskell 是否存在类似(xs:x)的东西
我是哈斯克尔的新手。我知道我可以通过以下操作创建一个Haskell 是否存在类似(xs:x)的东西,haskell,Haskell,我是哈斯克尔的新手。我知道我可以通过以下操作创建一个反向函数: reverse :: [a] -> [a] reverse [] = [] reverse (x:xs) = (Main.reverse xs) ++ [x] 是否存在这样一种情况,即(xs:x)(与元素连接的列表,即x是列表中的最后一个元素),因此我将最后一个列表元素放在列表的前面 rotate :: [a] -> [a] rotate [] = [] rotate (xs:x) = [x] ++ xs 当我试图编
反向函数:
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = (Main.reverse xs) ++ [x]
是否存在这样一种情况,即(xs:x)
(与元素连接的列表,即x
是列表中的最后一个元素),因此我将最后一个列表元素放在列表的前面
rotate :: [a] -> [a]
rotate [] = []
rotate (xs:x) = [x] ++ xs
当我试图编译包含此函数的程序时,会出现以下错误:
Occurs check: cannot construct the infinite type: a = [a]
When generalising the type(s) for `rotate'
head :: List a -> a
head (Empty) = error " the empty list have no head"
head (Cons x xs) = x
在后一个示例中,x
实际上是一个列表<代码>[x]
成为列表列表,例如[[1,2],[3,4]]
(++)
希望两侧都有相同类型的列表。当您使用它时,您正在执行[[a]]+[a]
,这就是编译器抱怨的原因。根据您的代码,a
将与[a]
的类型相同,这是不可能的
在(x:xs)
中,x
是列表的第一项(头部),而xs
是除头部(即尾部)以外的所有内容。这里的名称不相关,您不妨称它们为(head:tail)
如果您真的想获取输入列表的最后一项并将其放在结果列表的前面,您可以执行以下操作:
rotate :: [a] -> [a]
rotate [] = []
rotate lst = (last lst):(rotate $ init lst)
view :: [a] -> (a, [a])
view xs = (last xs, init xs)
someFunction :: [a] -> ...
someFunction (view -> (x,xs)) = ...
注意:我根本没有测试过这段代码,因为我目前没有可用的Haskell环境。我也是Haskell的新手,所以我的答案并不权威。无论如何,我会使用last
和init
:
Prelude> last [1..10] : init [1..10]
[10,1,2,3,4,5,6,7,8,9]
或
按照你的建议倒车可能效率要低得多。最后一个不是O(1)运算,而是O(N),这意味着按照您的建议旋转将变成O(N^2)alghorhim
资料来源:
您的第一个版本具有O(n)复杂性。事实并非如此,因为ase++也是O(N)运算
你应该这样做
rotate l = rev l []
where
rev [] a = a
rev (x:xs) a = rev xs (x:a)
来源:如果您愿意使用与普通列表不同的内容,您可以查看containers包中的Seq
类型,如文档所示。这有O(1)个cons(前面的元素)和snoc(后面的元素),并允许通过使用视图从前面和后面对元素进行模式匹配。“是否有(xs:x)(与元素连接的列表,即x是列表中的最后一个元素)这样我就可以将最后一个列表元素放在列表的前面?”
不,不是你的意思。函数定义左侧的这些“模式”反映了程序员如何定义数据结构并将其存储在内存中。Haskell的内置列表实现是一个从一开始就排序的单链接列表,因此函数定义可用的模式正好反映了这一点,公开了第一个元素加上列表的其余部分(或者,空列表)
对于以这种方式构造的列表,最后一个元素不能立即作为列表最顶端节点的存储组件之一使用。因此,该值不是出现在函数定义左侧的模式中,而是由右侧的函数体计算
当然,您可以定义新的数据结构,因此如果您想要一个新的列表,通过模式匹配使最后一个元素可用,您可以构建它。但也有一些代价:也许你只是将列表向后存储,这样它就成为了模式匹配无法使用的第一个元素,并且需要计算。可能您正在结构中存储第一个和最后一个值,这需要额外的存储空间和簿记
考虑一个数据结构概念的多个实现是完全合理的——向前看一点,这是Haskell的类/实例定义的一种用法。简单的回答是:这在模式匹配中是不可能的,必须使用函数
很长的答案是:它不在标准Haskell中,但如果您愿意使用名为View Patterns的扩展,并且如果您对模式匹配没有问题,最终需要的时间比常量时间更长,则可以使用它
原因是模式匹配首先基于结构的构造方式。列表是一种抽象类型,具有以下结构:
data List a = Empty | Cons a (List a)
deriving (Show) -- this is just so you can print the List
当您声明这样一个类型时,您将生成三个对象:一个类型构造函数List
,以及两个数据构造函数:Empty
和Cons
。类型构造函数接受类型并将其转换为其他类型,即,List
接受类型a
,并创建另一个类型List a
。数据构造函数的工作方式类似于返回类型为列表a
的函数。在这种情况下,您有:
Empty :: List a
表示空列表和
Cons :: a -> List a -> List a
它获取类型为a
的值和一个列表,并将该值附加到列表的头部,返回另一个列表。因此,您可以按如下方式构建列表:
empty = Empty -- similar to []
list1 = Cons 1 Empty -- similar to 1:[] = [1]
list2 = Cons 2 list1 -- similar to 2:(1:[]) = 2:[1] = [2,1]
这或多或少是列表的工作方式,但是在空的地方有[]
,在Cons的地方有(:)
。当您键入类似于[1,2,3]
的内容时,这只是1:2:3:[]
或cons1(cons2(cons3为空))
的语法糖
当您进行模式匹配时,您正在“反构造”类型。了解类型的结构可以让您对其进行独特的反汇编。考虑函数:
Occurs check: cannot construct the infinite type: a = [a]
When generalising the type(s) for `rotate'
head :: List a -> a
head (Empty) = error " the empty list have no head"
head (Cons x xs) = x
类型匹配的结果是数据构造函数与您给定的某种结构匹配。如果它与Empty
匹配,则您的列表为空。如果If匹配Const x xs
,则x
必须具有类型a
,并且必须是列表的头,并且xs
必须具有类型list a
并且是列表的尾,因为这是数据构造函数的类型:
Cons :: a -> List a -> List a
如果Cons x xs
属于列表a
类型,则x
必须是a
且xs
必须
view :: [a] -> (a, [a])
view xs = (last xs, init xs)
someFunction :: [a] -> ...
someFunction (view -> (x,xs)) = ...