142 lines
3.5 KiB
JavaScript
142 lines
3.5 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// ----------------- COMMA QUIBBLING -----------------
|
|
|
|
// quibble :: [String] -> String
|
|
const quibble = xs =>
|
|
1 < xs.length ? (
|
|
intercalate(' and ')(
|
|
ap([
|
|
compose(
|
|
intercalate(', '),
|
|
reverse,
|
|
tail
|
|
),
|
|
head
|
|
])([reverse(xs)])
|
|
)
|
|
) : concat(xs);
|
|
|
|
|
|
// ---------------------- TEST -----------------------
|
|
const main = () =>
|
|
unlines(
|
|
map(compose(x => '{' + x + '}', quibble))(
|
|
append([
|
|
[],
|
|
["ABC"],
|
|
["ABC", "DEF"],
|
|
["ABC", "DEF", "G", "H"]
|
|
])(
|
|
map(words)([
|
|
"One two three four",
|
|
"Me myself I",
|
|
"Jack Jill",
|
|
"Loner"
|
|
])
|
|
)
|
|
));
|
|
|
|
|
|
// ---------------- GENERIC FUNCTIONS ----------------
|
|
|
|
// ap (<*>) :: [(a -> b)] -> [a] -> [b]
|
|
const ap = fs =>
|
|
// The sequential application of each of a list
|
|
// of functions to each of a list of values.
|
|
// apList([x => 2 * x, x => 20 + x])([1, 2, 3])
|
|
// -> [2, 4, 6, 21, 22, 23]
|
|
xs => fs.flatMap(f => xs.map(f));
|
|
|
|
|
|
// append (++) :: [a] -> [a] -> [a]
|
|
const append = xs =>
|
|
// A list defined by the
|
|
// concatenation of two others.
|
|
ys => xs.concat(ys);
|
|
|
|
|
|
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
|
const compose = (...fs) =>
|
|
// A function defined by the right-to-left
|
|
// composition of all the functions in fs.
|
|
fs.reduce(
|
|
(f, g) => x => f(g(x)),
|
|
x => x
|
|
);
|
|
|
|
|
|
// concat :: [[a]] -> [a]
|
|
// concat :: [String] -> String
|
|
const concat = xs => (
|
|
ys => 0 < ys.length ? (
|
|
ys.every(Array.isArray) ? (
|
|
[]
|
|
) : ''
|
|
).concat(...ys) : ys
|
|
)(xs);
|
|
|
|
|
|
// head :: [a] -> a
|
|
const head = xs => (
|
|
ys => ys.length ? (
|
|
ys[0]
|
|
) : undefined
|
|
)(list(xs));
|
|
|
|
|
|
// intercalate :: String -> [String] -> String
|
|
const intercalate = s =>
|
|
// The concatenation of xs
|
|
// interspersed with copies of s.
|
|
xs => xs.join(s);
|
|
|
|
|
|
// list :: StringOrArrayLike b => b -> [a]
|
|
const list = xs =>
|
|
// xs itself, if it is an Array,
|
|
// or an Array derived from xs.
|
|
Array.isArray(xs) ? (
|
|
xs
|
|
) : Array.from(xs || []);
|
|
|
|
|
|
// 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 => [...xs].map(f);
|
|
|
|
|
|
// reverse :: [a] -> [a]
|
|
const reverse = xs =>
|
|
'string' !== typeof xs ? (
|
|
xs.slice(0).reverse()
|
|
) : xs.split('').reverse().join('');
|
|
|
|
|
|
// tail :: [a] -> [a]
|
|
const tail = xs =>
|
|
// A new list consisting of all
|
|
// items of xs except the first.
|
|
xs.slice(1);
|
|
|
|
|
|
// unlines :: [String] -> String
|
|
const unlines = xs =>
|
|
// A single string formed by the intercalation
|
|
// of a list of strings with the newline character.
|
|
xs.join('\n');
|
|
|
|
|
|
// words :: String -> [String]
|
|
const words = s =>
|
|
// List of space-delimited sub-strings.
|
|
s.split(/\s+/);
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|