Comma quibbling: Difference between revisions
Content deleted Content added
Add Plain English |
m →JS ES6: Tidied functional composition, updated primitives. |
||
Line 1,813: | Line 1,813: | ||
'use strict'; |
'use strict'; |
||
// |
// ----------------- COMMA QUIBBLING ----------------- |
||
// quibble :: [String] -> String |
// quibble :: [String] -> String |
||
const quibble = xs => |
const quibble = xs => |
||
1 < xs.length ? ( |
|||
intercalate( |
intercalate(' and ')( |
||
ap([ |
|||
compose( |
|||
intercalate(', '), |
|||
reverse, |
|||
tail |
|||
), |
|||
head |
|||
])([reverse(xs)]) |
|||
) |
) |
||
) : concat(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" |
|||
]) |
|||
) |
|||
)); |
|||
// 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)); |
|||
}; |
|||
// |
// ap (<*>) :: [(a -> b)] -> [a] -> [b] |
||
const |
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 []; |
|||
}; |
|||
// |
// append (++) :: [a] -> [a] -> [a] |
||
const |
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 = |
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 => |
||
'string' !== typeof xs ? ( |
|||
xs. |
xs.slice(0).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 => |
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 => |
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 ------------------------------------------------------------------ |
|||
const words = s => |
|||
// List of space-delimited sub-strings. |
|||
map( |
|||
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}} |