好吧,人们通常会将您拥有的数据结构称为一种树,而不是列表。但不管怎么说...
问题#1:Haskell 对缩进敏感,你的case
表达式没有缩进。这会导致解析错误。
问题 #2,也是更大的问题:你还没有理解Maybe
类型是如何工作的。我的印象是你认为它在更常见的语言中就像空值一样工作,这让你失望。
在像 Java 这样的语言中,null
是一个可以出现在大多数其他值可以出现的地方的值。如果我们有一个具有以下签名的方法:
public Foo makeAFoo(Bar someBar)
...那么将其称为以下任何一种方式都是合法的:
// Way #1: pass in an actual value
Bar theBar = getMeABar();
Foo result = makeAFoo(theBar);
// Way #2: pass in a null
Foo result2 = makeAFoo(null)
theBar
并且null
在某种意义上是“并行的”,或者更准确地说,它们具有相同的类型——您可以在程序中将一个替换为另一个,并且在两种情况下都可以编译。
另一方面,在 Haskell 中,字符串"hello"
和Nothing
不具有相同的类型,并且您不能在另一个地方使用一个。Haskell 区分这三件事:
- 需要存在的字符串:
"hello" :: String
- 缺少可选字符串:
Nothing :: Maybe String
- 可选字符串的存在:
Just "hello" :: Maybe String
#1 和 #3 之间的区别是你在你的函数中系统地遗漏了什么。使用Maybe a
,在您确实有一个必须使用的值的情况下Just
,它就像一个包装器来表示“这不仅仅是一个a
,它是一个Maybe a
”。
您缺少的第一个地方是表达式Just
的右侧case
,我们可以这样修复:
-- This still fails to compile!
cListGet :: CList a -> Maybe (a, CList a)
cListGet Nil = Nothing
cListGet xs@(NotNil nxs) =
case nxs of
-- I added 'Just' here and in the next line:
Sing x -> Just (x, Nil)
Append l r -> Just (fst $ cListGet (NotNil l), (Append (snd $ cListGet (NotNil l)), r))
但这还没有结束,因为你正在做fst $ cListGet (NotNil l)
,它会遇到相反的问题:cListGet
返回Maybe (a, CList a)
,但fst
在 上工作(a, b)
,而不是在 上Maybe (a, b)
。您需要对结果进行模式匹配cListGet
以测试它是Nothing
还是Just (x, l')
. (同样的问题也出现在您的snd $ cListGet (NotNil l)
.)
第三,您Append
错误地使用了构造函数。你有它的形式,它应该在和(Append foo, bar)
之间没有逗号。在 Haskell 中,这种事情会给你比大多数其他语言更令人困惑的错误消息,因为当 Haskell 看到这个时,它不会告诉你“你犯了语法错误”;Haskell 比大多数语言更文字化,因此它认为您正在尝试将 with作为第一个元素,作为第二个元素,因此它得出的结论是必须具有 type 。foo
bar
Append foo
bar
(Append foo, bar)
(NNList a -> NNList a, NNList a)
第四个也是最后一个问题:你给自己设置的问题没有明确说明,因此没有好的答案。你说你想找到 a 的“头”和“尾” CList a
。这意味着什么?在 Haskell[a]
类型的情况下,带有构造函数[]
and :
,这很清楚:头部是x
in x:xs
,尾部是xs
.
据我了解,您所说的“头”似乎是递归结构的最左侧元素。我们可以这样得到:
cListHead :: CList a -> Maybe a
cListHead Nil = Nothing
-- No need to cram everything together into one definition; deal with
-- the NNList case in an auxiliary function, it's easier...
cListGet (NotNil nxs) = Just (nnListHead nxs)
-- Note how much easier this function is to write, because since 'NNList'
-- doesn't have a 'Nil' case, there's no need to mess around with 'Maybe'
-- here. Basically, by splitting the problem into two functions, only
-- 'cListHead' needs to care about 'Maybe' and 'Just'.
nnListHead :: NNList a -> a
nnListHead (Sing a) = a
nnListHead (Append l _) = nnListHead l
所以你可能认为“尾巴”就是其他一切。好吧,问题在于“其他所有内容”不是您的子部分CList
or NNList
。举个例子:
example :: CList Int
example = NotNil (Append (Append (Sing 1) (Sing 2)) (Sing 3))
“头”是1
。但是没有example
包含2
和3
不包含的结构中定义的子部分1
。您必须构建一个CList
形状与原始形状不同的新产品才能获得它。这是可能的,但坦率地说,我不认为它作为初学者练习的价值。
如果不清楚我所说的“子部分”是什么意思,可以把这个例子想象成一棵树:
NotNil
|
v
Append
/ \
v v
Sing Append
| / \
v v v
1 Sing Sing
| |
v v
2 3
子部分 = 子树。