我正在codewar做一个与Haskell相关的问题,就是为着名的深奥语言Brainfuck写一个翻译。
最初,我正在考虑使用Array
编写程序。在我开始实现解释器之后,我意识到解释器的效率会有多低,因为数组有很多变化。然后我切换到使用STArray
。但是除了保存数组指针数组之外,我还需要输出String
的可变引用,这在STArray
中是不可能的。所以我完全惊呆了。
写一个monadic解析器可能是个好主意,我当时想。但事实证明,我不知道应该用什么样的表达来模拟问题。我只阅读了一些关于monadic解析风格的论文,这些都是。 Brainfuck比天真Add
和Minus
等更复杂。
感谢任何解决问题的指导。以下是我的代码。我在这里发布它只是为了表明代码是多么混乱。不要尝试编译它,因为它充满了类型错误。
executeString' :: String -> String -> Maybe String
executeString' [] _ = Just ""
executeString' "," _ = Nothing
-- executeString' source input = Just $ map chr $ elems $ consume
length' ::String -> Int
length' source = right - left
where step (l, r) '>' = (l, r+1)
step (l, r) '<' = (l+1, r)
(left, right) = foldl' step (0, 0) source
-- decrement the data pointer
neverMinus :: Int -> Int
neverMinus n = if n == 0 then 255 else n - 1
-- increment the data pointer
alwaysPlus :: Int -> Int
alwaysPlus n = if n == 255 then 0 else n + 1
consume :: String -> String -> (Array Int Int, Array Int Char)
consume source input = runSTArray $ do
pointer <- newArray (0, arrlength') 0
forM_ source $ \t -> do
pointed <- readArray pointer point
elem <- readArray pointer pointed
isWrong <- readArray pointer error
status' <- readArray pointer status
when (1 == isWrong) $ return 0
when (doJump status') $ return 0
case t of
'>' -> writeArray pointer point (pointed + 1)
'<' -> writeArray pointer point (pointed - 1)
'+' -> writeArray pointer pointed (alwaysPlus elem)
'-' -> writeArray pointer pointed (neverMinus elem)
',' -> do index <- readArray pointer inputIndex
writeArray pointer pointed (ord $
head . drop index input)
writeArray pointer inputIndex (index+1)
1
'[' -> writeArray pointer status jump
']' -> writeArray pointer status execute
return pointer
where arrlength' = length'' + 4
length'' = length' source
strlength' = 1 + foldl' (\count s -> case s of
'.' -> count + 1) 0 source
point = length'' + 1
inputIndex = length'' + 2
status = length'' + 3 -- should the program execute current instruction or jump
error = length'' + 4 -- if there is program error during execution
-- Program status
jump = 1
execute = 0
doJump :: Int -> Bool
doJump jump = True
duJump execute = False
答案 0 :(得分:0)
使用像haskell这样的语言编写brainfuck解释器的一些技巧。
最初,我在考虑使用Array编写程序。在我开始实现解释器之后,我意识到解释器的效率会有多低,因为数组有很多变化。然后我切换到使用STArray。但是除了保存数组指针数组之外,我还需要输出String的可变引用,这在STArray中是不可能的。所以我完全惊呆了。
关于List Zippers的研究。这是一个可以定义的数据结构 - &gt; [枢轴元素的左侧] {透视元素} [枢轴元素的右侧],在代码中看起来像。
`data Tape a = Tape [a] a [a]`
您可以轻松地在此数据类型上定义>
,<
,+
,-
等。
编写一个monadic解析器可能是一个好主意,我当时想。但事实证明我不知道我应该用什么样的表达来模拟问题。我只阅读了一些关于monadic解析风格的论文,这就是全部。 Brainfuck比天真的Add和Minus等复杂得多
事实并非如此,Brainfuck比典型的算术解析器复杂得多。正如其中一条评论所指出的那样。如下所示,应该让你走上正轨。
stripComments = filter (`elem` "+-<>[],.")
```
token :: Parser Token
token = const TRight <$> char '>'
<|> const TLeft <$> char '<'
<|> const TInc <$> char '+'
<|> const TDec <$> char '-'
<|> const TPrint <$> char ','
<|> const TRead <$> char '.'
<|> const TLoopS <$> char '['
<|> const TLoopE <$> char ']'
```
最后你需要一个评估策略,我会使用类似下面的内容。
eval :: String -> Tape Token -> Tape Int -> String
其中第一个参数是程序的输入,Tape Token
将是解析的程序,Tape Int
将是磁带上的操纵值,您将在其上应用值,最后一个参数将是输出。
我相信这可以帮助你走上正轨。
我曾经写过类似的https://gist.github.com/sherubthakur/16a784e61efbe54d885ad60c6e18f254。