Comma quibbling: Difference between revisions

Content deleted Content added
Chunes (talk | contribs)
Add Plain English
Hout (talk | contribs)
m →‎JS ES6: Tidied functional composition, updated primitives.
Line 1,813: Line 1,813:
'use strict';
'use strict';


// COMMA QUIBBLING -------------------------------------------------------
// ----------------- COMMA QUIBBLING -----------------


// quibble :: [String] -> String
// quibble :: [String] -> String
const quibble = xs =>
const quibble = xs =>
(xs.length > 1) ? (
1 < xs.length ? (
intercalate(
intercalate(' and ')(
" and ",
ap([
ap(
compose(
[compose([intercalate(", "), reverse, tail]), head], //
intercalate(', '),
[reverse(xs)]
reverse,
)
tail
),
head
])([reverse(xs)])
)
)
) : concat(xs);
) : concat(xs);




// GENERIC FUNCTIONS -----------------------------------------------------
// ---------------------- 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"
])
)
));


// A list of functions applied to a list of arguments
// <*> :: [(a -> b)] -> [a] -> [b]
const ap = (fs, xs) => //
[].concat.apply([], fs.map(f => //
[].concat.apply([], xs.map(x => [f(x)]))));


// ---------------- GENERIC FUNCTIONS ----------------
// curry :: Function -> Function
const curry = (f, ...args) => {
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
function () {
return go(xs.concat([].slice.apply(arguments)));
};
return go([].slice.call(args, 1));
};


// intercalate :: String -> [a] -> String
// ap (<*>) :: [(a -> b)] -> [a] -> [b]
const intercalate = curry((s, xs) => xs.join(s));
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));


// concat :: [[a]] -> [a] | [String] -> String
const concat = xs => {
if (xs.length > 0) {
const unit = typeof xs[0] === 'string' ? '' : [];
return unit.concat.apply(unit, xs);
} else return [];
};


// compose :: [(a -> a)] -> (a -> a)
// append (++) :: [a] -> [a] -> [a]
const compose = fs => x => fs.reduceRight((a, f) => f(a), x);
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]
// map :: (a -> b) -> [a] -> [b]
const map = curry((f, xs) => xs.map(f));
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]
// reverse :: [a] -> [a]
const reverse = xs =>
const reverse = xs =>
typeof xs === 'string' ? (
'string' !== typeof xs ? (
xs.split('')
xs.slice(0).reverse()
.reverse()
) : xs.split('').reverse().join('');
.join('')
) : xs.slice(0)
.reverse();


// head :: [a] -> a
const head = xs => xs.length ? xs[0] : undefined;


// tail :: [a] -> [a]
// tail :: [a] -> [a]
const tail = xs => xs.length ? xs.slice(1) : undefined;
const tail = xs =>
// A new list consisting of all
// items of xs except the first.
xs.slice(1);


// (++) :: [a] -> [a] -> [a]
const append = (xs, ys) => xs.concat(ys);

// words :: String -> [String]
const words = s => s.split(/\s+/);


// unlines :: [String] -> String
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
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]
// TEST ------------------------------------------------------------------
return unlines(
const words = s =>
// List of space-delimited sub-strings.
map(
compose([x => '{' + x + '}', quibble]),
s.split(/\s+/);

append([
[],
// MAIN ---
return main();
["ABC"],
["ABC", "DEF"],
["ABC", "DEF", "G", "H"]
], map(
words, [
"One two three four", "Me myself I", "Jack Jill", "Loner"
]
))
));
})();</lang>
})();</lang>
{{Out}}
{{Out}}