20/05/2010
I’ve fixed a bug related to upgrading GHC to version 6.12 (thanks to Cale and the folks on haskell-cafe who helped me with the issue) and got my Befunge-93 interpreter up on hackage. The program is written in haskell (as usual). You should be able to get it soon with a:
$> cabal install Befunge93
If you want to read about how I designed it you can check out the source above, or take a look at my previous blog post.
Please report any bugs to me, and I’m also very interested in patches or suggestions for performance improvements if anyone ends up being interested in this program.
EDIT: Here is the package page: http://hackage.haskell.org/package/Befunge93
31/01/2010
I just finished an initial release of an interpreter for the befunge programming language, based on the ‘93 spec. The project was quite fun! My goal was to produce a well-designed program with performance that didn’t suck too bad. Here are some highlights:
Design
I found that writing the core functionality of the interpreter took almost no time, once I settled on the approach I would take. I used a monad transformer for the first time, StateT:
type REPL a = StateT ProgramState IO a
This let me pass around the state of the computation in the State monad while doing IO actions. This made some potentially-awkward befunge commands really easy to implement.
Read the rest of this article »
22/08/2009
I put together a skeleton module which should be usable for any of the problems in this year’s Google CodeJam Programming Contest (as long as they don’t adopt a new format/pattern for the problems this year). The module includes some useful parsing functions which should in the worst case be useful as a cheatsheet for those not too experienced with Parsec (*cough me). I hope it will encourage people to sign up and use Haskell in the contest!
To adapt the module to a problem, one defines the following:
- a type
Case into which we will parse a test case (i.e. problem)
- a type
SolvedCase, representing a solution to a test case
- our
algorithm :: Case -> SolvedCase
- a Parser
caseParser which can parse a single test case (n.b. not the whole file) into the Case type
- a function
formatCase :: SolvedCase -> String which brings the solution to the state where it can be prepended with the standard “Case #1: ” string
Here is the module, filled in to solve the Minimum Scalar Product practice problem. You can download the code here:
module Main
where
import Text.ParserCombinators.Parsec
import IO hiding (try)
import System.Environment
import Data.List (sort)
import Control.Arrow
type Input = [Case]
type Solution = [SolvedCase]
type Output = [String]
type Case =
([Int],[Int])
type SolvedCase =
Int
algorithm :: Case -> SolvedCase
algorithm =
sum . uncurry (zipWith (*)) . (reverse . sort *** sort)
caseParser :: Parser Case
caseParser = do
w <- word
let n = read w
as <- count n word
bs <- count n word
return (map read as, map read bs)
formatCase :: SolvedCase -> String
formatCase sol =
show sol
main = do
(f:_) <- getArgs
file <- readFile f
let inp = parseWith mainParser file
solution = map algorithm inp
solutionStrings = map formatCase solution
outp = zipWith (++) prefixes solutionStrings
putStr $ unlines outp
parseWith p = either (error . show) id . parse p ""
mainParser :: Parser Input
mainParser = do
n <- word
ms <- count (read n) caseParser
return ms
<?> "mainParser"
prefixes = [ "Case #" ++ show n ++ ": " | n <- [1..]]
wholeLine :: Parser String
wholeLine = manyTill anyChar (try newline) <?> "wholeLine"
whiteSepLine = manyTill spaceSepWord newline <?> "whiteSepLine"
word = do
w <- many1 nonWhite
spaces
return w
<?> "word"
spaceSepWord = do
w <- many1 nonWhite
many (char ' ')
return w
<?> "spaceSepWord"
twoWordsSepBy c = do
x <- manyTill nonWhite (try$ char c)
y <- many1 nonWhite
many (char ' ')
return (x,y)
<?> "twoWordsSepBy"
nonWhite = noneOf " \v\f\t\r\n" <?> "nonWhite"