在几种编程语言(包括JavaScript,Python和Ruby)中,可以在其自身内部放置一个列表,这在使用列表表示无限详细的分形时非常有用。但是,我尝试在Haskell中执行此操作,并且它没有像我预期的那样工作:
--aList!!0!!0!!1 should be 1, since aList is recursively defined: the first element of aList is aList.
main = putStrLn $ show $ aList!!0!!0!!1
aList = [aList, 1]
该程序产生了这个编译器错误,而不是打印1
:
[1 of 1] Compiling Main ( prog.hs, prog.o )
prog.hs:3:12:
Occurs check: cannot construct the infinite type: t0 = [t0]
In the expression: aList
In the expression: [aList, 1]
In an equation for `aList': aList = [aList, 1]
是否有可能在Haskell中放入一个列表,因为我试图在这里做?
答案 0 :(得分:17)
aList = [aList, 1]
的类型是什么?
让我们考虑更简单的aList = [aList]
案例。我们知道aList
必须是某些内容的列表,因此某些类型aList :: [α]
需要α
。什么是α
?作为列表元素的类型,我们知道α
必须是aList
的类型;也就是α ~ [α]
,其中~
表示类型相等。所以α ~ [α] ~ [[α]] ~ [[[α]]] ~ ⋯ ~ [⋯[α]⋯] ~ ⋯
。这确实是一种无限类型,Haskell禁止这样的事情。
如果值为aList = [aList, 1]
,您也有1 :: α
的限制,但所有可以让我们得出结论的是,必须有Num α
约束({{1} }}),它不会改变任何东西。
显而易见的接下来的三个问题是:
让我们按顺序解决这些问题。
第一:为什么Haskell列表只包含一种元素?这是因为Haskell的类型系统。假设您有一个不同类型的值列表:Num α => [⋯[α]⋯]
。函数[False,1,2.0,'c']
的类型是什么?没有一个,因为你不知道你会得到什么类型。那么你还能用这个价值做什么呢?毕竟,你对此一无所知!
第二:为什么Haskell禁止无限类型?无限类型的问题在于它们不会添加许多功能(您可以始终将它们包装在新类型中;请参见下文),并且它们会进行一些真正的错误类型检查。例如,in the question "Why does this Haskell code produce the ‘infinite type’ error?",无限类型的不存在排除了someElement n = [False,1,2.0,'c'] !! n
的错误实现(甚至没有明确的类型签名)。
第三:我能做些什么?如果要在Haskell中伪造无限类型,则必须使用递归数据类型。数据类型阻止类型具有真正的无限扩展,并且显式性避免了上面提到的意外错误。因此,我们可以为无限嵌套列表定义一个新类型,如下所示:
intersperse
这让我们得到了我们想要的无限嵌套列表 - 打印它永远不会终止 - 但类型都不是无限的。 (Prelude> newtype INL a = MkINL [INL a] deriving Show
Prelude> let aList = MkINL [aList]
Prelude> :t aList
aList :: INL a
Prelude> aList
MkINL [MkINL [MkINL [MkINL ^CInterrupted.
isomorphic 到INL a
,但它与相等。如果你对此感到好奇,那么差异在{{ 3}})
但请注意,这种类型不是很有用;它包含的唯一列表是无限嵌套的东西,如[INL a]
,或者是空列表的各种嵌套集合。没有办法将aList
类型的基本案例放入其中一个列表中:
a
所以你想要的列表是任意嵌套列表。 isorecursive types (what Haskell has) and equirecursive types (which allow infinite types).对这些问题有疑问,需要定义新的数据类型:
Prelude> MkINL [()]
<interactive>:15:8:
Couldn't match expected type `INL a0' with actual type `()'
In the expression: ()
In the first argument of `MkINL', namely `[()]'
In the expression: MkINL [()]
data NestedList a = Elem a | List [NestedList a]
的每个元素都是NestedList a
类型的普通值,或者是更多a
的列表。 (这与任意分支树一样,只在其叶子中存储数据。)然后你有
NestedList a
您现在必须定义自己的查找函数,并注意它可能具有类型Prelude> data NestedList a = Elem a | List [NestedList a] deriving Show
Prelude> let aList = List [aList, Elem 1]
Prelude> :t aList
aList :: NestedList Integer
Prelude> aList
List [List [List [List ^CInterrupted.
- NestedList a -> Int -> Maybe (NestedList a)
用于处理超出范围的整数,但重要的部分是它不能只返回Maybe
。毕竟,a
不是整数!
答案 1 :(得分:8)
是。如果您想要一个包含自身的值,您将需要一个包含自身的类型。这没问题;例如,您可能喜欢玫瑰树,在Data.Tree中大致定义如下:
data Tree a = Node a [Tree a]
现在我们可以写:
recursiveTree = Node 1 [recursiveTree]
答案 2 :(得分:6)
对于Haskell中的列表类型,这是不可能的,因为每个元素必须是相同的类型,但是您可以创建一个数据类型来执行它。不过,我不确定你为什么要这么做。
data Nested a
= Value a
| List [Nested a]
deriving (Eq, Show)
nested :: Nested Int
nested = List [nested, Value 1]
(!) :: Nested a -> Int -> Nested a
(!) (Value _) _ = undefined
(!) (List xs) n = xs !! n
main = print $ nested ! 0 ! 0 ! 1
这将打印出Value 1
,这种结构可能会有所帮助,但我认为它非常有限。
答案 3 :(得分:1)
有几个答案从“是的你可以”到“不,你绝对不能”。嗯,两者都是对的,因为它们都涉及你问题的不同方面。
另一种向自身添加“数组”的方法是允许列出任何内容。
{-# LANGUAGE ExistentialQuantification #-}
data T = forall a. T a
arr :: [T]
arr = [T arr, T 1]
所以,这会给自己添加arr,但你不能用它做任何其他事情,除了证明它是一个有效的构造并编译它。
由于Haskell是强类型的,访问列表元素会给你T,你可以提取包含的值。但那个价值的类型是什么?它是“forall a.a” - 可以是任何类型,实质上意味着根本没有任何函数可以对它做任何事情,甚至不打印,因为这需要一个可以将任何类型转换为String的函数。请注意,这不是Haskell特有的 - 即使在动态语言中也存在问题;没有办法弄清楚arr !! 1
的类型,你只假设它是一个Int。 Haskell与其他语言的不同之处在于它不会让你使用该函数,除非你能解释表达式的类型。
这里的其他例子定义了归纳类型,这并不是你所要求的,但是它们表现出易于处理的自我引用。
以下是你如何能够做出明智的结构:
{-# LANGUAGE ExistentialQuantification #-}
data T = forall a. Show a => T a
instance Show T where -- this also makes Show [T],
-- because Show a => Show [a] is defined in standard library
show (T x) = show x
arr :: [T]
arr = [T arr, T 1]
main = print $ arr !! 1
现在由T包装的内部值被限制为Show的任何实例(“OOP用语中的”Show interface“的实现”),因此您至少可以打印列表的内容。
请注意,之前我们不能仅仅因为a和[a]之间没有共同点而包含arr。但是,一旦您可以确定列表中所有元素支持的常见操作,后一个示例就是一个有效的构造。如果你可以为[T]定义这样的函数,那么你可以在自己的列表中包含arr - 这个函数确定某些类型的a和[a]之间的共同点。
答案 4 :(得分:0)
没有。我们可以效仿:
data ValueRef a = Ref | Value a deriving Show
lref :: [ValueRef Int]
lref = [Value 2, Ref, Value 1]
getValue :: [ValueRef a] -> Int -> [ValueRef a]
getValue lref index = case lref !! index of
Ref -> lref
a -> [a]
并有结果:
>getValue lref 0
[Value 2]
>getValue lref 1
[Value 2,Ref,Value 1]
当然,我们可以重复Maybe a
而不是ValueRef a