Haskell Funktionen Definieren Sie eine Funktion circleArea zur Berechnung der Fläche eines Kreises mit gegebenen Radius (Float). circleArea :: Float -> Float circleArea radius = 2 * pi * radius^2 Definieren Sie zwei Funktionen min und max, die das Minimum bzw. Maximum zweier Integers bestimmen. min :: Integer -> Integer -> Integer min x y = if x < y then x else y max :: Integer -> Integer -> Integer max x y = if x > y then x else y Definieren Sie zwei Funktionen abs und signum, die den Absolutbetrag bzw. das Vorzeichen einer Integer-Zahl berechnet. abs :: Integer -> Integer abs x = if x < 0 then -x else x signum :: Integer -> Integer signum x | x == 0 = 0 |x<0 = -1 |x>0 =1 1 Definieren Sie eine Funktion fib, die die n-te Fibonacci-Zahl berechnet. fib :: Integer -> Integer fib n | n == 0 = 1 | n == 1 = 1 | n >= 2 = fib(n-2) + fib(n-1) | otherwise = error „fib mit negativem Argument aufgerufen“ Unendliche Liste der Fibonacci-Zahlen: fib n m = n : (fib m (n+m)) fib1 n = (take1 n (fib 1 1)) take1 0 _ = [] take1 _ [] = [] take1 (n+1) (x : xs) = x : take1 n xs take :: Int -> [a] -> a take n (x : xs) = if n == 0 then x else take (n-1) xs Definieren Sie zwei Funktionen even und odd für Integer. even :: Integer -> Bool even n = (n ‘mod’ 2 == 0) odd :: Integer -> Bool odd n = (n ‘mod’ 2 == 1) -- noch einfacher ist odd n = not (even n) Definieren Sie eine Funktion eqMod, die testet, ob zwei Integer-Zahlen modulo n gleich sind. eqMod :: Integer -> Integer -> Integer -> Bool eqMod x y n = ((x - y) ’mod’ n == 0) -- äquivalent zu x ‘mod’ n == y ‘mod’ n 2 Definieren Sie eine Funktion hoch, die xn für Integer-Zahlen berechnet. Verwenden Sie 1. rekursive Multiplikation mit x, 2. rekursive Quadrierung (xn = (x(n/2))2 für gerade n). hoch1 :: Integer -> Integer -> Integer hoch1 x n |n>0 = x * hoch1 x (n-1) | n == 0 =1 | otherwise = error “hoch1 mit negativem Exponenten“ hoch2 :: Integer -> Integer -> Integer hoch2 x n |n<0 = error “hoch1 mit negativem Exponenten“ | n == 0 =1 | even n = (hoch2 x (n ’div’ 2))^2 | odd n = x * hoch2 (n - 1) Definieren Sie eine Funktion toUpper und eine Funktion toLower, die Kleinbuchstaben in Großbuchstaben umwandelt bzw. umgekehrt. Jedes andere Zeichen soll unverändert bleiben. --Beruht darauf, daß die Buchstaben des Alphabets in der (ASCII-)Codierung hintereinander --stehen toUpper :: Char -> Char toUpper c | ‘a’ <= c && c <= ’z’ | otherwise = char (ord c –ord ’a’ + ord ‘A’) =c toLower :: Char -> Char toLower c | ‘A’ <= c && c <= ’Z’ | otherwise = char (ord c –ord ’A’ + ord ‘a’) =c Definieren Sie eine Funktion head, die das erste Zeichen eines Strings zurückgibt, und eine Funktion tail, die das letzte Zeichen eines Strings bestimmt. head :: String -> Char head string = string !! 0 tail :: String -> Char tail string = string !! (length string –1) 3 Definieren Sie eine Funktion swapT, die zwei boolsche Werte eines Tupels vertauscht. swapT :: (Bool, Bool) -> (Bool, Bool) swapT tupel = (snd tupel, fst tupel) Definieren Sie eine Funktion sortT, die zwei boolsche Werte eines Tupels ordnet. sortT :: (Bool, Bool) -> (Bool, Bool) sortT tupel = if fst tupel > snd tupel then swapT tupel else tupel Definieren Sie eine Funktion distance mit zwei Argumenten, die zu zwei Punkten in einem zweidimensionalen Koordinatensystem (Datentyp Float) deren Abstand berechnet. distance :: (Float, Float) -> (Float, Float) -> Float distance point1 point2 = sqrt ((fst point1 – fst point2)^2 + (snd point1 – snd point2)^2 Definieren Sie eine Funktion xor, die das logische exklusive Oder berechnet. 1. Mit Patternmatching 2. Ohne Patternmatching xor1 :: Bool -> Bool -> Bool xor1 x y = x /= y xor2 :: Bool -> Bool -> Bool xor2 True True = False xor2 False False = False xor2 True False = True xor2 false True = True Definieren Sie eine Funktion toUpperStr und eine Funktion toLowerStr, die in einer ganzen Zeichenkette Kleinbuchstaben in Großbuchstaben umwandelt bzw. umgekehrt. toUpperStr :: String -> String toUpperStr [] = [] toUpperStr (x : xs) = toUpper x : toUpperStr xs toLowerStr :: String -> String toLowerStr [] = [] toLowerStr (x : xs) = toLower x : toLowerStr xs 4 Definieren Sie eine Funktion elem, die prüft, ob ein Zeichen Element eines Strings ist. elem :: Char -> String -> Bool elem c [] = False elem c (x:xs) | c == x = True | otherwise = elem c xs Definieren Sie eine Funktion product, die alle Integers einer Liste miteinander multipliziert. product :: [Integer] -> Integer product [] =1 product (x : xs) = x * product xs Definieren Sie eine Funktion sumOfLists, die zu einer Liste von Listen, deren Einträge vom Typ Integer sind, die Summe der Listeneinträge berechnet. sumOfLists :: [[Integer]] -> Integer sumOfLists [] = [] sumOfLists (x : xs) = sum x : sumOfLists xs Definieren Sie eine Funktion maxMinList, die in einem rekursiven Listendurchlauf das Maximum und das Minimum einer Liste von Integers bestimmt. maxMinList :: [Int] -> (Int, Int) maxMinList [] = (minBound, maxBound) --vordefiniert als kleinster und größter Wert des Typs. --Fehlermeldung für leere Liste wäre hier auch sinnvoll. maxMinList (x : xs) = (max x maxList, min x minList) where (maxList, minList) = maxMinList xs 5 Implementieren Sie Sortierfunktionen für Listen von Integers, mit den folgenden Algorithmen: • Insertion-Sort • Bubble-Sort • Merge-Sort A) Insertion-Sort insSort :: [Int] -> [Int] insSort [] = [] insSort (x : xs) = insert x (insSort xs) Einfügen einer Zahl an der der Ordnung entsprechenden Stelle. insert :: Int -> [Int] -> [Int] insert e [] = [e] insert e (x : xs) | e <= x = e : x :xs | otherwise = x : insert e xs B) Bubble-Sort Einmaliges Durchlaufen der Liste mit Vertauschen falsch sortierter benachbarter Elemente. bubble :: [Int] -> [Int] bubble [] = [] bubble (x : []) = [x] bubble (x : y : xs) | y > x = y : bubble (x : xs) | otherwise = x : bubble (y : xs) bubbleSort :: [Int] -> [Int] bubbleSort [] = [] bubbleSort xs = x : bubbleSort ys where (x : ys) = reverse (bubble xs) C) Merge-Sort Zusammenmischen zweier sortierter Listen. merge :: [Int] -> [Int] -> [Int] merge l1 [] = l1 merge [] = l2 = l2 merge (e1 : l1) (e2 : l2) | e1 < e2 = e1 : merge l1 (e2 : l2) | otherwise = e2 : merge (e1 : l1) l2 mergeSort :: [Int] -> [Int] mergeSort [] = [] mergeSort [e] = [e] mergeSort liste = merge (mergeSort links) (mergeSort rechts) where (links, rechts) = splitAt (length liste ’div’ 2) liste 6 Definieren Sie eine Funktion rotateL2 bzw. rotateL3, die die Elemente eines 2er bzw. 3er Tupels um eine Stelle nach links rotiert. rotateL2 :: (a, b) -> (b, a) rotateL2 (x, y) = (y, x) rotateL3 :: (a, b, c) -> (b, c, a) rotateL3 (x, y, z) = (y, z, x) Definieren Sie eine Funktion oddListEl, die aus einer Liste die Teilliste der Einträge mit ungeradem Index herausfiltert und diese zurückgibt. oddListEl :: [a] -> [a] oddListEl [] = [] oddListEl [_] = [] oddListEl (x : y : zs) = y : oddListEl zs Definieren Sie eine Funktion swapListEl, die die benachbarten Einträge einer Liste vertauscht, d.h. den 1. mit dem 2., den 3. mit dem 4., usw. swapListEl :: [a] -> [a] swapListEl [x] = [x] swapListEl (x : y : zs) = y : x : swapListEl zs Definieren Sie eine Funktion concat, die eine Liste von Listen durch Aneinanderhängen zu einer Liste verknüpft concat :: [[a]] -> [a] concat [] = [] concat (x : xs) = x ++ concat xs 7 Definieren Sie eine Funktion transpose, die eine Liste von Listen (gleicher Länge) als Matrix auffasst und diese Matrix transponiert (Vertauschen von Zeilen und Spalten). transpose :: [[a]] -> [[a]] transpose ((x11 : x1s) : xss) = (x11 : heads) : conss x1s (transpose tails) where (heads, tails) = headTails xss transpose _ = error “ Unterlisten nicht gleich lang in {transpose tails}“ conss :: [a] -> [[a]] -> [[a]] conss [] [] = [] conss (x : xs) (ys : yss) = (x : ys) : conss xs yss conss _ = error „Unterlisten nicht gleich lang in {transpose}“ headTails :: [[a]] -> ([a], [[a]]) headTails [] = ([], []) headTails ((x : xs) : xss) = (x : ys, xs : yss) where (ys, yss) = headTails xss headTails _ = error “Unterlisten nicht gleich lang in {transpose}“ Definieren Sie einen Operator *_*, der das Quadrat des zweiten Arguments vom Quadrat des ersten abzieht (Int-Zahlen). Testen Sie, welchen Unterschied es macht, diesen Operator als links-, rechts- oder nicht assoziativ zu definieren. Infixl 0 *-* -- hier auch infixr und infix ausprobieren (*-*) :: Int -> Int -> Int n *-* m = n^2 – m^2 -- zum Testen der Abhängigkeit von der Assiziativität: x = 7 *-* 5 *-* 3 Definieren Sie eine Funktion permutations, die zu einer Liste die Liste aller Permutationen dieser Liste bestimmt. permutations :: [a] -> [[a]] permutations [] = [] permutations (x : xs) = concat [insert x ys | ys <- permutations xs] -- Einfuegen des ersten Argumentes in einer Liste. -- Gibt eine Liste aller möglichen Ergebnislisten zurück. insert :: a -> [a] -> [[a]] insert x [] = [[x]] insert x zs@ (y : ys) 0 (x : zs) : [y : inserted | inserted <- insert x ys] 8 Definieren Sie eine Funktion isPermutation mit zwei Argumenten, die testet, ob die eine IntListe eine Permutation der anderen Liste ist. isPermutation :: [Int] -> [Int] -> Bool isPermutation list1 list 2 = list1 ‘elem’ permutations list2 Definieren Sie eine Funktion powerList, die zu einer Liste die Liste aller Permutationen von Teillisten erzeugt, die durch Streichen einiger Listenelemente entstehen (entspricht der Potenzmenge, wenn die Liste eine Menge darstellt). powerList :: [a] -> [[a]] powerListn [] = [[]] powerList (x : xs) = p ++ [x : y | y <- p] where p = powerList xs Definieren Sie eine Funktion zipWith :: (a -> b -> c) -> [a] -> [b] -> [c], die die Elemente zweier gleichlanger Listen durch eine Funktion paarweise verknüpft, und eine Funktion zip :: [a] -> [b] -> [(a, b)], die zwei gleichlange Listen zu einer Liste von Paaren verknüpft. zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith z (a : as) (b : bs) = z a b : zipWith z as bs zipWith _ _ _ = [] zip :: [a] -> [b] -> [(a, b)] zip = zipWith (\a b -> (a, b)) Definieren Sie eine Funktion mapMat, die ein map auf Matrizen berechnet. Eine Matrix kann durch eine Liste von Listen dargestellt werden. mapMat :: (a -> b) -> [[a]] -> [[b]] mapMat = map . map Definieren Sie eine Funktion factors, die die Listen aller Faktoren einer Integer-Zahl berechnet. Verwenden Sie hierbei die Funktion filter. factors :: Integer -> [Integer] factors n = filter divedsn [1 .. n] where divedsn m = n ‘mod’ m == 0 9 Definieren Sie eine Funktion unlines :: [String] -> String, die alle Strings einer Liste mit einem Zeilenendzeichen \n versieht und aneinanderhängt, ohne Verwendung expliziter Rekursion. unlines :: [String] -> String unlines = concat . map (\l -> l ++ “\n“) Definieren Sie eine Funktion reverse unter Verwendung von foldl. reverse :: [a] -> [a] reverse = foldl (flip (:)) [] Definieren Sie eine Funktion length unter Verwendung von foldl. length :: [a] -> Int length = foldl (\n _ -> n + 1) 0 Definieren Sie eine Funktion unzip :: [(a, b)] -> ([a], [b]), die die Umkehrung von zip unter Verwendung von foldr darstellt. unzip :: [(a, b)] -> ([a], [b]) unzip = foldr (\(a, b) xs -> let (ax, bs) = xs in (a : as, b : bs)) ([], []) Schreiben Sie eine Funktion square, die zu einem Parameter, der als Int übergeben wird, das Quadrat berechnet und zurückliefert. square :: Int -> Int square x = x* x Schreiben Sie eine Funktion cube, die den Kubik zu einem Integer berechnet. cube :: Int -> Int cube x = x + square x Schreiben Sie eine Funktion allEqual, die überprüft, ob drei übergebene Integers gleich sind. allEqual :: Int -> Int -> Int -> Bool allEqual n m p = (n == m) && (m == p) 10 Schreiben Sie eine Funktion hcf, die den größten gemeinsamen Teiler von zwei Zahlen bestimmt. hcf :: Int -> Int -> Int hcf n m | n == m = n | m > n = hcf m n | otherwise = hcf (n – m) m Zur besseren Strukturierung von Funktionsdefinitionen können sogenannte lokale Definitionen verwendet werden. Zum Beispiel: maxSquare n m | sqN > sqM = sqN | otherwise = sqM where sqN = square n sqM = square m square :: Int -> Int square z = z*z Schreiben Sie eine Funktion sumList, die eine Liste von Integern bekommt und die Summe der Integer aus der Liste als Ergebnis zurückliefert. sumList :: [Int] -> Int sumList [] = 0 sumList (a : xs) = a + sumList x Schreiben Sie eine Funktion double, die zu einer Liste von Integern eine Liste berechnet, die zu jedem Eintrag das Doppelte enthält. double :: [Int] -> [Int] double [] = [] double (a : xs) = (2 * a) : double x Schreiben Sie eine Funktion append, die zwei Listen von Integern zu einer Liste verkettet. append :: [Int] -> [Int] -> [Int] append [] y 0 y append (a : x) y = a : (append x y) 11 Schreibe eine Funktion, die eine Liste von Zahlen sortiert. Verwende dazu den folgenden rekursiven Algorithmus (Insertsort): Wenn [a1, ..., an] die zu sortierende Liste ist, dann sortiere die Liste [a2, ..., an] und füge das Element a1 an der passenden Stelle ein. iSort :: [Int] -> [Int] iSort [] = [] iSort (a : x) = ins a (iSort x) ins :: Int -> [Int] -> [Int] ins a [] = [a] ins a (b : y) | a <= b = a : (b : y) | otherwise = b : ins a y Die Funktion map nimmt ein Argument vom Typ Int -> Int und liefert eine Funktion vom Typ [Int] -> [Int] als Ergebnis. Damit lässt sich zum Beispiel sehr einfach aus einer Liste von Zahlen eine Liste der mit 2 multiplizierten Zahlen konstruieren. map :: (Int -> Int) -> [Int] -> [Int] map f [] = [] map f (a : x) = (f a) : (map f x) double :: Int -> Int double x = 2 * x map double [1, 2, 4] = (double 2) : (map double [1, 4]) = (double 2) : ((double 1) map double [4]) = (double 2) : ((double 1) : ((double 4) (map double []))) = (double 2) : ((double 1) : ((double 4) [])) = 4 : (2 : (8 : [])) = 4 : (2 : [8]) = 4 : [2, 8] = [4 : 2 : 8] Möchten wir jetzt auf die Listenelemente eine andere Funktion anwenden, so braucht man diese nur zu definieren und als erstes Argument von map anzugeben. Die Funktion map wirkt also wie eine Art von benutzerdefinierter Kontrollstruktur. 12 Allgemeine Definition von map: map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs Mit map kann eine Funktion vom Typ a -> zu einer Funktion vom Typ [a] -> [b] erweitert werden. Die Funktion map ist parametrisch polymorph, d.h. sie wirkt auf gleiche Weise auf ihre Argumentliste, völlig unabhängig davon, welchen Typ für a oder b eingesetzt wird. Ausgehend von einem Startwert werden die Elemente von rechts nach links mit einem zweistelligen Operator behandelt. Beide Funktionen ersetzen die leere Liste durch den Startwert und den Listenkonstruktor durch eine zweistellige Funktion. Bei der zu definierenden Funktion höherer Ordnung müssen daher ein Startwert e sowie eine Funktion f, die anstelle des Listenkonstruktors angewendet wird, als Parameter auftreten. Die Funktion foldr geht über map hinaus, indem sie die Ergebnisse von f faltet. foldr :: (Int -> Int -> Int) -> Int -> [Int] -> Int foldr f a [] = a foldr f a (b : x) = f b (foldr f a x) Allgemeiner: foldr :: (a -> b -> b) -> b -> [a] -> b foldr f e [] = e foldr f e (x.xs) = x `f` foldr f e xs Die Funktion foldr klammert von rechts nach links. Alternativ dazu gibt es die Funktion foldl, die in umgedrehter Richtung klammert. foldl :: (a -> b -> a) -> a -> [b] -> a foldl f e [] = e foldl f e (x : xs) = foldl f (e `f` x) xs Wie kann mittels foldr die Summe oder das Produkt der Zahlen einer Liste programmiert werden? sumList :: [Int] -> Int sumList = foldr (+) 0 list prodList :: [Int] -> Int prodList list = foldr (*) 1 list 13 Schreiben Sie eine Funktion filter, die aus einer Liste von Zahlen solche mit einer bestimmten Eigenschaft herausfiltert. filter :: (Int -> Bool) -> [Int] -> [Int] filter p [] = [] filter p (a : x) | (p a) = a : (filter p x) | otherwise = filter p x Der Quicksort Algorithmus zum Sortieren einer Liste von Zahlen ist ein weiteres Beispiel für die Benutzung von Funktionen höherer Ordnung. quicksort :: [Int] -> [Int] smaller :: Int -> Int -> Bool smaller a b = (b < a) greq :: Int -> Int -> Bool greq a b = (b >= a) quicksort [] = [] quicksort (a : x) = quicksort (filter (smaller a) x) ++ [a] ++ quicksort (filter (greq a) x) --oder: quicksort [] = [] quicksort (x:l) = quicksort (filter ( <x ) l ) ++ [x] ++quicksort(filter(>=x)l) Komposition von Funktionen. Funktionen lassen sich komponieren: compose :: (Int -> Int) -> (Int -> Int) -> (Int -> Int) compose f g x = (f (g x)) Für die Funktionskomposition gibt es in Haskell ein Symbol: (f . g) x = (f (g x)) Die Funktion length mit Typvariablen. length :: [t] -> Int length [] = 0 length (a : x) = 1 + (length x) Hier ist t eine Typvariable, die mit einem beliebigen Typ instanziiert werden kann. Im Unterschied zur Typbezeichnung beginnt der Name der Typvariablen immer mit einem kleinen Buchstaben. Der Typ von length heißt polymorpher Typ, weil er verschiedene Gestalten annehmen kann. Wird length in einem konkreten Kontext benutzt, so wird die Typvariable t entsprechend instanziiert. 14 Auch andere nützliche Listenfunktionen hängen nicht vom Typ der Elemente ab und sollten sofort polymorph definiert werden. append :: [t] -> [t] -> [t] append [] l = l append (a : x) l = a : (append x l) zip :: [t] -> [u] -> [(t, u)] zip [] [] = [] zip (a : x) [] = [] zip [] (b : y) = [] zip (a : x) (b : y) = (a, b) : (zip x y) map :: (t -> t) -> [t] -> [t] map f [] = [] map f (a : x) = (f a) : (map f x) Schreiben Sie eine Funktion height, die zu dem folgenden Baum: data Tree t = Nil | Node t (Tree t) (Tree t) die Höhe (d.h. maximale Länge von Pfaden) berechnet. height :: Tree t -> Int height Nil =1 height (Node n t1 t2) = 1 + (max (height t1) (height t2)) Die Funktion height berechnet die Höhe (d.h. maximale Länge von Pfaden) eines vorgelegten Baums. Welchen Typ die Knotenwerte haben spielt keine Rolle. Schreiben Sie eine Funktion collapse, die den folgenden Baum: data Tree t = Nil | Node t (Tree t) (Tree t) „inorder“ durchläuft und die Knotenbeschriftungen ausgibt. collapse :: Tree t -> [t] collapse Nil = [] collapse (Node n t1 t2) = (collapse t1) ++ [n] ++ (collapse t2) Die Funktion collapse durchläuft den vorgelegten Baum „inorder“ und gibt die Knotenbeschriftungen aus. 15 Listenfunktionen: ins :: Ord a => a -> [a] -> [a] x `ins` [] = [x] x `ins` (y:ys) | x <= y = x:y:ys | x > y = y:(x `ins` ys) merge :: Ord a => [a] -> [a] -> [a] xs `merge` [] = xs [] `merge` ys = ys (x:xs) `merge` (y:ys) | x<=y = x : (xs `merge` (y:ys)) | x> y = y : ((x:xs) `merge` ys) asList :: a -> [a] asList x = [x] liste :: Ord a => Tree a -> [a] liste = foldTree insmerge asList where insmerge x ys zs = (x `ins` ys) `merge` zs listeUnSort :: Tree a -> [a] listeUnSort = foldTree consapp asList where consapp x ys zs = (x:ys) ++ zs 16 Baumdeklarationen: data Tree t = Leaf t | Branch t (Tree t) (Tree t) --oder data Tree a = Leaf a | Node a (Tree a) (Tree a) Die Funktionen foldTree und mapTree: Die Funktion mapTree wendet eine Funktion auf alle Knotenbeschriftungen eines Bames an. Die Funktion foldTree nimmt eine Funktion f als Parameter und ersetzt jedes Vorkommen des Datenkonstruktors Node durch f, nachdem foldTree f über die Liste der direkten Teilbäume gemappt worden ist. foldTree :: (a -> b -> b -> b) -> (a -> b) -> Tree a -> b foldTree _ g (Leaf x) =gx foldTree f g (Branch x l r) = f x (foldTree f g l) (foldTree f g r) mapTree :: (a->b) -> Tree a -> Tree b mapTree f = foldTree (\x l r -> Branch (f x) l r) (\x -> Leaf (f x)) --oder data Tree element = Node element [Tree element] mapTree :: (a -> b) -> Tree a -> Tree b mapTree f (Node x subtrees) = Node (f x) (map (mapTree f) subtrees) foldTree :: (a -> [b] -> b) -> Tree -> a -> b foldTree f (Node x subtrees) = f x (map (foldTree f) subtrees) 17 Baumfunktionen: -- Darstellung eines Baumes: showTree :: Show a => Tree a -> String showTree = foldTree showB showL where showL = ("Leaf " ++) . show showB x l r = "Branch " ++ show x ++ " (" ++ l ++ ") (" ++ r ++ ")" instance Show a => Show (Tree a) where show = showTree --Verdopplung aller Baumeinträge: doubleTree :: Tree Int -> Tree Int doubleTree (Leaf x) = Leaf (2*x) doubleTree (Node x l r) = Node (2*x) (doubleTree l) (doubleTree r) --Summation aller Baumeinträge: sumTree :: Tree Int -> Int sumTree (Leaf x) = x sumTree (Node x l r) = x + (sumTree l) + (sumTree r) --Bestimmung der minimalen Beschriftung: minTree :: Tree Int -> Int minTree (Leaf x) = x minTree (Node x l r) = min x (min(minTree l) (minTree r )) --Bestimmung der Baumtiefe: depthTree :: Tree Int -> Int depthTree (Leaf x ) = l depthTree (Node x l r) = 1 + max(depthTree l) (depthTree r) 18