RosettaCodeData/Task/Tree-traversal/JavaScript/tree-traversal-6.js

168 lines
4.2 KiB
JavaScript

(() => {
'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();
})();