168 lines
4.2 KiB
JavaScript
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();
|
|
})();
|