В вашем случае это проблема строгости, которая вызывает переполнение стека. Один очень простой способ найти такие проблемы - использовать deepseq library. Это добавляет несколько функций, которые позволяют вам полностью оценить значение (которое лучше, чем seq
, которое выходит только на один уровень). Ключевая функция - force :: NFData a => a -> a
. Это принимает значение, полностью оценивает его и возвращает его.
Он работает только с типами, которые реализуют класс типа NFData
. К счастью, есть макрос шаблона haskell в deepseq-th library: deriveNFData
. Это используется с вашими собственными типами данных, например deriveNFData ''BfMachine
.
Чтобы использовать, вы ставите force $
перед своими функциями, которые могут иметь проблемы с строгими ограничениями (или liftM force $
для монадических функций).Например, с кодом, я поставил его перед step
, так как это была ключевая функция в файле:
{-# LANGUAGE TemplateHaskell #-}
import Data.Char
import Debug.Trace
import Control.DeepSeq
import Control.DeepSeq.TH
import Control.Monad (liftM)
type Stack = [Int]
data BfMachine = BfMachine
{ program :: String
, pc :: Int
, stack :: Stack
, sp :: Int
} deriving Show
deriveNFData ''BfMachine
setElem :: [Int] -> Int -> Int -> [Int]
setElem list n value = map (\(i, v) -> if i == n then value else v) (zip [0..] list)
step :: BfMachine -> IO (BfMachine)
step [email protected](BfMachine { program = program, pc = pc, stack = stack, sp = sp }) = liftM force $
case program !! pc of
'-' -> return m { pc = pc + 1, stack = setElem stack sp ((stack !! sp) - 1) }
'+' -> return m { pc = pc + 1, stack = setElem stack sp ((stack !! sp) + 1) }
'<' -> return m { pc = pc + 1, sp = sp - 1 }
'>' -> return m { pc = pc + 1, sp = sp + 1 }
'[' -> return $ if stack !! sp /= 0 then m { pc = pc + 1 }
else m { pc = (findNextBracket program $ pc + 1) + 1 }
']' -> return m { pc = findPrevBracket program $ pc - 1 }
'.' -> do putChar $ chr $ stack !! sp
return m { pc = pc + 1 }
',' -> do c <- getChar
let s' = setElem stack sp $ ord c
in return m { stack = s', pc = pc + 1 }
a -> return m { pc = pc + 1 }
findNextBracket :: String -> Int -> Int
findNextBracket program pos =
case program !! pos of
'[' -> findNextBracket program $ (findNextBracket program $ pos + 1) + 1
']' -> pos
x -> findNextBracket program (pos + 1)
findPrevBracket :: String -> Int -> Int
findPrevBracket program pos =
case program !! pos of
']' -> findPrevBracket program $ (findPrevBracket program $ pos - 1) - 1
'[' -> pos
x -> findPrevBracket program (pos - 1)
isFinished :: BfMachine -> Bool
isFinished [email protected](BfMachine { program = p, pc = pc })
| pc == length p = True
| otherwise = False
run :: BfMachine -> IO()
run m = do
if isFinished m then
return()
else do
m <- step m
run m
fib = ">++++++++++>+>+[ [+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[ [-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- [>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>> ]<<< ] This program doesn't terminate; you will have to kill it. Daniel B Cristofani (cristofdathevanetdotcom) http://www.hevanet.com/cristofd/brainfuck/"
main = run BfMachine { program = fib , pc = 0, stack = replicate 1024 0, sp = 0 }
Это фактически решает проблему - даже после нескольких минут работы, он не разбился и память использование составляет всего 3,2 МБ.
Вы можете придерживаться этого решения или попытаться найти, где находится настоящая проблема строгости (поскольку это делает все строгим). Вы делаете это, удаляя силу из функции step
и пробуя ее на вспомогательные функции, которые она использует (например, setElem
, findPrevBacket
и т. Д.). Оказывается, виновником является setElem
, поставив force
перед этой функцией, также решая проблему строгости. Я предполагаю, что это связано с тем, что if
на карте лямбда означает, что большинство значений никогда не нужно оценивать в списке и, возможно, создавать огромные трюки по мере продолжения программы.
как вы скомпилировали? ghc -O2 blah.hs может быть в состоянии оптимизировать лучше –
Спасибо, но это не помогло – Philippe
Вы могли бы предоставить ссылку на pastefin код? – jev