Я не очень хорошо знаю, насколько Haskell / GHC может оптимизировать код. Ниже у меня есть довольно "грубая сила" (в декларативном смысле) реализация задачи n ферзей. Я знаю, что это можно написать более эффективно, но вопрос не в этом. Это заставило меня задуматься о возможностях и ограничениях оптимизации GHC.
Я выразил это в том, что я считаю довольно простым декларативным смыслом. Отфильтруйте перестановки [1..n], которые удовлетворяют предикату Для всех индексов i, j st j Я надеюсь, что это то, что может быть оптимизированным, но это также похоже на то, что от компилятора требуется много усилий.
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
, и не заметил значительной разницы, но я не знаю более тонких моментов (просто добавил флажки)
Мой создается впечатление, что функция, которую я вызываю ферзей
, не может быть оптимизирована и должна генерировать все перестановки перед фильтром. У безточечной версии больше шансов?С одной стороны, мне кажется, что умное понимание функций между фильтром и предикатом могло бы отбросить некоторые явно нежелательные элементы еще до того, как они будут полностью сгенерированы, но с другой стороны, мне кажется, что спросить очень много.
Извините, если это кажется бессвязным, я предполагаю, что у меня такой вопрос:
ghc --make queensN -O2 -v
, на которую мне следует обратить внимание? Для меня ничего не выделяется. Даже не вижу большой разницы в выводе из-за флагов оптимизации . Я не слишком озабочен этим примером кода, но я думал, что его написание заставило меня задуматься, и это кажется мне достойным средством для обсуждения оптимизации.
PS - перестановки
взяты из 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)