我不明白为什么需要基本案例:
-- perms :: Ord a => [a] -> [[a]]
perms [] = [[]]
perms xs = [ (x:ps) | x <- xs, ps <- perms (xs \\ [x])]
在我看来,从列表理解中它应该是自动的,但后来我注意到了:
[ x:y | x<-[], y<-[] ]
评估为[],而不是[[]],这似乎令人惊讶。
即便如此,我也很惊讶,如果没有它运行的基本情况,但总是给[],这违反了类型签名。 有没有一种简单的方法来跟踪列表理解的执行?它似乎是Debug.Trace.trace的原子。
答案 0 :(得分:8)
您可以将<-
视为使用左侧表达式右侧列表中的每个元素。
由于[]
没有元素,因此整个列表理解返回一个空列表。
如果[ x:y | x<-[], y<-[] ]
返回[[]]
,则会很奇怪,因为:
包含元素和列表。因此要生成[[]]
y必须是[]
,但那么x
会是什么?
正如KennyTM所说,[]
的类型为[[a]]
。实际上[]
来自这种类型:
[a] [[a]] [[[a]]]
等等。如果不是那么函数就没有办法返回一个空列表。
无论如何,忘记一些括号是一个非常常见的错误,这就是为什么类型注释是必要的。
答案 1 :(得分:4)
让我们去糖!
[ x:y | x <- [], y <- [] ]
变成
do x <- []
y <- []
return (x:y)
现在,更加脱糖!耶!
[] >>= \x -> ([] >>= \y -> return (x:y))
iirc,>>=
列表为flip concatMap
,而return
只是\x -> [x]
我们只做一点替代。
concatMap (\x -> ([] >>= \y -> return (x:y))) []
那里,你现在看到了吗? concatMap f []
显然会评估为[]
。因为concatMap f
只是concat . map f
。因此将f
映射到空列表,然后连接结果。除非没有结果,因此最终评估为[]
。
答案 2 :(得分:3)
所以它归结为
的含义[ x:y | x<-[], y<-[] ]
阅读此方法的方法是,“当x:y
没有可能的值且x
没有可能的值时,y
有哪些可能的值?”希望很明显,在这种情况下,x:y
根本没有值,因为您需要x
的值和y
的值才能获得可能性对于x:y
,你没有。
选择x
和y
的可能值组合的一般模式称为笛卡尔积,而“product”一词的使用部分是因为数字可能组合的数量等于x
,次的可能性数量y
的可能性数量。因此,如果x
选项为零,y
选项为零,则可以选择0 * 0 = 0
选择两者的组合。
答案 3 :(得分:1)
这是另一个提示为什么有一个带有空列表的基本情况。 n
个元素有多少个排列?有n!
个排列。什么是0!
?它是1.因此perm []
的结果列表的长度必须为1.类型a
的类型和缺少强制结果为[[]]
。没有其他定义的值具有正确的类型和长度。
答案 4 :(得分:1)
如果您对表单有列表理解:
[ x:y | stuff ]
然后您正在生成一个列表,其元素的格式为x:y
,用于x
和y
的某些选项,由stuff
确定。 [[]]
是一个列表,其元素的格式不是x:y
或x
的{{1}},因此前者无法生成后者。
在期待y
的{{1}}时,您似乎设置了[[]]
和[ x:y | x <- [], y <- [] ]
,然后考虑x = []
。这有两个原因:
y = []
来自x:y = [[]]
类x
,因此xs
的类型为[a]
,而且(通常)不是列表x
元素的列表,而不是单个元素。