[英]Haskell permutation function does not compile
I'm brushing up on some Haskell and I am trying to write a permutation function that would map [1,2,3] -> [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]. 我正在刷一些Haskell,并且尝试编写一个置换函数,该函数将映射[1,2,3]-> [[1,2,3],[1,3,2],[2,1 ,3],[2,3,1],[3,1,2],[3,2,1]。 I have the following -
我有以下内容-
permute:: [a] -> [[a]]
permute [] = []
permute list = map f list
where
f x = listProduct x (permute (exclude x list))
exclude e list1 = filter (/= e) list1
listProduct x list2 = map (x :) list2
The following is the error message I get - 以下是我收到的错误消息-
permutations.hs:3:20:
Couldn't match type `a' with `[a]'
`a' is a rigid type variable bound by
the type signature for permute :: [a] -> [[a]]
at permutations.hs:1:11
Expected type: a -> [a]
Actual type: a -> [[a]]
In the first argument of `map', namely `f'
In the expression: map f list
In an equation for `permute':
permute list
= map f list
where
f x = listProduct x (permute (exclude x list))
exclude e list1 = filter (/= e) list1
listProduct x list2 = map (x :) list2
Failed, modules loaded: none.
I would try to debug, but it doesn't even compile. 我会尝试调试,但它甚至无法编译。 Any ideas?
有任何想法吗?
Let's focus on the involved list types, only: 让我们仅关注涉及的列表类型:
permute (exclude x list)
is of type [[a]]
because of the type signature of permute
, hence 由于
permute
的类型签名而为[[a]]
类型,因此
listProduct x (permute (exclude x list))
is also of type [[a]]
by the def. def的类型也为
[[a]]
。 of listProduct
产品
listProduct
listProduct x list2 = map (x :) list2
Summing up, 加起来,
f x = listProduct x (permute (exclude x list))
returns [[a]]
, but then 返回
[[a]]
,但是
permute list = map f list
applies f
to all the elements of a [a]
, returning a [[[a]]]
, which is not the correct return type for permute
. 将
f
应用于[a]
所有元素,返回[[[a]]]
,这不是permute
的正确返回类型。
[[[a]]]
into [[a]]
by concatenating all the lists. [[[a]]]
转换为[[a]]
。 Eq a
constraint, since you re using /= x
in exclude
Eq a
约束,因为您在exclude
使用/= x
[]
. []
有一个排列。 (Indeed, 0!=1, not 0) To anyone who may be interested - to solve this problem I needed a way to simulate iteration from imperative programming, and therefore a circular list suggested itself (essentially I have been trying to simulate a solution I wrote in javascript once which involved the same process I describe, with the single exception that I took advantage of a for loop). 对于可能感兴趣的任何人-为了解决此问题,我需要一种方法来模拟命令式编程的迭代,因此,建议使用一个循环列表(本质上,我一直在尝试模拟我用javascript写过的解决方案,而该解决方案涉及相同的过程,描述,唯一的例外是我利用了for循环)。
permute [] = [[]]
permute list = [h:tail | h <- list, tail <- (permute (exclude h list))]
where
exclude x = filter (/= x)
Not necessarily the most efficient solution because exclude
is an O(n)
operation, but neat, and works very well as a proof of concept. 不一定是最有效的解决方案,因为
exclude
是O(n)
运算,但是很简洁,并且可以很好地用作概念证明。
The "right" way to do this is to combine the picking and the excluding of a list item into one purely positional operation select :: [a] -> [(a,[a])]
: 做到这一点的“正确”方法是将挑选和排除列表项组合为一个纯位置操作
select :: [a] -> [(a,[a])]
:
import Control.Arrow (second)
-- second f (a, b) = (a, f b)
select [] = []
select (x:xs) = (x,xs) : map (second (x:)) (select xs)
permute [] = [[]] -- 1 permutation of an empty list
permute xs = [x : t | (x,rest) <- select xs, t <- permute rest]
To "debug"-develop your program you could define each internal function on its own as a global one, and see whether the bits and pieces fit together: 要“调试”-开发您的程序,您可以将每个内部函数自己定义为全局函数 ,并查看各部分是否组合在一起:
> let exclude e list1 = filter (/= e) list1
> let listProduct x list2 = map (x :) list2
> let f x = listProduct x (permute (exclude x list))
<interactive>:1:25: Not in scope: `permute' -- permute is not defined yet!!
<interactive>:1:44: Not in scope: `list' -- need to pass 'list' in
> let f list x = listProduct x (undefined (exclude x list))
--------- -- use a stand-in for now
> let permute [] = [] ; permute list = map (f list) list
> :t permute ---
permute :: (Eq t) => [t] -> [[[t]]] -- one too many levels of list!
So they do fit together, it's just that the result is not what we want. 因此它们确实融合在一起,只是结果不是我们想要的。 Instead of changing what
f
produces (as the compiler suggested), we can change how its results are combined. 无需更改
f
生成方式(如编译器建议的那样),我们可以更改其结果的组合方式。 concat
removes one level of list nesting (it is the monadic join
for the []
type constructor): concat
删除一级列表嵌套(这是[]
类型构造函数的单子join
):
> let permute [] = [] ; permute list = concatMap (f list) list
> :t permute ---------
permute :: (Eq t) => [t] -> [[t]] -- much better
Incidentally, were you not to specify the type signature yourself, it would compile and report the [a] -> [[[a]]]
type back to you. 顺便说一句,如果您不自行指定类型签名,它将编译并向您报告
[a] -> [[[a]]]
类型。
More importantly, by bringing the /=
into the picture, you needlessly require an Eq a
constraint on the items in a list: permute :: (Eq a) => [a] -> [[a]]
. 更重要的是,通过将
/=
放入图片中,您不需要对列表中的项目使用Eq a
约束: permute :: (Eq a) => [a] -> [[a]]
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.