我对Haskell/GHC优化代码的程度不是很熟悉。下面是一个相当“暴力”(在声明性方面)的n皇后问题实现。我知道它可以更有效地编写,但这不是我的问题。我的问题是这让我开始思考GHC优化能力和限制。
我以我认为相当直接的声明方式表达了它。过滤满足谓词对于所有索引i,j s.t j<i, abs(vi - vj) != j-i
的[1..n]的排列。我希望这是一种可以优化的东西,但它也感觉像是要求编译器做很多事情。
validQueens x = and [abs (x!!i - x!!j) /= j-i | i<-[0..length x - 2], j<-[i+1..length x - 1]]
queens n = filter validQueens (permutations [1..n])
oneThru x = [1..x]
pointlessQueens = filter validQueens . permutations . oneThru
main = do
n <- getLine
print $ pointlessQueens $ (read :: String -> Int) n
这段代码运行缓慢且增长迅速。当
n=10
时需要大约一秒钟,而n=12
则需要很长时间。没有优化的情况下,我可以确定增长是阶乘(排列数)乘以二次方程式(要检查的谓词中的差异数)。是否有任何方法可以通过智能编译使此代码更快?我尝试了基本的ghc
选项,如-O2
,但没有注意到显着的差异,但我不知道细节(只添加了标志)。我的印象是,我调用的函数
queens
不能被优化,并且必须在过滤之前生成所有排列。点无版本有更好的机会吗?一方面,我觉得一个聪明的函数理解可能会在完全生成之前从筛选器和谓词之间削减一些明显不需要的元素,但另一方面,这似乎有点困难。如果这看起来有点混乱,请原谅,我想问的问题是:
- 以上函数的点无版本是否更有优化能力?
- 我可以采取哪些步骤,在编译/链接时鼓励优化?
- 您能简要描述一些可能的(并与不可能进行对比!)上述代码的优化方法吗?这些过程在哪个阶段发生?
- 我应该关注
ghc --make queensN -O2 -v
输出的哪个特定部分?对我来说没有什么特别突出的地方。甚至由于优化标志而看不到太多输出差异。
PS-
permutations
来自Data.List,看起来像这样:permutations :: [a] -> [[a]]
permutations xs0 = xs0 : perms xs0 []
where
perms [] _ = []
perms (t:ts) is = foldr interleave (perms ts (t:is)) (permutations is)
where interleave xs r = let (_,zs) = interleave' id xs r in zs
interleave' _ [] r = (ts, r)
interleave' f (y:ys) r = let (us,zs) = interleave' (f . (y:)) ys r
in (y:us, f (t:y:us) : zs)
oneThru = enumFromTo 1
。(翻译:BTW是“顺便提一句”的意思;oneThru = enumFromTo 1
表示将从1开始的整数序列生成为一个枚举类型) - augustss(.)
函数组合是否会在幕后发生任何聪明的事情,看起来答案是否定的。 - jon_darkstar