Karva notation is used in Gene Expression Programming to represent mathematical expressions.
See here http://www.gene-expression-programming.com/Tutorial002.asp
You create an expression tree by reading the off the gene and filling in nodes from left to right, top to bottom.
So for example using the operators ( +, * ) and terminals (1,2,3,4,5,6) in "+*+1+2*3456" would evaluate to 39.
How would I do this in haskell using attoparsec (or parsec)?
karvaParser :: Parser Int
karvaParser = ????????????
Prelude> parse karvaParser "+*+1+2*3456"
Done 39
(I've proved this is a linear time algorithm in this answer to the question mentioned in the comments. There's a lengthier more hand-rolled solution in a previous revision of this answer.)
Gene Expression Programming: Karva notation.
There's probably a neat solution using the continuation passing monad,
Cont
, but I haven't thought of it. Here's a fairly clean pure functional solution to the problem. I'll take the opportunity to name drop some good general recursion schemes along the way.Plan:
split the input into lists, one for each layer, using the total arity of the previous line. This is an anamorphism, i.e. grows a list from a seed (
[]
) and can be written usingunfoldr :: (b -> Maybe (a, b)) -> b -> [a]
or equivalently,unfoldr' :: (b -> (a, b)) -> (b -> Bool)-> b -> [a]
Recursively use
splitAt
to glue the children under the parent. This is a catamorphism, i.e. collapses a list down to a single (tree) value, and can be written usingfoldr :: (a -> b -> b) -> b -> [a] -> b
Combine the anamorphism and the catamorphism into one. That's called a hylomorphism. These terms are introduced to the FP community in the seminal paper Functional Programming with Bananas, Lenses and Barbed wire.
Code
In case you're not familiar with it,
Data.Tree
suppliesdata Tree a = Node {rootLabel :: a, subForest :: Forest a}
wheretype Forest a = [Tree a]
.To pull out a level, we use the total arity from the previous level to find where to split off this new level, and pass on the total arity for this one ready for next time:
To combine a level (as a String) with the level below (that's already a Forest), we just pull off the number of trees that each character needs.
Now we can parse the Karva using a hylomorphism. Note that we seed it with a total arity from outside the string of
1
, since there's only one node at the root level. I've used thehead
function because that1
causes the top level to be a list containing one tree.Demo
Let's have a draw of the results (because Tree is so full of syntax it's hard to read the output!). You have to
cabal install pretty-tree
to getData.Tree.Pretty
.Which matches
as we see when we
see
it:Eval
Once you have a Tree, it's easy to convert it to other things. Let's evaluate an expression in Karva notation: