(() => { 'use strict'; // foldTree :: (a -> [b] -> b) -> Tree a -> b const foldTree = f => // The catamorphism on trees. A summary // value obtained by a depth-first fold. tree => { const go = x => f(x.root)( x.nest.map(go) ); return go(tree); }; // preorder :: a -> [[a]] -> [a] const preorder = x => xs => [x, ...concat(xs)]; // inorder :: a -> [[a]] -> [a] const inorder = x => xs => length(xs) ? ( [...xs[0], x, ...concat(xs.slice(1))] ) : [x]; // postorder :: a -> [[a]] -> [a] const postorder = x => xs => [...concat(xs), x]; // levelOrder :: Tree a -> [a] const levelOrder = tree => concatMap(map(root))( takeWhile(length)( iterate(concatMap(nest))( [tree] ) ) ); // ------------------------TEST------------------------ const main = () => { const tree = Node(1)([ Node(2)([ Node(4)([ Node(7)([]) ]), Node(5)([]) ]), Node(3)([ Node(6)([ Node(8)([]), Node(9)([]) ]) ]) ]); // Generated by code in Rosetta Code // task: 'Visualize a tree' console.log([ ' ┌ 4 ─ 7', ' ┌ 2 ┤', ' │ └ 5', ' 1 ┤', ' │ ┌ 8', ' └ 3 ─ 6 ┤', ' └ 9' ].join('\n')); [preorder, inorder, postorder] .forEach( f => console.log( justifyRight(11)(' ')(f.name + ':'), foldTree(f)( tree ) ) ); console.log( 'levelOrder:', levelOrder(tree) ) }; // -----------------GENERIC FUNCTIONS------------------ // Node :: a -> [Tree a] -> Tree a const Node = v => // Constructor for a Tree node which connects a // value of some kind to a list of zero or // more child trees. xs => ({ type: 'Node', root: v, nest: xs || [] }); // concat :: [[a]] -> [a] // concat :: [String] -> String const concat = xs => 0 < xs.length ? ( xs.every(x => 'string' === typeof x) ? ( '' ) : [] ).concat(...xs) : xs; // concatMap :: (a -> [b]) -> [a] -> [b] const concatMap = f => xs => xs.flatMap(f); // iterate :: (a -> a) -> a -> Gen [a] const iterate = f => function*(x) { let v = x; while (true) { yield(v); v = f(v); } }; // justifyRight :: Int -> Char -> String -> String const justifyRight = n => // The string s, preceded by enough padding (with // the character c) to reach the string length n. c => s => n > s.length ? ( s.padStart(n, c) ) : s; // length :: [a] -> Int const length = xs => // Returns Infinity over objects without finite // length. This enables zip and zipWith to choose // the shorter argument when one is non-finite, // like cycle, repeat etc (Array.isArray(xs) || 'string' === typeof xs) ? ( xs.length ) : Infinity; // map :: (a -> b) -> [a] -> [b] const map = f => // The list obtained by applying f to each element of xs. // (The image of xs under f). xs => (Array.isArray(xs) ? ( xs ) : xs.split('')).map(f); // nest :: Tree a -> [a] const nest = tree => tree.nest; // root :: Tree a -> a const root = tree => tree.root; // takeWhile :: (a -> Bool) -> Gen [a] -> [a] const takeWhile = p => xs => { const ys = []; let nxt = xs.next(), v = nxt.value; while (!nxt.done && p(v)) { ys.push(v); nxt = xs.next(); v = nxt.value } return ys; }; // MAIN --- return main(); })();