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"
14/08/2009
EDIT: Don’t use this package, but use instead Data.List.Split by Brent Yorgey. I didn’t see that a package like his existed! This module will hopefully be removed from hackage if they can do that.
I just finished the initial release of a simple module called list-grouping that contains functions to partition a list into sub-lists in various ways, based on some predicate or integer offset. Functions like these are a little awkward to write and I was surprised when I didn’t see anything on hackage!
Check out the package description and install it with:
$ cabal install list-grouping
Here is an example from a previous post to build a binary tree from an in-order list, which uses the above library:
module Main
where
import Data.List.Grouping
data Tree a = Node a (Tree a) (Tree a)
| End
deriving Show
fromSorted :: [a] -> Tree a
fromSorted = foldl mkTree End . splitWith [2^n | n<-[0..]]
where mkTree l (n:ns) = Node n l (fromSorted ns)
I’m sure the functions can be made more efficient, to take advantage of fusion or what-not, and I hope the library will eventually contain the most efficient implementations possible.
I also am looking for suggestions for other useful list grouping functions to include. Send your suggestions along! You can get the darcs source with:
$ darcs get http://coder.bsimmons.name/code/ListGrouping/