Brainfuck コードを解析して実行する小さなスクリプトを作成しようとしています。最適化の GHC オプションを理解するために、コードを最適化して、少し高速化し、そこで何が起こっているのかを理解しようとしています。
パーツの 1 つは BF コードの内部表現です。これには特別なデータ型を使用します。ソースコードは次のとおりです。変換を行う 2 つの関数が含まれています。
data BFinstruction
= AdjustValue Int
| MovePointer Int
| GetChar
| PutChar
| Loop BFcode
deriving (Eq)
type BFcode = [BFinstruction]
unsafeCompileBrainfuck :: String -> BFcode
unsafeCompileBrainfuck = fst . parse [] where
-- arguments: input string, built code; output: output code, rest of input
parse :: BFcode -> String -> (BFcode,String)
parse c ('+':s) = parse (AdjustValue 1 :c) s
parse c ('-':s) = parse (AdjustValue (-1):c) s
parse c ('>':s) = parse (MovePointer 1 :c) s
parse c ('<':s) = parse (MovePointer (-1):c) s
parse c ('.':s) = parse (PutChar :c) s
parse c (',':s) = parse (GetChar :c) s
parse c (']':s) = (reverse c, s)
parse c ('[':s) = parse (Loop l :c) s' where (l,s') = parse [] s
parse c [] = (reverse c ,"")
parse c ( _ :s) = parse c s
simplifyBrainfuck :: BFcode -> BFcode
simplifyBrainfuck ((AdjustValue x):(AdjustValue y):zs) = if x + y /= 0
then simplifyBrainfuck (AdjustValue (x + y):zs)
else simplifyBrainfuck zs
simplifyBrainfuck ((MovePointer x):(MovePointer y):zs) = if x + y /= 0
then simplifyBrainfuck (MovePointer (x + y):zs)
else simplifyBrainfuck zs
simplifyBrainfuck (x :zs) = x: simplifyBrainfuck zs
simplifyBrainfuck [] = []
アイデアは、コードが何らかの入力 (文字列) から読み取られ、上記のコードによって事前に解析および単純化されてから、他の関数によって実行されるというものです。(入力が有効であると仮定します)。
この例を最適化するために、次のような domething を実行して、コンストラクターMovePointer
とコンストラクターの Int パラメーターをアンボックスしようとしました。AdjustValue
data BFinstruction -- BangPatterns
= AdjustValue {-# UNPACK #-} !Int
| MovePointer {-# UNPACK #-} !Int
| GetChar
| PutChar
| Loop BFcode
deriving (Eq)
これにより、ボックス化された型が、GHcの実装の詳細であるボックス化Int
されていない生の型に変わります。Int#
私が読んだように、このオプションはいくつかの場合にのみ有効です。そのため、この種の最適化を実行する場合、どの点に注意する必要があるかを尋ねたいと思います。私の目標は、Haskell の利点を利用して BF コードを実行できるようにすることです。