嗯,人们通常会将您拥有的数据结构称为树形结构,而不是列表。但无论如何...
问题#1:Haskell对缩进很敏感,而您的case
表达式没有缩进。这会导致解析错误。
问题#2,也是更大的问题:您还没有理解Maybe
类型的工作原理。我得到的印象是,您认为它的工作方式类似于更常见的语言中的null值,这使您困惑了。
在像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)
的形式,
foo
和
bar
之间不应该有逗号。在 Haskell 中,这种情况会给你带来比大多数其他语言更令人困惑的错误消息,因为当 Haskell 看到这个时,它不会告诉你“你犯了一个语法错误”;Haskell 比大多数语言更字面,所以它认为你正在尝试构建一个以
Append foo
为第一个元素,以
bar
为第二个元素的对,因此它得出结论
(Append foo, bar)
必须具有类型
(NNList a -> NNList a, NNList a)
。
第四个问题,也是最后一个问题:你设定的问题没有明确定义,因此没有好的答案。你说想要找到
CList a
的“头”和“尾”。这是什么意思?对于 Haskell 中的
[a]
类型,其构造器为
[]
和
:
,这是很清楚的:头是
x:xs
中的
x
,而尾是
xs
。
就我理解的来说,你所说的“头”的意思似乎是递归结构的最左边的元素。我们可以这样得到它:
cListHead :: CList a -> Maybe a
cListHead Nil = Nothing
cListGet (NotNil nxs) = Just (nnListHead nxs)
nnListHead :: NNList a -> a
nnListHead (Sing a) = a
nnListHead (Append l _) = nnListHead l
因此,您可能认为“尾部”是其他所有内容。 问题在于,“其他所有内容”不是您的
CList
或
NNList
的
子部分。 看这个例子:
example :: CList Int
example = NotNil (Append (Append (Sing 1) (Sing 2)) (Sing 3))
“head”是1。但在“example”中没有定义包含2和3而不包含1的结构的子部分。要实现这一点,您必须构建一个与原始形状不同的新“CList”。虽然这是可能的,但老实说,我不认为这对初学者来说有什么价值。
如果“subpart”不清楚是什么意思,请将示例视为树:
NotNil
|
v
Append
/ \
v v
Sing Append
| / \
v v v
1 Sing Sing
| |
v v
2 3
Subpart表示子树。