177 lines
4.9 KiB
JavaScript
177 lines
4.9 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// main :: IO ()
|
|
const main = () => {
|
|
const
|
|
month = fst,
|
|
day = snd;
|
|
showLog(
|
|
map(x => Array.from(x), (
|
|
|
|
// The month with only one remaining day,
|
|
|
|
// (A's month contains only one remaining day)
|
|
// (3 :: A "Then I also know")
|
|
uniquePairing(month)(
|
|
|
|
// among the days with unique months,
|
|
|
|
// (B's day is paired with only one remaining month)
|
|
// (2 :: B "I know now")
|
|
uniquePairing(day)(
|
|
|
|
// excluding months with unique days,
|
|
|
|
// (A's month is not among those with unique days)
|
|
// (1 :: A "I know that Bernard does not know")
|
|
monthsWithUniqueDays(false)(
|
|
|
|
// from the given month-day pairs:
|
|
|
|
// (0 :: Cheryl's list)
|
|
map(x => tupleFromList(words(strip(x))),
|
|
splitOn(/,\s+/,
|
|
`May 15, May 16, May 19,
|
|
June 17, June 18, July 14, July 16,
|
|
Aug 14, Aug 15, Aug 17`
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
))
|
|
);
|
|
};
|
|
|
|
// monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)]
|
|
const monthsWithUniqueDays = blnInclude => xs => {
|
|
const months = map(fst, uniquePairing(snd)(xs));
|
|
return filter(
|
|
md => (blnInclude ? id : not)(
|
|
elem(fst(md), months)
|
|
),
|
|
xs
|
|
);
|
|
};
|
|
|
|
// uniquePairing :: ((a, a) -> a) ->
|
|
// -> [(Month, Day)] -> [(Month, Day)]
|
|
const uniquePairing = f => xs =>
|
|
bindPairs(xs,
|
|
md => {
|
|
const
|
|
dct = f(md),
|
|
matches = filter(
|
|
k => 1 === length(dct[k]),
|
|
Object.keys(dct)
|
|
);
|
|
return filter(tpl => elem(f(tpl), matches), xs);
|
|
}
|
|
);
|
|
|
|
// bindPairs :: [(Month, Day)] -> (Dict, Dict) -> [(Month, Day)]
|
|
const bindPairs = (xs, f) => f(
|
|
Tuple(
|
|
dictFromPairs(fst)(snd)(xs),
|
|
dictFromPairs(snd)(fst)(xs)
|
|
)
|
|
);
|
|
|
|
// dictFromPairs :: ((a, a) -> a) -> ((a, a) -> a) -> [(a, a)] -> Dict
|
|
const dictFromPairs = f => g => xs =>
|
|
foldl((a, tpl) => Object.assign(
|
|
a, {
|
|
[f(tpl)]: (a[f(tpl)] || []).concat(g(tpl).toString())
|
|
}
|
|
), {}, xs);
|
|
|
|
|
|
// GENERIC ABSTRACTIONS -------------------------------
|
|
|
|
// Tuple (,) :: a -> b -> (a, b)
|
|
const Tuple = (a, b) => ({
|
|
type: 'Tuple',
|
|
'0': a,
|
|
'1': b,
|
|
length: 2
|
|
});
|
|
|
|
// elem :: Eq a => a -> [a] -> Bool
|
|
const elem = (x, xs) => xs.includes(x);
|
|
|
|
// filter :: (a -> Bool) -> [a] -> [a]
|
|
const filter = (f, xs) => xs.filter(f);
|
|
|
|
// foldl :: (a -> b -> a) -> a -> [b] -> a
|
|
const foldl = (f, a, xs) => xs.reduce(f, a);
|
|
|
|
// fst :: (a, b) -> a
|
|
const fst = tpl => tpl[0];
|
|
|
|
// id :: a -> a
|
|
const id = x => x;
|
|
|
|
// intersect :: (Eq a) => [a] -> [a] -> [a]
|
|
const intersect = (xs, ys) =>
|
|
xs.filter(x => -1 !== ys.indexOf(x));
|
|
|
|
// 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
|
|
|
|
// length :: [a] -> Int
|
|
const length = xs =>
|
|
(Array.isArray(xs) || 'string' === typeof xs) ? (
|
|
xs.length
|
|
) : Infinity;
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = (f, xs) => xs.map(f);
|
|
|
|
// not :: Bool -> Bool
|
|
const not = b => !b;
|
|
|
|
// showLog :: a -> IO ()
|
|
const showLog = (...args) =>
|
|
console.log(
|
|
args
|
|
.map(JSON.stringify)
|
|
.join(' -> ')
|
|
);
|
|
|
|
// snd :: (a, b) -> b
|
|
const snd = tpl => tpl[1];
|
|
|
|
// splitOn :: String -> String -> [String]
|
|
const splitOn = (pat, src) =>
|
|
src.split(pat);
|
|
|
|
// strip :: String -> String
|
|
const strip = s => s.trim();
|
|
|
|
// tupleFromList :: [a] -> (a, a ...)
|
|
const tupleFromList = xs =>
|
|
TupleN.apply(null, xs);
|
|
|
|
// TupleN :: a -> b ... -> (a, b ... )
|
|
function TupleN() {
|
|
const
|
|
args = Array.from(arguments),
|
|
lng = args.length;
|
|
return lng > 1 ? Object.assign(
|
|
args.reduce((a, x, i) => Object.assign(a, {
|
|
[i]: x
|
|
}), {
|
|
type: 'Tuple' + (2 < lng ? lng.toString() : ''),
|
|
length: lng
|
|
})
|
|
) : args[0];
|
|
};
|
|
|
|
// words :: String -> [String]
|
|
const words = s => s.split(/\s+/);
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|