Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

haskell - types - functions - trees

For haskell practice I want to implement a game where students/pupils should learn some algebra playfully.

As basic datatype I want to use a tree:

  • with nodes that have labels and algebraic operators stored.
  • with leaves that have labels and variables (type String) or numbers

Now I want to define something like

data Tree = Leaf {l :: Label, val :: Expression}
          | Node {l :: Label, f :: Fun, lBranch :: Tree, rBranch :: Tree}

data Fun = "one of [(+),(*),(-),(/),(^)]"

-- type Fun = Int -> Int 

would work

Next things I think about is to make a 'equivalence' of trees - as multiplication/addition is commutative and one can simplify additions to multiplication etc. the whole bunch of algebraic operations. I also have to search through the tree - by label I think is best, is this a good approach.

Any ideas what tags/phrases to look for and how to solve the "data Fun".

like image 674
epsilonhalbe Avatar asked May 18 '11 18:05

epsilonhalbe


People also ask

What is a rose tree Haskell?

A rose tree [...] is either a leaf containing a value, or a node that can have an arbitrary list of subtrees . The most common definition used in functional programming (particularly in Haskell) combines 3+2b: data Rose α = Node α [Rose α]

How types are used in Haskell?

However, understanding the type system is a very important part of learning Haskell. A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

What are binary trees in data structure?

A binary tree is a rooted tree that is also an ordered tree (a.k.a. plane tree) in which every node has at most two children. A rooted tree naturally imparts a notion of levels (distance from the root), thus for every node a notion of children may be defined as the nodes connected to it a level below.


2 Answers

To expand a bit on Edward Z. Yang's answer:

The simplest way to define your operators here is probably as a data type, along with the types for atomic values in leaf nodes and the expression tree as a whole:

data Fun = Add | Mul | Sub | Div | Exp deriving (Eq, Ord, Show)

data Val a = Lit a | Var String deriving (Eq, Ord, Show)

data ExprTree a = Node String Fun (ExprTree a) (ExprTree a)
                | Leaf String (Val a)
    deriving (Eq, Ord, Show)

You can then define ExprTree a as an instance of Num and whatnot:

instance (Num a) => Num (ExprTree a) where
    (+) = Node "" Add
    (*) = Node "" Mul
    (-) = Node "" Sub
    negate = Node "" Sub 0
    fromInteger = Leaf "" . Lit

...which allows creating unlabelled expressions in a very natural way:

*Main> :t 2 + 2
2 + 2 :: (Num t) => t
*Main> 2 + 2 :: ExprTree Int
Node "" Add (Leaf "" (Lit 2)) (Leaf "" (Lit 2))

Also, note the deriving clauses above on the data definitions, particularly Ord; this tells the compiler to automatically create an ordering relation on values of that type. This lets you sort them consistently which means you can, for instance, define a canonical ordering on subexpressions so that when rearranging commutative operations you don't get stuck in a loop. Given some canonical reductions and subexpressions in canonical order, in most cases you'll then be able to use the automatic equality relation given by Eq to check for subexpression equivalence.

Note that labels will affect the ordering and equality here. If that's not desired, you'll need to write your own definitions for Eq and Ord, much like the one I gave for Num.

After that, you can write some traversal and reduction functions, to do things like apply operators, perform variable substitution, etc.

like image 166
C. A. McCann Avatar answered Oct 21 '22 09:10

C. A. McCann


It looks like you want to construct a symbolic algebra system. There is a large and varied literature on the subject.

You don't want to represent operators as Int -> Int, because then you can't check what operation any given function implements and then implement peephole optimization for things like simplification, etc. So a simple enumerated data type would do the trick, and then write the function eval which actually evaluates your tree.

like image 3
Edward Z. Yang Avatar answered Oct 21 '22 10:10

Edward Z. Yang