Итак, короткая версия моего вопроса: как мы должны кодировать циклы в Haskell вообще? В Haskell нет гарантии оптимизации хвоста, шаблоны ударов даже не являются частью стандарта (правильно?), И парадигма fold/unfold не гарантируется работать во всех ситуациях. Здесь случай в точке были только баг-шаблонами, которые помогли мне заставить его работать в постоянном пространстве (даже при использовании $!
не помогло.. хотя тестирование было выполнено на Ideone.com, который использует ghc-6.8.2).
Это в основном о вложенном цикле, который в list-парадигме можно указать как
prod (sum,concat) . unzip $
[ (c, [r | t]) | k<-[0..kmax], j<-[0..jmax], let (c,r,t)=...]
prod (f,g) x = (f.fst $ x, g.snd $ x)
Или в псевдокоде:
let list_store = [] in
for k from 0 to kmax
for j from 0 to jmax
if test(k,j)
list_store += [entry(k,j)]
count += local_count(k,j)
result = (count, list_store)
До тех пор, пока я не добавлю к нему шаблон привязки, я получил либо выпадение памяти, либо даже переполнение стека. Но шаблоны ударов не являются частью стандарта, не так ли? Итак, вопрос в том, как кодировать выше, в стандартном Haskell, для работы в постоянном пространстве?
Вот тестовый код. Расчет является поддельным, но проблемы одинаковы. EDIT: foldr
-форменный код:
testR m n = foldr f (0,[])
[ (c, [(i,j) | (i+j) == d ])
| i<- [0..m], j<-[0..n],
let c = if (rem j 3) == 0 then 2 else 1 ]
where d = m + n - 3
f (!c1, []) (!c, h) = (c1+c,h)
f (!c1, (x:_)) (!c, h) = (c1+c,x:h)
Попытка запуска print $ testR 1000 1000
создает переполнение стека. Переход на foldl
возможен только при использовании шаблонов bang в f
, но он строит список в обратном порядке. Я хотел бы построить его лениво и в правильном порядке. Может ли это быть сделано с любым типом fold
, для идиоматического решения?
РЕДАКТИРОВАТЬ:, чтобы подытожить ответ, который я получил от @ehird: нечего бояться, используя шаблон взлома. Хотя он и не в стандартном Haskell, он легко кодируется в нем как f ... c ... = case (seq c False) of {True -> undefined; _ -> ...}
. Урок состоит в том, что только совпадение шаблона заставляет значение, а seq
делает НЕ что-то само собой, а скорее упорядочивает, когда seq x y
принудительно - по совпадению с шаблоном - x
будет тоже, и y
будет ответом. Вопреки тому, что я мог понять из онлайн-отчета, $!
делает НЕ принудительно что-то само собой, хотя называется "строгим оператором приложения".
И точка от @stephentetley - строгость очень важна для управления пространственным поведением. Таким образом, вполне нормально кодировать циклы в Haskell с правильным использованием аннотаций строгости с шаблонами помех, где необходимо, для написания любой специальной функции складывания (то есть структуры), которая нужна - как, в конце концов, я делал это - и полагаться на GHC для оптимизации кода.
Спасибо вам всем за вашу помощь.