Cheryl's birthday: Difference between revisions
(=={{header|JavaScript}}== Added a first JS draft, aiming for some degree of legibility) |
Alextretyak (talk | contribs) m (→{{header|11l}}: named tuples) |
||
(125 intermediate revisions by 39 users not shown) | |||
Line 1: | Line 1: | ||
{{ |
{{task}} |
||
Albert and Bernard just became friends with Cheryl, and they want to know when her birthday is. |
Albert and Bernard just became friends with Cheryl, and they want to know when her birthday is. |
||
Cheryl gave them a list of |
Cheryl gave them a list of ten possible dates: |
||
May 15, May 16, May 19 |
|||
June 17, June 18 |
|||
July 14, July 16 |
|||
August 14, August 15, August 17 |
|||
Cheryl then tells Albert the ''month'' of birth, and Bernard the ''day'' (of the month) of birth. |
|||
May 15, May 16, May 19 |
|||
1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. |
|||
June 17, June 18 |
|||
2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. |
|||
July 14, July 16 |
|||
3) Albert: Then I also know when Cheryl's birthday is. |
|||
August 14, August 15, August 17 |
|||
Cheryl then tells Albert and Bernard separately the month and the day of the birthday respectively. |
|||
;Task |
|||
1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. |
|||
Write a computer program to deduce, by successive elimination, Cheryl's birthday. |
|||
2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. |
|||
;Related task: |
|||
3) Albert: Then I also know when Cheryl's birthday is. |
|||
* [[Sum and Product Puzzle]] |
|||
;References |
|||
;Task |
|||
* [https://en.wikipedia.org/wiki/Cheryl%27s_Birthday Wikipedia article] of the same name. |
|||
* [https://en.wikipedia.org/wiki/Tuple_relational_calculus, Tuple Relational Calculus] |
|||
<br><br> |
|||
=={{header|11l}}== |
|||
{{trans|Nim}} |
|||
<syntaxhighlight lang="11l">T Date = (String month, Int day) |
|||
Write a program in your language to deduce, by successive elimination, Cheryl's birthday. |
|||
V dates = [Date(‘May’, 15), Date(‘May’, 16), Date(‘May’, 19), Date(‘June’, 17), Date(‘June’, 18), |
|||
See the [https://en.wikipedia.org/wiki/Cheryl%27s_Birthday Wikipedia article] of the same name. |
|||
Date(‘July’, 14), Date(‘July’, 16), Date(‘August’, 14), Date(‘August’, 15), Date(‘August’, 17)] |
|||
DefaultDict[Int, Set[String]] monthTable |
|||
;Related task: |
|||
L(date) dates |
|||
* [[Sum and Product Puzzle]] |
|||
monthTable[date.day].add(date.month) |
|||
<br><br> |
|||
DefaultDict[String, Set[Int]] dayTable |
|||
L(date) dates |
|||
dayTable[date.month].add(date.day) |
|||
Set[String] possibleMonths |
|||
Set[Int] possibleDays |
|||
L(month, days) dayTable |
|||
I days.len > 1 |
|||
possibleMonths.add(month) |
|||
L(month, days) dayTable |
|||
L(day) days |
|||
I monthTable[day].len == 1 |
|||
possibleMonths.remove(month) |
|||
print(‘After first Albert's sentence, possible months are ’Array(possibleMonths).join(‘, ’)‘.’) |
|||
L(day, months) monthTable |
|||
I months.len > 1 |
|||
possibleDays.add(day) |
|||
Set[Int] impossibleDays |
|||
L(day) possibleDays |
|||
I monthTable[day].intersection(possibleMonths).len > 1 |
|||
impossibleDays.add(day) |
|||
L(day) impossibleDays |
|||
possibleDays.remove(day) |
|||
print(‘After Bernard's sentence, possible days are ’Array(possibleDays).join(‘, ’)‘.’) |
|||
Set[String] impossibleMonths |
|||
L(month) possibleMonths |
|||
I dayTable[month].intersection(possibleDays).len > 1 |
|||
impossibleMonths.add(month) |
|||
L(month) impossibleMonths |
|||
possibleMonths.remove(month) |
|||
assert(possibleMonths.len == 1) |
|||
V month = possibleMonths.pop() |
|||
print(‘After second Albert's sentence, remaining month is ’month‘...’) |
|||
possibleDays = possibleDays.intersection(dayTable[month]) |
|||
assert(possibleDays.len == 1) |
|||
V day = possibleDays.pop() |
|||
print(‘and thus remaining day is ’day‘.’) |
|||
print() |
|||
print(‘So birthday date is ’month‘ ’day‘.’)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
After first Albert's sentence, possible months are August, July. |
|||
After Bernard's sentence, possible days are 15, 16, 17. |
|||
After second Albert's sentence, remaining month is July... |
|||
and thus remaining day is 16. |
|||
So birthday date is July 16. |
|||
</pre> |
|||
=={{Header|Ada}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO; |
|||
procedure Main is |
|||
type Months is |
|||
(January, February, March, April, May, June, July, August, September, |
|||
November, December); |
|||
type day_num is range 1 .. 31; |
|||
type birthdate is record |
|||
Month : Months; |
|||
Day : day_num; |
|||
Active : Boolean; |
|||
end record; |
|||
type birthday_list is array (Positive range <>) of birthdate; |
|||
Possible_birthdates : birthday_list := |
|||
((May, 15, True), (May, 16, True), (May, 19, True), (June, 17, True), |
|||
(June, 18, True), (July, 14, True), (July, 16, True), (August, 14, True), |
|||
(August, 15, True), (August, 17, True)); |
|||
procedure print_answer is |
|||
begin |
|||
for the_day of Possible_birthdates loop |
|||
if the_day.Active then |
|||
Put_Line (the_day.Month'Image & "," & the_day.Day'Image); |
|||
end if; |
|||
end loop; |
|||
end print_answer; |
|||
procedure print_remaining is |
|||
count : Natural := 0; |
|||
begin |
|||
for date of Possible_birthdates loop |
|||
if date.Active then |
|||
count := count + 1; |
|||
end if; |
|||
end loop; |
|||
Put_Line (count'Image & " remaining."); |
|||
end print_remaining; |
|||
-- the month cannot have a unique day |
|||
procedure first_pass is |
|||
count : Natural; |
|||
begin |
|||
for first_day of Possible_birthdates loop |
|||
count := 0; |
|||
for next_day of Possible_birthdates loop |
|||
if first_day.Day = next_day.Day then |
|||
count := count + 1; |
|||
end if; |
|||
end loop; |
|||
if count = 1 then |
|||
for the_day of Possible_birthdates loop |
|||
if the_day.Active and then first_day.Month = the_day.Month then |
|||
the_day.Active := False; |
|||
end if; |
|||
end loop; |
|||
end if; |
|||
end loop; |
|||
end first_pass; |
|||
-- the day must now be unique |
|||
procedure second_pass is |
|||
count : Natural; |
|||
begin |
|||
for first_day of Possible_birthdates loop |
|||
if first_day.Active then |
|||
count := 0; |
|||
for next_day of Possible_birthdates loop |
|||
if next_day.Active then |
|||
if next_day.Day = first_day.Day then |
|||
count := count + 1; |
|||
end if; |
|||
end if; |
|||
end loop; |
|||
if count > 1 then |
|||
for next_day of Possible_birthdates loop |
|||
if next_day.Active and then next_day.Day = first_day.Day then |
|||
next_day.Active := False; |
|||
end if; |
|||
end loop; |
|||
end if; |
|||
end if; |
|||
end loop; |
|||
end second_pass; |
|||
-- the month must now be unique |
|||
procedure third_pass is |
|||
count : Natural; |
|||
begin |
|||
for first_day of Possible_birthdates loop |
|||
if first_day.Active then |
|||
count := 0; |
|||
for next_day of Possible_birthdates loop |
|||
if next_day.Active and then next_day.Month = first_day.Month |
|||
then |
|||
count := count + 1; |
|||
end if; |
|||
end loop; |
|||
if count > 1 then |
|||
for next_day of Possible_birthdates loop |
|||
if next_day.Active and then next_day.Month = first_day.Month |
|||
then |
|||
next_day.Active := False; |
|||
end if; |
|||
end loop; |
|||
end if; |
|||
end if; |
|||
end loop; |
|||
end third_pass; |
|||
begin |
|||
print_remaining; |
|||
first_pass; |
|||
print_remaining; |
|||
second_pass; |
|||
print_remaining; |
|||
third_pass; |
|||
print_answer; |
|||
end Main;</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
10 remaining. |
|||
5 remaining. |
|||
3 remaining. |
|||
JULY, 16 |
|||
</pre> |
|||
=={{header|ALGOL 68}}== |
|||
{{works with|ALGOL 68G|Any - tested with release 2.8.3.win32}} |
|||
<syntaxhighlight lang="algol68"> |
|||
BEGIN # Cheryl's birthday puzzle # |
|||
[ 1 : 4, 1 : 6 ]INT dates # non-zero indicates a possible date # |
|||
:= ( ( 0, 15, 16, 0, 0, 19 ) # may # |
|||
, ( 0, 0, 0, 17, 18, 0 ) # june # |
|||
, ( 14, 0, 16, 0, 0, 0 ) # july # |
|||
, ( 14, 15, 0, 17, 0, 0 ) # august # |
|||
); |
|||
[]STRING month name = ( "May", "June", "July", "August" ); |
|||
print( ( "Cheryl tells Albert the month and Bernard the day", newline ) ); |
|||
print( ( "Albert doesn't know the date and knows Bernard doesn't either", newline ) ); |
|||
FOR d TO 2 UPB dates DO # elimiate the months with unique days # |
|||
INT day count := 0; |
|||
INT day := 0; |
|||
INT month := 0; |
|||
FOR m TO 1 UPB dates DO |
|||
IF dates[ m, d ] /= 0 THEN |
|||
day count +:= 1; |
|||
day := dates[ m, d ]; |
|||
month := m |
|||
FI |
|||
OD; |
|||
IF day count = 1 THEN |
|||
print( ( " Eliminating ", month name[ month ], ", ", whole( day, 0 ), "th is unique", newline ) ); |
|||
FOR p TO 2 UPB dates DO dates[ month, p ] := 0 OD |
|||
FI |
|||
OD; |
|||
print( ( "Bernard now knows the date", newline ) ); |
|||
FOR d TO 2 UPB dates DO # eliminate the days that aren't unique # |
|||
INT day count := 0; |
|||
INT day := 0; |
|||
INT month := 0; |
|||
FOR m TO 1 UPB dates DO |
|||
IF dates[ m, d ] /= 0 THEN |
|||
day count +:= 1; |
|||
day := dates[ m, d ]; |
|||
month := m |
|||
FI |
|||
OD; |
|||
IF day count > 1 THEN |
|||
print( ( " Eliminating ", whole( day, 0 ), "th, it is non-unique", newline ) ); |
|||
FOR p TO 1 UPB dates DO dates[ p, d ] := 0 OD |
|||
FI |
|||
OD; |
|||
print( ( "Albert now knows the date", newline ) ); |
|||
FOR m TO 1 UPB dates DO # eliminate months with non-unique days # |
|||
INT day count := 0; |
|||
INT day := 0; |
|||
INT month := 0; |
|||
FOR d TO 2 UPB dates DO |
|||
IF dates[ m, d ] /= 0 THEN |
|||
day count +:= 1; |
|||
day := dates[ m, d ]; |
|||
month := m |
|||
FI |
|||
OD; |
|||
IF day count > 1 THEN |
|||
print( ( " Eliminating ", month name[ m ], ", it has multiple days", newline ) ); |
|||
FOR p TO 2 UPB dates DO dates[ m, p ] := 0 OD |
|||
FI |
|||
OD; |
|||
print( ( "Cheryl's birthday: " ) ); # show the solution(s) # |
|||
FOR m TO 1 UPB dates DO |
|||
FOR d TO 2 UPB dates DO |
|||
IF dates[ m, d ] /= 0 THEN |
|||
print( ( " ", month name[ m ], " ", whole( dates[ m, d ], 0 ), "th" ) ) |
|||
FI |
|||
OD |
|||
OD; |
|||
print( ( newline ) ) |
|||
END |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl tells Albert the month and Bernard the day |
|||
Albert doesn't know the date and knows Bernard doesn't either |
|||
Eliminating June, 18th is unique |
|||
Eliminating May, 19th is unique |
|||
Bernard now knows the date |
|||
Eliminating 14th, it is non-unique |
|||
Albert now knows the date |
|||
Eliminating August, it has multiple days |
|||
Cheryl's birthday: July 16th |
|||
</pre> |
|||
=={{header|AppleScript}}== |
|||
<syntaxhighlight lang="applescript">use AppleScript version "2.4" |
|||
use framework "Foundation" |
|||
use scripting additions |
|||
property M : 1 -- Month |
|||
property D : 2 -- Day |
|||
on run |
|||
-- The MONTH with only one remaining day |
|||
-- among the DAYs with unique months, |
|||
-- EXCLUDING months with unique days, |
|||
-- in Cheryl's list: |
|||
showList(uniquePairing(M, ¬ |
|||
uniquePairing(D, ¬ |
|||
monthsWithUniqueDays(false, ¬ |
|||
map(composeList({tupleFromList, |words|, toLower}), ¬ |
|||
splitOn(", ", ¬ |
|||
"May 15, May 16, May 19, June 17, June 18, " & ¬ |
|||
"July 14, July 16, Aug 14, Aug 15, Aug 17")))))) |
|||
--> "[('july', '16')]" |
|||
end run |
|||
-- QUERY FUNCTIONS ---------------------------------------- |
|||
-- monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)] |
|||
on monthsWithUniqueDays(blnInclude, xs) |
|||
set _months to map(my fst, uniquePairing(D, xs)) |
|||
script uniqueDay |
|||
on |λ|(md) |
|||
set bln to elem(fst(md), _months) |
|||
if blnInclude then |
|||
bln |
|||
else |
|||
not bln |
|||
end if |
|||
end |λ| |
|||
end script |
|||
filter(uniqueDay, xs) |
|||
end monthsWithUniqueDays |
|||
-- uniquePairing :: DatePart -> [(M, D)] -> [(M, D)] |
|||
on uniquePairing(dp, xs) |
|||
script go |
|||
property f : my mReturn(item dp of {my fst, my snd}) |
|||
on |λ|(md) |
|||
set dct to f's |λ|(md) |
|||
script unique |
|||
on |λ|(k) |
|||
set mb to lookupDict(k, dct) |
|||
if Nothing of mb then |
|||
false |
|||
else |
|||
1 = length of (Just of mb) |
|||
end if |
|||
end |λ| |
|||
end script |
|||
set uniques to filter(unique, keys(dct)) |
|||
script found |
|||
on |λ|(tpl) |
|||
elem(f's |λ|(tpl), uniques) |
|||
end |λ| |
|||
end script |
|||
filter(found, xs) |
|||
end |λ| |
|||
end script |
|||
bindPairs(xs, go) |
|||
end uniquePairing |
|||
-- bindPairs :: [(M, D)] -> ((Dict Text [Text], Dict Text [Text]) |
|||
-- -> [(M, D)]) -> [(M, D)] |
|||
on bindPairs(xs, f) |
|||
tell mReturn(f) |
|||
|λ|(Tuple(dictFromPairs(xs), ¬ |
|||
dictFromPairs(map(my swap, xs)))) |
|||
end tell |
|||
end bindPairs |
|||
-- dictFromPairs :: [(M, D)] -> Dict Text [Text] |
|||
on dictFromPairs(mds) |
|||
set gps to groupBy(|on|(my eq, my fst), ¬ |
|||
sortBy(comparing(my fst), mds)) |
|||
script kv |
|||
on |λ|(gp) |
|||
Tuple(fst(item 1 of gp), map(my snd, gp)) |
|||
end |λ| |
|||
end script |
|||
mapFromList(map(kv, gps)) |
|||
end dictFromPairs |
|||
-- LIBRARY GENERICS --------------------------------------- |
|||
-- comparing :: (a -> b) -> (a -> a -> Ordering) |
|||
on comparing(f) |
|||
script |
|||
on |λ|(a, b) |
|||
tell mReturn(f) |
|||
set fa to |λ|(a) |
|||
set fb to |λ|(b) |
|||
if fa < fb then |
|||
-1 |
|||
else if fa > fb then |
|||
1 |
|||
else |
|||
0 |
|||
end if |
|||
end tell |
|||
end |λ| |
|||
end script |
|||
end comparing |
|||
-- composeList :: [(a -> a)] -> (a -> a) |
|||
on composeList(fs) |
|||
script |
|||
on |λ|(x) |
|||
script |
|||
on |λ|(f, a) |
|||
mReturn(f)'s |λ|(a) |
|||
end |λ| |
|||
end script |
|||
foldr(result, x, fs) |
|||
end |λ| |
|||
end script |
|||
end composeList |
|||
-- drop :: Int -> [a] -> [a] |
|||
-- drop :: Int -> String -> String |
|||
on drop(n, xs) |
|||
set c to class of xs |
|||
if c is not script then |
|||
if c is not string then |
|||
if n < length of xs then |
|||
items (1 + n) thru -1 of xs |
|||
else |
|||
{} |
|||
end if |
|||
else |
|||
if n < length of xs then |
|||
text (1 + n) thru -1 of xs |
|||
else |
|||
"" |
|||
end if |
|||
end if |
|||
else |
|||
take(n, xs) -- consumed |
|||
return xs |
|||
end if |
|||
end drop |
|||
-- dropAround :: (a -> Bool) -> [a] -> [a] |
|||
-- dropAround :: (Char -> Bool) -> String -> String |
|||
on dropAround(p, xs) |
|||
dropWhile(p, dropWhileEnd(p, xs)) |
|||
end dropAround |
|||
-- dropWhile :: (a -> Bool) -> [a] -> [a] |
|||
-- dropWhile :: (Char -> Bool) -> String -> String |
|||
on dropWhile(p, xs) |
|||
set lng to length of xs |
|||
set i to 1 |
|||
tell mReturn(p) |
|||
repeat while i ≤ lng and |λ|(item i of xs) |
|||
set i to i + 1 |
|||
end repeat |
|||
end tell |
|||
drop(i - 1, xs) |
|||
end dropWhile |
|||
-- dropWhileEnd :: (a -> Bool) -> [a] -> [a] |
|||
-- dropWhileEnd :: (Char -> Bool) -> String -> String |
|||
on dropWhileEnd(p, xs) |
|||
set i to length of xs |
|||
tell mReturn(p) |
|||
repeat while i > 0 and |λ|(item i of xs) |
|||
set i to i - 1 |
|||
end repeat |
|||
end tell |
|||
take(i, xs) |
|||
end dropWhileEnd |
|||
-- elem :: Eq a => a -> [a] -> Bool |
|||
on elem(x, xs) |
|||
considering case |
|||
xs contains x |
|||
end considering |
|||
end elem |
|||
-- enumFromToInt :: Int -> Int -> [Int] |
|||
on enumFromToInt(M, n) |
|||
if M ≤ n then |
|||
set lst to {} |
|||
repeat with i from M to n |
|||
set end of lst to i |
|||
end repeat |
|||
return lst |
|||
else |
|||
return {} |
|||
end if |
|||
end enumFromToInt |
|||
-- eq (==) :: Eq a => a -> a -> Bool |
|||
on eq(a, b) |
|||
a = b |
|||
end eq |
|||
-- filter :: (a -> Bool) -> [a] -> [a] |
|||
on filter(f, xs) |
|||
tell mReturn(f) |
|||
set lst to {} |
|||
set lng to length of xs |
|||
repeat with i from 1 to lng |
|||
set v to item i of xs |
|||
if |λ|(v, i, xs) then set end of lst to v |
|||
end repeat |
|||
return lst |
|||
end tell |
|||
end filter |
|||
-- foldl :: (a -> b -> a) -> a -> [b] -> a |
|||
on foldl(f, startValue, xs) |
|||
tell mReturn(f) |
|||
set v to startValue |
|||
set lng to length of xs |
|||
repeat with i from 1 to lng |
|||
set v to |λ|(v, item i of xs, i, xs) |
|||
end repeat |
|||
return v |
|||
end tell |
|||
end foldl |
|||
-- foldr :: (a -> b -> b) -> b -> [a] -> b |
|||
on foldr(f, startValue, xs) |
|||
tell mReturn(f) |
|||
set v to startValue |
|||
set lng to length of xs |
|||
repeat with i from lng to 1 by -1 |
|||
set v to |λ|(item i of xs, v, i, xs) |
|||
end repeat |
|||
return v |
|||
end tell |
|||
end foldr |
|||
-- fst :: (a, b) -> a |
|||
on fst(tpl) |
|||
if class of tpl is record then |
|||
|1| of tpl |
|||
else |
|||
item 1 of tpl |
|||
end if |
|||
end fst |
|||
-- Typical usage: groupBy(on(eq, f), xs) |
|||
-- groupBy :: (a -> a -> Bool) -> [a] -> [[a]] |
|||
on groupBy(f, xs) |
|||
set mf to mReturn(f) |
|||
script enGroup |
|||
on |λ|(a, x) |
|||
if length of (active of a) > 0 then |
|||
set h to item 1 of active of a |
|||
else |
|||
set h to missing value |
|||
end if |
|||
if h is not missing value and mf's |λ|(h, x) then |
|||
{active:(active of a) & {x}, sofar:sofar of a} |
|||
else |
|||
{active:{x}, sofar:(sofar of a) & {active of a}} |
|||
end if |
|||
end |λ| |
|||
end script |
|||
if length of xs > 0 then |
|||
set dct to foldl(enGroup, {active:{item 1 of xs}, sofar:{}}, rest of xs) |
|||
if length of (active of dct) > 0 then |
|||
sofar of dct & {active of dct} |
|||
else |
|||
sofar of dct |
|||
end if |
|||
else |
|||
{} |
|||
end if |
|||
end groupBy |
|||
-- insertMap :: Dict -> String -> a -> Dict |
|||
on insertMap(rec, k, v) |
|||
tell (current application's NSMutableDictionary's ¬ |
|||
dictionaryWithDictionary:rec) |
|||
its setValue:v forKey:(k as string) |
|||
return it as record |
|||
end tell |
|||
end insertMap |
|||
-- intercalateS :: String -> [String] -> String |
|||
on intercalateS(sep, xs) |
|||
set {dlm, my text item delimiters} to {my text item delimiters, sep} |
|||
set s to xs as text |
|||
set my text item delimiters to dlm |
|||
return s |
|||
end intercalateS |
|||
-- Just :: a -> Maybe a |
|||
on Just(x) |
|||
{type:"Maybe", Nothing:false, Just:x} |
|||
end Just |
|||
-- keys :: Dict -> [String] |
|||
on keys(rec) |
|||
(current application's NSDictionary's dictionaryWithDictionary:rec)'s allKeys() as list |
|||
end keys |
|||
-- lookupDict :: a -> Dict -> Maybe b |
|||
on lookupDict(k, dct) |
|||
set ca to current application |
|||
set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s objectForKey:k |
|||
if v ≠ missing value then |
|||
Just(item 1 of ((ca's NSArray's arrayWithObject:v) as list)) |
|||
else |
|||
Nothing() |
|||
end if |
|||
end lookupDict |
|||
-- Lift 2nd class handler function into 1st class script wrapper |
|||
-- mReturn :: First-class m => (a -> b) -> m (a -> b) |
|||
on mReturn(f) |
|||
if class of f is script then |
|||
f |
|||
else |
|||
script |
|||
property |λ| : f |
|||
end script |
|||
end if |
|||
end mReturn |
|||
-- map :: (a -> b) -> [a] -> [b] |
|||
on map(f, xs) |
|||
tell mReturn(f) |
|||
set lng to length of xs |
|||
set lst to {} |
|||
repeat with i from 1 to lng |
|||
set end of lst to |λ|(item i of xs, i, xs) |
|||
end repeat |
|||
return lst |
|||
end tell |
|||
end map |
|||
-- mapFromList :: [(k, v)] -> Dict |
|||
on mapFromList(kvs) |
|||
set tpl to unzip(kvs) |
|||
script |
|||
on |λ|(x) |
|||
x as string |
|||
end |λ| |
|||
end script |
|||
(current application's NSDictionary's ¬ |
|||
dictionaryWithObjects:(|2| of tpl) ¬ |
|||
forKeys:map(result, |1| of tpl)) as record |
|||
end mapFromList |
|||
-- min :: Ord a => a -> a -> a |
|||
on min(x, y) |
|||
if y < x then |
|||
y |
|||
else |
|||
x |
|||
end if |
|||
end min |
|||
-- Nothing :: Maybe a |
|||
on Nothing() |
|||
{type:"Maybe", Nothing:true} |
|||
end Nothing |
|||
-- e.g. sortBy(|on|(compare, |length|), ["epsilon", "mu", "gamma", "beta"]) |
|||
-- on :: (b -> b -> c) -> (a -> b) -> a -> a -> c |
|||
on |on|(f, g) |
|||
script |
|||
on |λ|(a, b) |
|||
tell mReturn(g) to set {va, vb} to {|λ|(a), |λ|(b)} |
|||
tell mReturn(f) to |λ|(va, vb) |
|||
end |λ| |
|||
end script |
|||
end |on| |
|||
-- partition :: predicate -> List -> (Matches, nonMatches) |
|||
-- partition :: (a -> Bool) -> [a] -> ([a], [a]) |
|||
on partition(f, xs) |
|||
tell mReturn(f) |
|||
set ys to {} |
|||
set zs to {} |
|||
repeat with x in xs |
|||
set v to contents of x |
|||
if |λ|(v) then |
|||
set end of ys to v |
|||
else |
|||
set end of zs to v |
|||
end if |
|||
end repeat |
|||
end tell |
|||
Tuple(ys, zs) |
|||
end partition |
|||
-- show :: a -> String |
|||
on show(e) |
|||
set c to class of e |
|||
if c = list then |
|||
showList(e) |
|||
else if c = record then |
|||
set mb to lookupDict("type", e) |
|||
if Nothing of mb then |
|||
showDict(e) |
|||
else |
|||
script |
|||
on |λ|(t) |
|||
if "Either" = t then |
|||
set f to my showLR |
|||
else if "Maybe" = t then |
|||
set f to my showMaybe |
|||
else if "Ordering" = t then |
|||
set f to my showOrdering |
|||
else if "Ratio" = t then |
|||
set f to my showRatio |
|||
else if class of t is text and t begins with "Tuple" then |
|||
set f to my showTuple |
|||
else |
|||
set f to my showDict |
|||
end if |
|||
tell mReturn(f) to |λ|(e) |
|||
end |λ| |
|||
end script |
|||
tell result to |λ|(Just of mb) |
|||
end if |
|||
else if c = date then |
|||
"\"" & showDate(e) & "\"" |
|||
else if c = text then |
|||
"'" & e & "'" |
|||
else if (c = integer or c = real) then |
|||
e as text |
|||
else if c = class then |
|||
"null" |
|||
else |
|||
try |
|||
e as text |
|||
on error |
|||
("«" & c as text) & "»" |
|||
end try |
|||
end if |
|||
end show |
|||
-- showList :: [a] -> String |
|||
on showList(xs) |
|||
"[" & intercalateS(", ", map(my show, xs)) & "]" |
|||
end showList |
|||
-- showTuple :: Tuple -> String |
|||
on showTuple(tpl) |
|||
set ca to current application |
|||
script |
|||
on |λ|(n) |
|||
set v to (ca's NSDictionary's dictionaryWithDictionary:tpl)'s objectForKey:(n as string) |
|||
if v ≠ missing value then |
|||
unQuoted(show(item 1 of ((ca's NSArray's arrayWithObject:v) as list))) |
|||
else |
|||
missing value |
|||
end if |
|||
end |λ| |
|||
end script |
|||
"(" & intercalateS(", ", map(result, enumFromToInt(1, length of tpl))) & ")" |
|||
end showTuple |
|||
-- snd :: (a, b) -> b |
|||
on snd(tpl) |
|||
if class of tpl is record then |
|||
|2| of tpl |
|||
else |
|||
item 2 of tpl |
|||
end if |
|||
end snd |
|||
-- Enough for small scale sorts. |
|||
-- Use instead sortOn :: Ord b => (a -> b) -> [a] -> [a] |
|||
-- which is equivalent to the more flexible sortBy(comparing(f), xs) |
|||
-- and uses a much faster ObjC NSArray sort method |
|||
-- sortBy :: (a -> a -> Ordering) -> [a] -> [a] |
|||
on sortBy(f, xs) |
|||
if length of xs > 1 then |
|||
set h to item 1 of xs |
|||
set f to mReturn(f) |
|||
script |
|||
on |λ|(x) |
|||
f's |λ|(x, h) ≤ 0 |
|||
end |λ| |
|||
end script |
|||
set lessMore to partition(result, rest of xs) |
|||
sortBy(f, |1| of lessMore) & {h} & ¬ |
|||
sortBy(f, |2| of lessMore) |
|||
else |
|||
xs |
|||
end if |
|||
end sortBy |
|||
-- splitOn :: String -> String -> [String] |
|||
on splitOn(pat, src) |
|||
set {dlm, my text item delimiters} to ¬ |
|||
{my text item delimiters, pat} |
|||
set xs to text items of src |
|||
set my text item delimiters to dlm |
|||
return xs |
|||
end splitOn |
|||
-- swap :: (a, b) -> (b, a) |
|||
on swap(ab) |
|||
if class of ab is record then |
|||
Tuple(|2| of ab, |1| of ab) |
|||
else |
|||
{item 2 of ab, item 1 of ab} |
|||
end if |
|||
end swap |
|||
-- take :: Int -> [a] -> [a] |
|||
-- take :: Int -> String -> String |
|||
on take(n, xs) |
|||
set c to class of xs |
|||
if list is c then |
|||
if 0 < n then |
|||
items 1 thru min(n, length of xs) of xs |
|||
else |
|||
{} |
|||
end if |
|||
else if string is c then |
|||
if 0 < n then |
|||
text 1 thru min(n, length of xs) of xs |
|||
else |
|||
"" |
|||
end if |
|||
else if script is c then |
|||
set ys to {} |
|||
repeat with i from 1 to n |
|||
set v to xs's |λ|() |
|||
if missing value is v then |
|||
return ys |
|||
else |
|||
set end of ys to v |
|||
end if |
|||
end repeat |
|||
return ys |
|||
else |
|||
missing value |
|||
end if |
|||
end take |
|||
-- toLower :: String -> String |
|||
on toLower(str) |
|||
set ca to current application |
|||
((ca's NSString's stringWithString:(str))'s ¬ |
|||
lowercaseStringWithLocale:(ca's NSLocale's currentLocale())) as text |
|||
end toLower |
|||
-- Tuple (,) :: a -> b -> (a, b) |
|||
on Tuple(a, b) |
|||
{type:"Tuple", |1|:a, |2|:b, length:2} |
|||
end Tuple |
|||
-- tupleFromList :: [a] -> (a, a ...) |
|||
on tupleFromList(xs) |
|||
set lng to length of xs |
|||
if 1 < lng then |
|||
if 2 < lng then |
|||
set strSuffix to lng as string |
|||
else |
|||
set strSuffix to "" |
|||
end if |
|||
script kv |
|||
on |λ|(a, x, i) |
|||
insertMap(a, (i as string), x) |
|||
end |λ| |
|||
end script |
|||
foldl(kv, {type:"Tuple" & strSuffix}, xs) & {length:lng} |
|||
else |
|||
missing value |
|||
end if |
|||
end tupleFromList |
|||
-- unQuoted :: String -> String |
|||
on unQuoted(s) |
|||
script p |
|||
on |λ|(x) |
|||
--{34, 39} contains id of x |
|||
34 = id of x |
|||
end |λ| |
|||
end script |
|||
dropAround(p, s) |
|||
end unQuoted |
|||
-- unzip :: [(a,b)] -> ([a],[b]) |
|||
on unzip(xys) |
|||
set xs to {} |
|||
set ys to {} |
|||
repeat with xy in xys |
|||
set end of xs to |1| of xy |
|||
set end of ys to |2| of xy |
|||
end repeat |
|||
return Tuple(xs, ys) |
|||
end unzip |
|||
-- words :: String -> [String] |
|||
on |words|(s) |
|||
set ca to current application |
|||
(((ca's NSString's stringWithString:(s))'s ¬ |
|||
componentsSeparatedByCharactersInSet:(ca's ¬ |
|||
NSCharacterSet's whitespaceAndNewlineCharacterSet()))'s ¬ |
|||
filteredArrayUsingPredicate:(ca's ¬ |
|||
NSPredicate's predicateWithFormat:"0 < length")) as list |
|||
end |words|</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>"[('july', '16')]"</pre> |
|||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="rebol">dates: [ |
|||
[May 15] [May 16] [May 19] |
|||
[June 17] [June 18] |
|||
[July 14] [July 16] |
|||
[August 14] [August 15] [August 17] |
|||
] |
|||
print ["possible dates:" dates] |
|||
print "\n(1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too." |
|||
print "\t-> meaning: the month cannot have a unique day" |
|||
dates: filter dates 'd [ |
|||
in? d\0 map select dates 'dd [ |
|||
1 = size select dates 'pd -> pd\1=dd\1 |
|||
] 'dd -> dd\0 |
|||
] |
|||
print ["\t-> remaining:" dates] |
|||
print "\n(2) Bernard: At first I don't know when Cheryl's birthday is, but I know now." |
|||
print "\t-> meaning: the day must be unique" |
|||
dates: select dates 'd [ |
|||
1 = size select dates 'pd -> pd\1=d\1 |
|||
] |
|||
print ["\t-> remaining:" dates] |
|||
print "\n(3) Albert: Then I also know when Cheryl's birthday is." |
|||
print "\t-> meaning: the month must be unique" |
|||
dates: select dates 'd [ |
|||
1 = size select dates 'pd -> pd\0=d\0 |
|||
] |
|||
print ["\t-> remaining:" dates] |
|||
print ["\nCheryl's birthday:" first dates]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>possible dates: [[May 15] [May 16] [May 19] [June 17] [June 18] [July 14] [July 16] [August 14] [August 15] [August 17]] |
|||
(1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. |
|||
-> meaning: the month cannot have a unique day |
|||
-> remaining: [[July 14] [July 16] [August 14] [August 15] [August 17]] |
|||
(2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. |
|||
-> meaning: the day must be unique |
|||
-> remaining: [[July 16] [August 15] [August 17]] |
|||
(3) Albert: Then I also know when Cheryl's birthday is. |
|||
-> meaning: the month must be unique |
|||
-> remaining: [[July 16]] |
|||
Cheryl's birthday: [July 16]</pre> |
|||
=={{header|AutoHotkey}}== |
|||
<syntaxhighlight lang="autohotkey">oDates:= {"May" : [ 15, 16, 19] |
|||
,"Jun" : [ 17, 18] |
|||
,"Jul" : [14, 16] |
|||
,"Aug" : [14, 15, 17]} |
|||
filter1(oDates) |
|||
filter2(oDates) |
|||
filter3(oDates) |
|||
MsgBox % result := checkAnswer(oDates) |
|||
return |
|||
filter1(ByRef oDates){ ; remove months that has a unique day in it. |
|||
for d, obj in MonthsOfDay(oDates) |
|||
if (obj.count() = 1) |
|||
for m, bool in obj |
|||
oDates.Remove(m) |
|||
} |
|||
filter2(ByRef oDates){ ; remove non-unique days from remaining months. |
|||
for d, obj in MonthsOfDay(oDates) |
|||
if (obj.count() > 1) |
|||
for m, bool in obj |
|||
for i, day in oDates[m] |
|||
if (day=d) |
|||
oDates[m].Remove(i) |
|||
} |
|||
filter3(ByRef oDates){ ; remove months that has multiple days from remaining months. |
|||
oRemove := [] |
|||
for m, obj in oDates |
|||
if obj.count() > 1 |
|||
oRemove.Push(m) |
|||
for i, m in oRemove |
|||
oDates.Remove(m) |
|||
} |
|||
MonthsOfDay(oDates){ ; create a list of months per day. |
|||
MonthsOfDay := [] |
|||
for m, obj in oDates |
|||
for i, d in obj |
|||
MonthsOfDay[d, m] := 1 |
|||
return MonthsOfDay |
|||
} |
|||
checkAnswer(oDates){ ; check unique answer if any. |
|||
if oDates.count()>1 |
|||
return false |
|||
for m, obj in oDates |
|||
if obj.count() > 1 |
|||
return false |
|||
else |
|||
return m " " obj.1 |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Jul 16</pre> |
|||
=={{header|AWK}}== |
|||
<syntaxhighlight lang="awk"> |
|||
# syntax: GAWK -f CHERYLS_BIRTHDAY.AWK [-v debug={0|1}] |
|||
# |
|||
# sorting: |
|||
# PROCINFO["sorted_in"] is used by GAWK |
|||
# SORTTYPE is used by Thompson Automation's TAWK |
|||
# |
|||
BEGIN { |
|||
debug += 0 |
|||
PROCINFO["sorted_in"] = "@ind_num_asc" ; SORTTYPE = 1 |
|||
n = split("05/15,05/16,05/19,06/17,06/18,07/14,07/16,08/14,08/15,08/17",arr,",") |
|||
for (i=1; i<=n; i++) { # move dates to a more friendly structure |
|||
mmdd_arr[arr[i]] = "" |
|||
} |
|||
print("Cheryl offers these ten MM/DD choices:") |
|||
cb_show_dates() |
|||
printf("Cheryl then tells Albert her birth 'month' and Bernard her birth 'day'.\n\n") |
|||
print("1. Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.") |
|||
cb_filter1() |
|||
print("2. Bernard: At first I don't know when Cheryl's birthday is, but I know now.") |
|||
cb_filter2() |
|||
print("3. Albert: Then I also know when Cheryl's birthday is.") |
|||
cb_filter3() |
|||
exit(0) |
|||
} |
|||
function cb_filter1( i,j) { |
|||
print("deduction: the month cannot have a unique day, leaving:") |
|||
cb_load_arrays(4) |
|||
for (j in arr1) { |
|||
if (arr1[j] == 1) { |
|||
if (debug) { printf("unique day %s\n",j) } |
|||
arr3[arr2[j]] = "" |
|||
} |
|||
} |
|||
cb_remove_dates() |
|||
} |
|||
function cb_filter2( i,j) { |
|||
print("deduction: the day must be unique, leaving:") |
|||
cb_load_arrays(4) |
|||
for (j in arr1) { |
|||
if (arr1[j] > 1) { |
|||
if (debug) { printf("non-unique day %s\n",j) } |
|||
arr3[j] = "" |
|||
} |
|||
} |
|||
cb_remove_dates("...") |
|||
} |
|||
function cb_filter3( i,j) { |
|||
print("deduction: the month must be unique, leaving:") |
|||
cb_load_arrays(1) |
|||
for (j in arr1) { |
|||
if (arr1[j] > 1) { |
|||
if (debug) { printf("non-unique month %s\n",j) } |
|||
arr3[j] = "" |
|||
} |
|||
} |
|||
cb_remove_dates() |
|||
} |
|||
function cb_load_arrays(col, i,key) { |
|||
delete arr1 |
|||
delete arr2 |
|||
delete arr3 |
|||
for (i in mmdd_arr) { |
|||
key = substr(i,col,2) |
|||
arr1[key]++ |
|||
arr2[key] = substr(i,1,2) |
|||
} |
|||
} |
|||
function cb_remove_dates(pattern, i,j) { |
|||
for (j in arr3) { |
|||
for (i in mmdd_arr) { |
|||
if (i ~ ("^" pattern j)) { |
|||
if (debug) { printf("removing %s\n",i) } |
|||
delete mmdd_arr[i] |
|||
} |
|||
} |
|||
} |
|||
cb_show_dates() |
|||
} |
|||
function cb_show_dates( i) { |
|||
if (debug) { printf("%d remaining\n",length(mmdd_arr)) } |
|||
for (i in mmdd_arr) { |
|||
printf("%s ",i) |
|||
} |
|||
printf("\n\n") |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl offers these ten MM/DD choices: |
|||
05/15 05/16 05/19 06/17 06/18 07/14 07/16 08/14 08/15 08/17 |
|||
Cheryl then tells Albert her birth 'month' and Bernard her birth 'day'. |
|||
1. Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. |
|||
deduction: the month cannot have a unique day, leaving: |
|||
07/14 07/16 08/14 08/15 08/17 |
|||
2. Bernard: At first I don't know when Cheryl's birthday is, but I know now. |
|||
deduction: the day must be unique, leaving: |
|||
07/16 08/15 08/17 |
|||
3. Albert: Then I also know when Cheryl's birthday is. |
|||
deduction: the month must be unique, leaving: |
|||
07/16 |
|||
</pre> |
|||
=={{header|C}}== |
|||
{{trans|C#}} |
|||
<syntaxhighlight lang="c">#include <stdbool.h> |
|||
#include <stdio.h> |
|||
char *months[] = { |
|||
"ERR", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
|||
}; |
|||
struct Date { |
|||
int month, day; |
|||
bool active; |
|||
} dates[] = { |
|||
{5,15,true}, {5,16,true}, {5,19,true}, |
|||
{6,17,true}, {6,18,true}, |
|||
{7,14,true}, {7,16,true}, |
|||
{8,14,true}, {8,15,true}, {8,17,true} |
|||
}; |
|||
#define UPPER_BOUND (sizeof(dates) / sizeof(struct Date)) |
|||
void printRemaining() { |
|||
int i, c; |
|||
for (i = 0, c = 0; i < UPPER_BOUND; i++) { |
|||
if (dates[i].active) { |
|||
c++; |
|||
} |
|||
} |
|||
printf("%d remaining.\n", c); |
|||
} |
|||
void printAnswer() { |
|||
int i; |
|||
for (i = 0; i < UPPER_BOUND; i++) { |
|||
if (dates[i].active) { |
|||
printf("%s, %d\n", months[dates[i].month], dates[i].day); |
|||
} |
|||
} |
|||
} |
|||
void firstPass() { |
|||
// the month cannot have a unique day |
|||
int i, j, c; |
|||
for (i = 0; i < UPPER_BOUND; i++) { |
|||
c = 0; |
|||
for (j = 0; j < UPPER_BOUND; j++) { |
|||
if (dates[j].day == dates[i].day) { |
|||
c++; |
|||
} |
|||
} |
|||
if (c == 1) { |
|||
for (j = 0; j < UPPER_BOUND; j++) { |
|||
if (!dates[j].active) continue; |
|||
if (dates[j].month == dates[i].month) { |
|||
dates[j].active = false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
void secondPass() { |
|||
// the day must now be unique |
|||
int i, j, c; |
|||
for (i = 0; i < UPPER_BOUND; i++) { |
|||
if (!dates[i].active) continue; |
|||
c = 0; |
|||
for (j = 0; j < UPPER_BOUND; j++) { |
|||
if (!dates[j].active) continue; |
|||
if (dates[j].day == dates[i].day) { |
|||
c++; |
|||
} |
|||
} |
|||
if (c > 1) { |
|||
for (j = 0; j < UPPER_BOUND; j++) { |
|||
if (!dates[j].active) continue; |
|||
if (dates[j].day == dates[i].day) { |
|||
dates[j].active = false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
void thirdPass() { |
|||
// the month must now be unique |
|||
int i, j, c; |
|||
for (i = 0; i < UPPER_BOUND; i++) { |
|||
if (!dates[i].active) continue; |
|||
c = 0; |
|||
for (j = 0; j < UPPER_BOUND; j++) { |
|||
if (!dates[j].active) continue; |
|||
if (dates[j].month == dates[i].month) { |
|||
c++; |
|||
} |
|||
} |
|||
if (c > 1) { |
|||
for (j = 0; j < UPPER_BOUND; j++) { |
|||
if (!dates[j].active) continue; |
|||
if (dates[j].month == dates[i].month) { |
|||
dates[j].active = false; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
int main() { |
|||
printRemaining(); |
|||
// the month cannot have a unique day |
|||
firstPass(); |
|||
printRemaining(); |
|||
// the day must now be unique |
|||
secondPass(); |
|||
printRemaining(); |
|||
// the month must now be unique |
|||
thirdPass(); |
|||
printAnswer(); |
|||
return 0; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>10 remaining. |
|||
5 remaining. |
|||
3 remaining. |
|||
Jul, 16</pre> |
|||
=={{header|C sharp|C#}}== |
|||
<syntaxhighlight lang="csharp">public static class CherylsBirthday |
|||
{ |
|||
public static void Main() { |
|||
var dates = new HashSet<(string month, int day)> { |
|||
("May", 15), |
|||
("May", 16), |
|||
("May", 19), |
|||
("June", 17), |
|||
("June", 18), |
|||
("July", 14), |
|||
("July", 16), |
|||
("August", 14), |
|||
("August", 15), |
|||
("August", 17) |
|||
}; |
|||
Console.WriteLine(dates.Count + " remaining."); |
|||
//The month cannot have a unique day. |
|||
var monthsWithUniqueDays = dates.GroupBy(d => d.day).Where(g => g.Count() == 1).Select(g => g.First().month).ToHashSet(); |
|||
dates.RemoveWhere(d => monthsWithUniqueDays.Contains(d.month)); |
|||
Console.WriteLine(dates.Count + " remaining."); |
|||
//The day must now be unique. |
|||
dates.IntersectWith(dates.GroupBy(d => d.day).Where(g => g.Count() == 1).Select(g => g.First())); |
|||
Console.WriteLine(dates.Count + " remaining."); |
|||
//The month must now be unique. |
|||
dates.IntersectWith(dates.GroupBy(d => d.month).Where(g => g.Count() == 1).Select(g => g.First())); |
|||
Console.WriteLine(dates.Single()); |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
10 remaining. |
|||
5 remaining. |
|||
3 remaining. |
|||
(July, 16) |
|||
</pre> |
|||
=={{header|C++}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="cpp">#include <algorithm> |
|||
#include <iostream> |
|||
#include <vector> |
|||
using namespace std; |
|||
const vector<string> MONTHS = { |
|||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
|||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
|||
}; |
|||
struct Birthday { |
|||
int month, day; |
|||
friend ostream &operator<<(ostream &, const Birthday &); |
|||
}; |
|||
ostream &operator<<(ostream &out, const Birthday &birthday) { |
|||
return out << MONTHS[birthday.month - 1] << ' ' << birthday.day; |
|||
} |
|||
template <typename C> |
|||
bool monthUniqueIn(const Birthday &b, const C &container) { |
|||
auto it = cbegin(container); |
|||
auto end = cend(container); |
|||
int count = 0; |
|||
while (it != end) { |
|||
if (it->month == b.month) { |
|||
count++; |
|||
} |
|||
it = next(it); |
|||
} |
|||
return count == 1; |
|||
} |
|||
template <typename C> |
|||
bool dayUniqueIn(const Birthday &b, const C &container) { |
|||
auto it = cbegin(container); |
|||
auto end = cend(container); |
|||
int count = 0; |
|||
while (it != end) { |
|||
if (it->day == b.day) { |
|||
count++; |
|||
} |
|||
it = next(it); |
|||
} |
|||
return count == 1; |
|||
} |
|||
template <typename C> |
|||
bool monthWithUniqueDayIn(const Birthday &b, const C &container) { |
|||
auto it = cbegin(container); |
|||
auto end = cend(container); |
|||
while (it != end) { |
|||
if (it->month == b.month && dayUniqueIn(*it, container)) { |
|||
return true; |
|||
} |
|||
it = next(it); |
|||
} |
|||
return false; |
|||
} |
|||
int main() { |
|||
vector<Birthday> choices = { |
|||
{5, 15}, {5, 16}, {5, 19}, {6, 17}, {6, 18}, |
|||
{7, 14}, {7, 16}, {8, 14}, {8, 15}, {8, 17}, |
|||
}; |
|||
// Albert knows the month but doesn't know the day. |
|||
// So the month can't be unique within the choices. |
|||
vector<Birthday> filtered; |
|||
for (auto bd : choices) { |
|||
if (!monthUniqueIn(bd, choices)) { |
|||
filtered.push_back(bd); |
|||
} |
|||
} |
|||
// Albert also knows that Bernard doesn't know the answer. |
|||
// So the month can't have a unique day. |
|||
vector<Birthday> filtered2; |
|||
for (auto bd : filtered) { |
|||
if (!monthWithUniqueDayIn(bd, filtered)) { |
|||
filtered2.push_back(bd); |
|||
} |
|||
} |
|||
// Bernard now knows the answer. |
|||
// So the day must be unique within the remaining choices. |
|||
vector<Birthday> filtered3; |
|||
for (auto bd : filtered2) { |
|||
if (dayUniqueIn(bd, filtered2)) { |
|||
filtered3.push_back(bd); |
|||
} |
|||
} |
|||
// Albert now knows the answer too. |
|||
// So the month must be unique within the remaining choices. |
|||
vector<Birthday> filtered4; |
|||
for (auto bd : filtered3) { |
|||
if (monthUniqueIn(bd, filtered3)) { |
|||
filtered4.push_back(bd); |
|||
} |
|||
} |
|||
if (filtered4.size() == 1) { |
|||
cout << "Cheryl's birthday is " << filtered4[0] << '\n'; |
|||
} else { |
|||
cout << "Something went wrong!\n"; |
|||
} |
|||
return 0; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Cheryl's birthday is Jul 16</pre> |
|||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
< |
<syntaxhighlight lang="lisp"> |
||
;; Author: Amir Teymuri, Saturday 20.10.2018 |
|||
(defparameter *possible-dates* |
(defparameter *possible-dates* |
||
'((15 . may) (16 . may) (19 . may) |
'((15 . may) (16 . may) (19 . may) |
||
Line 37: | Line 1,457: | ||
(14 . august) (15 . august) (17 . august))) |
(14 . august) (15 . august) (17 . august))) |
||
(defun unique-date-parts (possible-dates &key (alist-look-at #'car) (alist-r-assoc #'assoc)) |
|||
(defun count-items (list) |
|||
(let* ((date-parts (mapcar alist-look-at possible-dates)) |
|||
"returns a list of (item how-many-of-it)" |
|||
( |
(unique-date-parts (remove-if #'(lambda (part) (> (count part date-parts) 1)) date-parts))) |
||
(remove-duplicates list :test #'equal))) |
|||
(defun filter-dates (possible-dates &key (alist-look-at #'car) (alist-r-assoc #'assoc)) |
|||
(let ((unique-date-parts (remove-if-not #'(lambda (part) (= (cadr part) 1)) |
|||
(count-items (mapcar alist-look-at possible-dates))))) |
|||
(mapcar #'(lambda (part) (funcall alist-r-assoc part possible-dates)) |
(mapcar #'(lambda (part) (funcall alist-r-assoc part possible-dates)) |
||
unique-date-parts))) |
|||
(defun person (person possible-dates) |
(defun person (person possible-dates) |
||
"Who's turn is it to think?" |
"Who's turn is it to think?" |
||
(case person |
(case person |
||
('albert ( |
('albert (unique-date-parts possible-dates :alist-look-at #'cdr :alist-r-assoc #'rassoc)) |
||
('bernard ( |
('bernard (unique-date-parts possible-dates :alist-look-at #'car :alist-r-assoc #'assoc)))) |
||
(defun cheryls-birthday (possible-dates) |
(defun cheryls-birthday (possible-dates) |
||
(person 'albert |
(person 'albert |
||
(person 'bernard |
(person 'bernard |
||
(set-difference |
(set-difference |
||
possible-dates |
possible-dates |
||
(person 'bernard possible-dates |
(person 'bernard possible-dates) |
||
:key #'cdr)))) |
:key #'cdr)))) |
||
(cheryls-birthday *possible-dates*) ;; => ((16 . JULY)) |
(cheryls-birthday *possible-dates*) ;; => ((16 . JULY)) |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|D}}== |
|||
<syntaxhighlight lang="d">import std.algorithm.iteration : filter, joiner, map; |
|||
import std.algorithm.searching : canFind; |
|||
import std.algorithm.sorting : sort; |
|||
import std.array : array; |
|||
import std.datetime.date : Date, Month; |
|||
import std.stdio : writeln; |
|||
void main() { |
|||
auto choices = [ |
|||
// Month.jan |
|||
Date(2019, Month.may, 15), |
|||
Date(2019, Month.may, 16), |
|||
Date(2019, Month.may, 19), // unique day (1) |
|||
Date(2019, Month.jun, 17), |
|||
Date(2019, Month.jun, 18), // unique day (1) |
|||
Date(2019, Month.jul, 14), |
|||
Date(2019, Month.jul, 16), // final answer |
|||
Date(2019, Month.aug, 14), |
|||
Date(2019, Month.aug, 15), |
|||
Date(2019, Month.aug, 17), |
|||
]; |
|||
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer |
|||
auto uniqueMonths = choices.sort!"a.day < b.day".groupBy.filter!"a.array.length == 1".joiner.map!"a.month"; |
|||
// writeln(uniqueMonths.save); |
|||
auto filter1 = choices.filter!(a => !canFind(uniqueMonths.save, a.month)).array; |
|||
// Bernard now knows the answer, so the day must be unique within the remaining choices |
|||
auto uniqueDays = filter1.sort!"a.day < b.day".groupBy.filter!"a.array.length == 1".joiner.map!"a.day"; |
|||
auto filter2 = filter1.filter!(a => canFind(uniqueDays.save, a.day)).array; |
|||
// Albert knows the answer too, so the month must be unique within the remaining choices |
|||
auto birthDay = filter2.sort!"a.month < b.month".groupBy.filter!"a.array.length == 1".joiner.front; |
|||
// print the result |
|||
writeln(birthDay.month, " ", birthDay.day); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>jul 16</pre> |
|||
=={{header|F_Sharp|F#}}== |
|||
<syntaxhighlight lang="fsharp"> |
|||
//Find Cheryl's Birthday. Nigel Galloway: October 23rd., 2018 |
|||
type Month = |May |June |July |August |
|||
let fN n= n |> List.filter(fun (_,n)->(List.length n) < 2) |> List.unzip |
|||
let dates = [(May,15);(May,16);(May,19);(June,17);(June,18);(July,14);(July,16);(August,14);(August,15);(August,17)] |
|||
let _,n = dates |> List.groupBy snd |> fN |
|||
let i = n |> List.concat |> List.map fst |> Set.ofList |
|||
let _,g = dates |> List.filter(fun (n,_)->not (Set.contains n i)) |> List.groupBy snd |> fN |
|||
let _,e = List.concat g |> List.groupBy fst |> fN |
|||
printfn "%A" e |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[[(July, 16)]] |
|||
</pre> |
|||
=={{header|Factor}}== |
|||
<syntaxhighlight lang="factor">USING: assocs calendar.english fry io kernel prettyprint |
|||
sequences sets.extras ; |
|||
: unique-by ( seq quot -- newseq ) |
|||
2dup map non-repeating '[ @ _ member? ] filter ; inline |
|||
ALIAS: day first |
|||
ALIAS: month second |
|||
{ |
|||
{ 15 5 } { 16 5 } { 19 5 } { 17 6 } { 18 6 } |
|||
{ 14 7 } { 16 7 } { 14 8 } { 15 8 } { 17 8 } |
|||
} |
|||
! the month cannot have a unique day |
|||
dup [ day ] map non-repeating over extract-keys values |
|||
'[ month _ member? ] reject |
|||
! of the remaining dates, day must be unique |
|||
[ day ] unique-by |
|||
! of the remaining dates, month must be unique |
|||
[ month ] unique-by |
|||
! print a date that looks like { { 16 7 } } |
|||
first first2 month-name write bl .</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
July 16 |
|||
</pre> |
|||
=={{header|FreeBASIC}}== |
|||
{{trans|ALGOL 68}} |
|||
<syntaxhighlight lang="vbnet">Dim As Integer i, j, contarDias, dia, mes |
|||
Dim fechas(1 To 4, 1 To 6) As Integer => {{0, 15, 16, 0, 0, 19}, {0, 0, 0, 17, 18, 0}, {14, 0, 16, 0, 0, 0}, {14, 15, 0, 17, 0, 0}} |
|||
Dim nombreMes(1 To 4) As String => {"May", "June", "July", "August"} |
|||
Print "Cheryl tells Albert the month and Bernard the day" |
|||
Print "Albert doesn't know the date and knows Bernard doesn't either" |
|||
' elimiate the months with unique days |
|||
For i = 1 To 6 |
|||
contarDias = 0 |
|||
dia = 0 |
|||
mes = 0 |
|||
For j = 1 To 4 |
|||
If fechas(j, i) <> 0 Then |
|||
contarDias += 1 |
|||
dia = fechas(j, i) |
|||
mes = j |
|||
End If |
|||
Next j |
|||
If contarDias = 1 Then |
|||
Print " Eliminating "; nombreMes(mes); ", "; Str(dia); "th is unique" |
|||
For j = 1 To 6 |
|||
fechas(mes, j) = 0 |
|||
Next j |
|||
End If |
|||
Next i |
|||
Print "Bernard now knows the date" |
|||
' eliminate the days that aren't unique |
|||
For i = 1 To 6 |
|||
contarDias = 0 |
|||
dia = 0 |
|||
mes = 0 |
|||
For j = 1 To 4 |
|||
If fechas(j, i) <> 0 Then |
|||
contarDias += 1 |
|||
dia = fechas(j, i) |
|||
mes = j |
|||
End If |
|||
Next j |
|||
If contarDias > 1 Then |
|||
Print " Eliminating "; Str(dia); "th, it is non-unique" |
|||
For j = 1 To 4 |
|||
fechas(j, i) = 0 |
|||
Next j |
|||
End If |
|||
Next i |
|||
Print "Albert now knows the date" |
|||
' eliminate months with non-unique days |
|||
For i = 1 To 4 |
|||
contarDias = 0 |
|||
dia = 0 |
|||
mes = 0 |
|||
For j = 1 To 6 |
|||
If fechas(i, j) <> 0 Then |
|||
contarDias += 1 |
|||
dia = fechas(i, j) |
|||
mes = i |
|||
End If |
|||
Next j |
|||
If contarDias > 1 Then |
|||
Print " Eliminating "; nombreMes(i); ", it has multiple days" |
|||
For j = 1 To 6 |
|||
fechas(i, j) = 0 |
|||
Next j |
|||
End If |
|||
Next i |
|||
Print "Cheryl's birthday: "; |
|||
For i = 1 To 4 |
|||
For j = 1 To 6 |
|||
If fechas(i, j) <> 0 Then |
|||
Print " "; nombreMes(i); " "; Str(fechas(i, j)); "th" |
|||
End If |
|||
Next j |
|||
Next i |
|||
Sleep</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Same as ALGOL 68 entry.</pre> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 161: | Line 1,752: | ||
fmt.Println("Something went wrong!") |
fmt.Println("Something went wrong!") |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 167: | Line 1,758: | ||
Cheryl's birthday is July 16 |
Cheryl's birthday is July 16 |
||
</pre> |
</pre> |
||
=={{header|Fortran}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="fortran"> |
|||
program code_translation |
|||
implicit none |
|||
character(len=3), dimension(13) :: months = ["ERR", "Jan", "Feb", "Mar", "Apr", "May",& |
|||
"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
|||
type :: Date |
|||
integer :: month, day |
|||
logical :: active |
|||
end type Date |
|||
type(Date), dimension(10) :: dates = [Date(5,15,.true.), Date(5,16,.true.), Date(5,19,.true.), & |
|||
Date(6,17,.true.), Date(6,18,.true.), & |
|||
Date(7,14,.true.), Date(7,16,.true.), & |
|||
Date(8,14,.true.), Date(8,15,.true.), Date(8,17,.true.)] |
|||
integer, parameter :: UPPER_BOUND = size(dates) |
|||
write(*,*) 'possible dates: [[May 15] [May 16] [May 19] [June 17] [June 18] [July 14] [July 16] [August 14] [August 15] [August & |
|||
17]]' |
|||
write(*,*) |
|||
write(*,*) '(1) Albert: I don''t know when Cheryl''s birthday is, but I know that Bernard does not know too.' |
|||
write(*,*) ' -> meaning: the month cannot have a unique day' |
|||
write(*,*) ' -> remaining: [[July 14] [July 16] [August 14] [August 15] [August 17]] ' |
|||
write(*,*) |
|||
write(*,*) "(2) Bernard: At first I don't know when Cheryl's birthday is, but I know now." |
|||
write(*,*) ' -> meaning: the day must be unique' |
|||
write(*,*) ' -> remaining: [[July 16] [August 15] [August 17]] ' |
|||
write(*,*) |
|||
write(*,*) '(3) Albert: Then I also know when Cheryl''s birthday is.' |
|||
write(*,*) ' -> meaning: the month must be unique' |
|||
write(*,*) ' -> remaining: [[July 16]] ' |
|||
call printRemaining() |
|||
! the month cannot have a unique day |
|||
call firstPass() |
|||
call printRemaining() |
|||
! the day must now be unique |
|||
call secondPass() |
|||
call printRemaining() |
|||
! the month must now be unique |
|||
call thirdPass() |
|||
call printAnswer() |
|||
contains |
|||
subroutine printRemaining() |
|||
integer :: i, c |
|||
do i = 1, UPPER_BOUND |
|||
if (dates(i)%active) then |
|||
write(*,'(a,1x,i0,1x)',advance="no") months(dates(i)%month+1),dates(i)%day |
|||
c = c + 1 |
|||
end if |
|||
end do |
|||
! |
|||
write(*,*) |
|||
end subroutine printRemaining |
|||
subroutine printAnswer() |
|||
integer :: i |
|||
write(*,'(a)',advance ='no') 'Cheryl''s birtday is on ' |
|||
do i = 1, UPPER_BOUND |
|||
if (dates(i)%active) then |
|||
write(*,'(a,1a1,i0)') trim(months(dates(i)%month+1)), ",", dates(i)%day |
|||
end if |
|||
end do |
|||
end subroutine printAnswer |
|||
subroutine firstPass() |
|||
! the month cannot have a unique day |
|||
integer :: i, j, c |
|||
do i = 1, UPPER_BOUND |
|||
c = 0 |
|||
do j = 1, UPPER_BOUND |
|||
if (dates(j)%day == dates(i)%day) then |
|||
c = c + 1 |
|||
end if |
|||
end do |
|||
if (c == 1) then |
|||
do j = 1, UPPER_BOUND |
|||
if (.not. dates(j)%active) cycle |
|||
if (dates(j)%month == dates(i)%month) then |
|||
dates(j)%active = .false. |
|||
end if |
|||
end do |
|||
end if |
|||
end do |
|||
end subroutine firstPass |
|||
subroutine secondPass() |
|||
! the day must now be unique |
|||
integer :: i, j, c |
|||
do i = 1, UPPER_BOUND |
|||
if (.not. dates(i)%active) cycle |
|||
c = 0 |
|||
do j = 1, UPPER_BOUND |
|||
if (.not. dates(j)%active) cycle |
|||
if (dates(j)%day == dates(i)%day) then |
|||
c = c + 1 |
|||
end if |
|||
end do |
|||
if (c > 1) then |
|||
do j = 1, UPPER_BOUND |
|||
if (.not. dates(j)%active) cycle |
|||
if (dates(j)%day == dates(i)%day) then |
|||
dates(j)%active = .false. |
|||
end if |
|||
end do |
|||
end if |
|||
end do |
|||
end subroutine secondPass |
|||
subroutine thirdPass() |
|||
! the month must now be unique |
|||
integer :: i, j, c |
|||
do i = 1, UPPER_BOUND |
|||
if (.not. dates(i)%active) cycle |
|||
c = 0 |
|||
do j = 1, UPPER_BOUND |
|||
if (.not. dates(j)%active) cycle |
|||
if (dates(j)%month == dates(i)%month) then |
|||
c = c + 1 |
|||
end if |
|||
end do |
|||
if (c > 1) then |
|||
do j = 1, UPPER_BOUND |
|||
if (.not. dates(j)%active) cycle |
|||
if (dates(j)%month == dates(i)%month) then |
|||
dates(j)%active = .false. |
|||
end if |
|||
end do |
|||
end if |
|||
end do |
|||
end subroutine thirdPass |
|||
end program code_translation |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
possible dates: [[May 15] [May 16] [May 19] [June 17] [June 18] [July 14] [July 16] [August 14] [August 15] [August 17]] |
|||
(1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. |
|||
-> meaning: the month cannot have a unique day |
|||
-> remaining: [[July 14] [July 16] [August 14] [August 15] [August 17]] |
|||
(2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. |
|||
-> meaning: the day must be unique |
|||
-> remaining: [[July 16] [August 15] [August 17]] |
|||
(3) Albert: Then I also know when Cheryl's birthday is. |
|||
-> meaning: the month must be unique |
|||
-> remaining: [[July 16]] |
|||
May 15 May 16 May 19 Jun 17 Jun 18 Jul 14 Jul 16 Aug 14 Aug 15 Aug 17 |
|||
Jul 14 Jul 16 Aug 14 Aug 15 Aug 17 |
|||
Jul 16 Aug 15 Aug 17 |
|||
Cheryl's birthday is on Jul,16 |
|||
</Pre> |
|||
=={{header|Groovy}}== |
|||
{{trans|Java}} |
|||
<syntaxhighlight lang="groovy">import java.time.Month |
|||
class Main { |
|||
private static class Birthday { |
|||
private Month month |
|||
private int day |
|||
Birthday(Month month, int day) { |
|||
this.month = month |
|||
this.day = day |
|||
} |
|||
Month getMonth() { |
|||
return month |
|||
} |
|||
int getDay() { |
|||
return day |
|||
} |
|||
@Override |
|||
String toString() { |
|||
return month.toString() + " " + day |
|||
} |
|||
} |
|||
static void main(String[] args) { |
|||
List<Birthday> choices = [ |
|||
new Birthday(Month.MAY, 15), |
|||
new Birthday(Month.MAY, 16), |
|||
new Birthday(Month.MAY, 19), |
|||
new Birthday(Month.JUNE, 17), |
|||
new Birthday(Month.JUNE, 18), |
|||
new Birthday(Month.JULY, 14), |
|||
new Birthday(Month.JULY, 16), |
|||
new Birthday(Month.AUGUST, 14), |
|||
new Birthday(Month.AUGUST, 15), |
|||
new Birthday(Month.AUGUST, 17) |
|||
] |
|||
println("There are ${choices.size()} candidates remaining.") |
|||
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer |
|||
Set<Birthday> uniqueMonths = choices.groupBy { it.getDay() } |
|||
.values() |
|||
.findAll() { it.size() == 1 } |
|||
.flatten() |
|||
.collect { ((Birthday) it).getMonth() } |
|||
.toSet() as Set<Birthday> |
|||
def f1List = choices.findAll { !uniqueMonths.contains(it.getMonth()) } |
|||
println("There are ${f1List.size()} candidates remaining.") |
|||
// Bernard now knows the answer, so the day must be unique within the remaining choices |
|||
List<Birthday> f2List = f1List.groupBy { it.getDay() } |
|||
.values() |
|||
.findAll { it.size() == 1 } |
|||
.flatten() |
|||
.toList() as List<Birthday> |
|||
println("There are ${f2List.size()} candidates remaining.") |
|||
// Albert knows the answer too, so the month must be unique within the remaining choices |
|||
List<Birthday> f3List = f2List.groupBy { it.getMonth() } |
|||
.values() |
|||
.findAll { it.size() == 1 } |
|||
.flatten() |
|||
.toList() as List<Birthday> |
|||
println("There are ${f3List.size()} candidates remaining.") |
|||
if (f3List.size() == 1) { |
|||
println("Cheryl's birthday is ${f3List.head()}") |
|||
} else { |
|||
System.out.println("No unique choice found") |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>There are 10 candidates remaining. |
|||
There are 5 candidates remaining. |
|||
There are 3 candidates remaining. |
|||
There are 1 candidates remaining. |
|||
Cheryl's birthday is JULY 16</pre> |
|||
=={{header|Haskell}}== |
|||
<syntaxhighlight lang="haskell">{-# LANGUAGE OverloadedStrings #-} |
|||
import Data.List as L (filter, groupBy, head, length, sortOn) |
|||
import Data.Map.Strict as M (Map, fromList, keys, lookup) |
|||
import Data.Text as T (Text, splitOn, words) |
|||
import Data.Maybe (fromJust) |
|||
import Data.Ord (comparing) |
|||
import Data.Function (on) |
|||
import Data.Tuple (swap) |
|||
import Data.Bool (bool) |
|||
data DatePart |
|||
= Month |
|||
| Day |
|||
type M = Text |
|||
type D = Text |
|||
main :: IO () |
|||
main = |
|||
print $ |
|||
-- 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) |
|||
(\(x:y:_) -> (x, y)) . T.words <$> |
|||
splitOn |
|||
", " |
|||
"May 15, May 16, May 19, June 17, June 18, \ |
|||
\July 14, July 16, Aug 14, Aug 15, Aug 17" |
|||
----------------------QUERY FUNCTIONS---------------------- |
|||
monthsWithUniqueDays :: Bool -> [(M, D)] -> [(M, D)] |
|||
monthsWithUniqueDays bln xs = |
|||
let months = fst <$> uniquePairing Day xs |
|||
in L.filter (bool not id bln . (`elem` months) . fst) xs |
|||
uniquePairing :: DatePart -> [(M, D)] -> [(M, D)] |
|||
uniquePairing dp xs = |
|||
let f = |
|||
case dp of |
|||
Month -> fst |
|||
Day -> snd |
|||
in (\md -> |
|||
let dct = f md |
|||
uniques = |
|||
L.filter |
|||
((1 ==) . L.length . fromJust . flip M.lookup dct) |
|||
(keys dct) |
|||
in L.filter ((`elem` uniques) . f) xs) |
|||
((((,) . mapFromPairs) <*> mapFromPairs . fmap swap) xs) |
|||
mapFromPairs :: [(M, D)] -> Map Text [Text] |
|||
mapFromPairs xs = |
|||
M.fromList $ |
|||
((,) . fst . L.head) <*> fmap snd <$> |
|||
L.groupBy (on (==) fst) (L.sortOn fst xs)</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>[("July","16")]</pre> |
|||
=={{header|J}}== |
|||
'''Solution:''' |
|||
<syntaxhighlight lang="j">Dates=: cutLF noun define |
|||
15 May |
|||
16 May |
|||
19 May |
|||
17 June |
|||
18 June |
|||
14 July |
|||
16 July |
|||
14 August |
|||
15 August |
|||
17 August |
|||
) |
|||
getDayMonth=: |:@:(cut&>) NB. retrieve lists of days and months from dates |
|||
keep=: adverb def '] #~ u' NB. apply mask to filter dates |
|||
monthsWithUniqueDay=: {./. #~ (1=#)/. NB. list months that have a unique day |
|||
isMonthWithoutUniqueDay=: (] -.@e. monthsWithUniqueDay)/@getDayMonth NB. mask of dates with a month that doesn't have a unique day |
|||
uniqueDayInMonth=: ~.@[ #~ (1=#)/. NB. list of days that are unique to 1 month |
|||
isUniqueDayInMonth=: ([ e. uniqueDayInMonth)/@getDayMonth NB. mask of dates with a day that is unique to 1 month |
|||
uniqueMonth=: ~.@] #~ (1=#)/.~ NB. list of months with 1 unique day |
|||
isUniqueMonth=: (] e. uniqueMonth)/@getDayMonth NB. mask of dates with a month that has 1 unique day</syntaxhighlight> |
|||
'''Usage:''' |
|||
<syntaxhighlight lang="j"> isUniqueMonth keep isUniqueDayInMonth keep isMonthWithoutUniqueDay keep Dates |
|||
+-------+ |
|||
|16 July| |
|||
+-------+</syntaxhighlight> |
|||
===Alternative Approach=== |
|||
The concepts here are the same, of course, it's just the presentation that's different. |
|||
<syntaxhighlight lang="j">possible=: cut;._2 'May 15, May 16, May 19, June 17, June 18, July 14, July 16, August 14, August 15, August 17,' |
|||
Albert=: {."1 NB. Albert knows month |
|||
Bernard=: {:"1 NB. Bernard knows day |
|||
NB. Bernard's understanding of Albert's first pass |
|||
days=: {:"1 possible |
|||
invaliddays=: (1=#/.~ days)#~.days |
|||
months=: {."1 possible |
|||
validmonths=: months -. (days e. invaliddays)#months |
|||
possibleA=. (months e. validmonths)# possible |
|||
NB. Albert's understanding of Bernard's pass |
|||
days=: {:"1 possibleA |
|||
invaliddays=: (1<#/.~ days)#~.days |
|||
possibleB=. (days e. days-.invaliddays)# possibleA |
|||
NB. our understanding of Albert's understanding of Bernard's understanding of Albert's first pass |
|||
months=: {."1 possibleB |
|||
invalidmonths=: (1<#/.~months)#~.months |
|||
echo ;:inv (months e. months -. invalidmonths)#possibleB</syntaxhighlight> |
|||
This gives us the July 16 result we were expecting |
|||
=={{header|Java}}== |
|||
{{trans|D}} |
|||
<syntaxhighlight lang="java">import java.time.Month; |
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import java.util.stream.Collectors; |
|||
public class Main { |
|||
private static class Birthday { |
|||
private Month month; |
|||
private int day; |
|||
public Birthday(Month month, int day) { |
|||
this.month = month; |
|||
this.day = day; |
|||
} |
|||
public Month getMonth() { |
|||
return month; |
|||
} |
|||
public int getDay() { |
|||
return day; |
|||
} |
|||
@Override |
|||
public String toString() { |
|||
return month + " " + day; |
|||
} |
|||
} |
|||
public static void main(String[] args) { |
|||
List<Birthday> choices = List.of( |
|||
new Birthday(Month.MAY, 15), |
|||
new Birthday(Month.MAY, 16), |
|||
new Birthday(Month.MAY, 19), |
|||
new Birthday(Month.JUNE, 17), |
|||
new Birthday(Month.JUNE, 18), |
|||
new Birthday(Month.JULY, 14), |
|||
new Birthday(Month.JULY, 16), |
|||
new Birthday(Month.AUGUST, 14), |
|||
new Birthday(Month.AUGUST, 15), |
|||
new Birthday(Month.AUGUST, 17) |
|||
); |
|||
System.out.printf("There are %d candidates remaining.\n", choices.size()); |
|||
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer |
|||
Set<Month> uniqueMonths = choices.stream() |
|||
.collect(Collectors.groupingBy(Birthday::getDay)) |
|||
.values() |
|||
.stream() |
|||
.filter(g -> g.size() == 1) |
|||
.flatMap(Collection::stream) |
|||
.map(Birthday::getMonth) |
|||
.collect(Collectors.toSet()); |
|||
List<Birthday> f1List = choices.stream() |
|||
.filter(birthday -> !uniqueMonths.contains(birthday.month)) |
|||
.collect(Collectors.toList()); |
|||
System.out.printf("There are %d candidates remaining.\n", f1List.size()); |
|||
// Bernard now knows the answer, so the day must be unique within the remaining choices |
|||
List<Birthday> f2List = f1List.stream() |
|||
.collect(Collectors.groupingBy(Birthday::getDay)) |
|||
.values() |
|||
.stream() |
|||
.filter(g -> g.size() == 1) |
|||
.flatMap(Collection::stream) |
|||
.collect(Collectors.toList()); |
|||
System.out.printf("There are %d candidates remaining.\n", f2List.size()); |
|||
// Albert knows the answer too, so the month must be unique within the remaining choices |
|||
List<Birthday> f3List = f2List.stream() |
|||
.collect(Collectors.groupingBy(Birthday::getMonth)) |
|||
.values() |
|||
.stream() |
|||
.filter(g -> g.size() == 1) |
|||
.flatMap(Collection::stream) |
|||
.collect(Collectors.toList()); |
|||
System.out.printf("There are %d candidates remaining.\n", f3List.size()); |
|||
if (f3List.size() == 1) { |
|||
System.out.printf("Cheryl's birthday is %s\n", f3List.get(0)); |
|||
} else { |
|||
System.out.println("No unique choice found"); |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>There are 10 candidates remaining. |
|||
There are 5 candidates remaining. |
|||
There are 3 candidates remaining. |
|||
There are 1 candidates remaining. |
|||
Cheryl's birthday is JULY 16</pre> |
|||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
< |
<syntaxhighlight lang="javascript">(() => { |
||
'use strict'; |
'use strict'; |
||
// main :: IO () |
// main :: IO () |
||
const main = () => |
const main = () => { |
||
const |
|||
month = fst, |
|||
day = snd; |
|||
showLog( |
showLog( |
||
map(x => Array.from(x), ( |
map(x => Array.from(x), ( |
||
// The month with only one remaining day, |
// The month with only one remaining day, |
||
monthsWithSingleDays(true)( |
|||
// (A's month contains only one remaining day) |
|||
// (3 :: A "Then I also know") |
|||
uniquePairing(month)( |
|||
// among the days with unique months, |
// among the days with unique months, |
||
daysWithUniqueMonths(true)( |
|||
// (B's day is paired with only one remaining month) |
|||
// (2 :: B "I know now") |
|||
uniquePairing(day)( |
|||
// excluding months with unique days, |
// 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)( |
monthsWithUniqueDays(false)( |
||
// from the given month-day pairs: |
// from the given month-day pairs: |
||
// (0 :: Cheryl's list) |
|||
map(x => tupleFromList(words(strip(x))), |
map(x => tupleFromList(words(strip(x))), |
||
splitOn(/,\s+/, |
splitOn(/,\s+/, |
||
Line 199: | Line 2,270: | ||
)) |
)) |
||
); |
); |
||
}; |
|||
// monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)] |
// monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)] |
||
const monthsWithUniqueDays = blnInclude => xs => { |
const monthsWithUniqueDays = blnInclude => xs => { |
||
const |
const months = map(fst, uniquePairing(snd)(xs)); |
||
return filter( |
|||
dctDays = dictFromPairs(snd)(fst)(xs), |
|||
md => (blnInclude ? id : not)( |
|||
elem(fst(md), months) |
|||
k => 1 === length(dctDays[k]), |
|||
Object.keys(dctDays) |
|||
), |
), |
||
xs |
|||
); |
|||
k => (blnInclude ? id : not)( |
|||
0 < length(intersect(dctMonths[k], days)) |
|||
), |
|||
Object.keys(dctMonths) |
|||
); |
|||
return filter(tpl => elem(fst(tpl), months), xs); |
|||
}; |
}; |
||
// |
// uniquePairing :: ((a, a) -> a) -> |
||
// -> [(Month, Day)] -> [(Month, Day)] |
|||
const daysWithUniqueMonths = blnInclude => xs => { |
|||
const uniquePairing = f => xs => |
|||
bindPairs(xs, |
|||
md => { |
|||
const |
|||
dct = f(md), |
|||
matches = filter( |
|||
), |
k => 1 === length(dct[k]), |
||
Object.keys( |
Object.keys(dct) |
||
); |
); |
||
return filter(tpl => elem( |
return filter(tpl => elem(f(tpl), matches), xs); |
||
} |
} |
||
); |
|||
// |
// bindPairs :: [(Month, Day)] -> (Dict, Dict) -> [(Month, Day)] |
||
const |
const bindPairs = (xs, f) => f( |
||
Tuple( |
|||
dictFromPairs(fst)(snd)(xs), |
|||
dictFromPairs(snd)(fst)(xs) |
|||
) |
|||
k => (blnInclude ? id : not)( |
|||
); |
|||
1 == length(dctMonths[k]) |
|||
), |
|||
Object.keys(dctMonths) |
|||
); |
|||
return filter(tpl => elem(fst(tpl), months), xs); |
|||
}; |
|||
// dictFromPairs :: ((a, a) -> a) -> ((a, a) -> a) -> [(a, a)] -> Dict |
// dictFromPairs :: ((a, a) -> a) -> ((a, a) -> a) -> [(a, a)] -> Dict |
||
Line 342: | Line 2,402: | ||
// MAIN --- |
// MAIN --- |
||
return main(); |
return main(); |
||
})();</ |
})();</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>[["July","16"]]</pre> |
<pre>[["July","16"]]</pre> |
||
=={{header|jq}}== |
|||
'''Adapted from [[#Wren|Wren]]''' |
|||
{{works with|jq}} |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
A Birthday is represented by a JSON object {month, day} where {month:0} represents January. |
|||
=={{header|Perl 6}}== |
|||
<syntaxhighlight lang="jq">def count(stream; cond): |
|||
reduce stream as $i (0; if $i|cond then .+1 else . end); |
|||
def Months: [ |
|||
<lang perl6>my @dates = |
|||
"January", "February", "March", "April", "May", "June", |
|||
"July", "August", "September", "October", "November", "December" |
|||
]; |
|||
# tostring |
|||
def birthday: "\(Months[.month-1]) \(.day)"; |
|||
# Input: a Birthday |
|||
def monthUniqueIn($bds): |
|||
.month as $thisMonth |
|||
| count( $bds[]; .month == $thisMonth) == 1; |
|||
# Input: a Birthday |
|||
def dayUniqueIn($bds): |
|||
.day as $thisDay |
|||
| count( $bds[]; .day == $thisDay) == 1; |
|||
# Input: a Birthday |
|||
def monthWithUniqueDayIn($bds): |
|||
.month as $thisMonth |
|||
| any( $bds[]; $thisMonth == .month and dayUniqueIn($bds)); |
|||
def choices: [ |
|||
{month: 5, day: 15}, {month: 5, day: 16}, {month: 5, day: 19}, {month: 6, day: 17}, |
|||
{month: 6, day: 18}, {month: 7, day: 14}, {month: 7, day: 16}, {month: 8, day: 14}, |
|||
{month: 8, day: 15}, {month: 8, day: 17} |
|||
]; |
|||
# Albert knows the month but doesn't know the day, |
|||
# so the month can't be unique within the choices. |
|||
def filter1: |
|||
. as $in |
|||
| map(select( monthUniqueIn($in) | not)); |
|||
# Albert also knows that Bernard doesn't know the answer, |
|||
# so the month can't have a unique day. |
|||
def filter2: |
|||
. as $in |
|||
| map(select( monthWithUniqueDayIn($in) | not)); |
|||
# Bernard now knows the answer, |
|||
# so the day must be unique within the remaining choices. |
|||
def filter3: |
|||
. as $in |
|||
| map(select( dayUniqueIn($in) )); |
|||
# Albert now knows the answer too. |
|||
# So the month must be unique within the remaining choices. |
|||
def filter4: |
|||
. as $in |
|||
| map(select( monthUniqueIn($in) )); |
|||
def solve: |
|||
(choices | filter1 | filter2 | filter3 | filter4) as $bds |
|||
| if $bds|length == 1 |
|||
then "Cheryl's birthday is \($bds[0]|birthday)." |
|||
else "Whoops!" |
|||
end; |
|||
solve</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl's birthday is July 16. |
|||
</pre> |
|||
=={{header|Julia}}== |
|||
<syntaxhighlight lang="julia">const dates = [[15, "May"], [16, "May"], [19, "May"], [17, "June"], [18, "June"], |
|||
[14, "July"], [16, "July"], [14, "August"], [15, "August"], [17, "August"]] |
|||
uniqueday(parr) = filter(x -> count(y -> y[1] == x[1], parr) == 1, parr) |
|||
# At the start, they come to know that they have no unique day of month to identify. |
|||
const f1 = filter(m -> !(m[2] in [d[2] for d in uniqueday(dates)]), dates) |
|||
# After cutting months with unique dates, get months remaining that now have a unique date. |
|||
const f2 = uniqueday(f1) |
|||
# filter for those of the finally remaining months that have only one date left. |
|||
const bday = filter(x -> count(m -> m[2] == x[2], f2) == 1, f2)[] |
|||
println("Cheryl's birthday is $(bday[2]) $(bday[1]).") |
|||
</syntaxhighlight>{{out}} |
|||
<pre> |
|||
Cheryl's birthday is July 16. |
|||
</pre> |
|||
=={{header|Kotlin}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="scala">// Version 1.2.71 |
|||
val months = listOf( |
|||
"January", "February", "March", "April", "May", "June", |
|||
"July", "August", "September", "October", "November", "December" |
|||
) |
|||
class Birthday(val month: Int, val day: Int) { |
|||
public override fun toString() = "${months[month - 1]} $day" |
|||
public fun monthUniqueIn(bds: List<Birthday>): Boolean { |
|||
return bds.count { this.month == it.month } == 1 |
|||
} |
|||
public fun dayUniqueIn(bds: List<Birthday>): Boolean { |
|||
return bds.count { this.day == it.day } == 1 |
|||
} |
|||
public fun monthWithUniqueDayIn(bds: List<Birthday>): Boolean { |
|||
return bds.any { (this.month == it.month) && it.dayUniqueIn(bds) } |
|||
} |
|||
} |
|||
fun main(args: Array<String>) { |
|||
val choices = listOf( |
|||
Birthday(5, 15), Birthday(5, 16), Birthday(5, 19), Birthday(6, 17), |
|||
Birthday(6, 18), Birthday(7, 14), Birthday(7, 16), Birthday(8, 14), |
|||
Birthday(8, 15), Birthday(8, 17) |
|||
) |
|||
// Albert knows the month but doesn't know the day. |
|||
// So the month can't be unique within the choices. |
|||
var filtered = choices.filterNot { it.monthUniqueIn(choices) } |
|||
// Albert also knows that Bernard doesn't know the answer. |
|||
// So the month can't have a unique day. |
|||
filtered = filtered.filterNot { it.monthWithUniqueDayIn(filtered) } |
|||
// Bernard now knows the answer. |
|||
// So the day must be unique within the remaining choices. |
|||
filtered = filtered.filter { it.dayUniqueIn(filtered) } |
|||
// Albert now knows the answer too. |
|||
// So the month must be unique within the remaining choices. |
|||
filtered = filtered.filter { it.monthUniqueIn(filtered) } |
|||
if (filtered.size == 1) |
|||
println("Cheryl's birthday is ${filtered[0]}") |
|||
else |
|||
println("Something went wrong!") |
|||
}</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
Cheryl's birthday is July 16 |
|||
</pre> |
|||
=={{header|Lua}}== |
|||
<syntaxhighlight lang="lua">-- Cheryl's Birthday in Lua 6/15/2020 db |
|||
local function Date(mon,day) |
|||
return { mon=mon, day=day, valid=true } |
|||
end |
|||
local choices = { |
|||
Date("May", 15), Date("May", 16), Date("May", 19), |
|||
Date("June", 17), Date("June", 18), |
|||
Date("July", 14), Date("July", 16), |
|||
Date("August", 14), Date("August", 15), Date("August", 17) |
|||
} |
|||
local function apply(t, f) |
|||
for k, v in ipairs(t) do |
|||
f(k, v) |
|||
end |
|||
end |
|||
local function filter(t, f) |
|||
local result = {} |
|||
for k, v in ipairs(t) do |
|||
if f(k, v) then |
|||
result[#result+1] = v |
|||
end |
|||
end |
|||
return result |
|||
end |
|||
local function map(t, f) |
|||
local result = {} |
|||
for k, v in ipairs(t) do |
|||
result[#result+1] = f(k, v) |
|||
end |
|||
return result |
|||
end |
|||
local function count(t) return #t end |
|||
local function isvalid(k, v) return v.valid end |
|||
local function invalidate(k, v) v.valid = false end |
|||
local function remaining() return filter(choices, isvalid) end |
|||
local function listValidChoices() |
|||
print(" " .. table.concat(map(remaining(), function(k, v) return v.mon .. " " .. v.day end), ", ")) |
|||
print() |
|||
end |
|||
print("Cheryl offers these ten choices:") |
|||
listValidChoices() |
|||
print("1) Albert knows that Bernard also cannot yet know, so cannot be a month with a unique day, leaving:") |
|||
apply(remaining(), function(k, v) |
|||
if count(filter(choices, function(k2, v2) return v.day==v2.day end)) == 1 then |
|||
apply(filter(remaining(), function(k2, v2) return v.mon==v2.mon end), invalidate) |
|||
end |
|||
end) |
|||
listValidChoices() |
|||
print("2) After Albert's revelation, Bernard now knows, so day must be unique, leaving:") |
|||
apply(remaining(), function(k, v) |
|||
local subset = filter(remaining(), function(k2, v2) return v.day==v2.day end) |
|||
if count(subset) > 1 then apply(subset, invalidate) end |
|||
end) |
|||
listValidChoices() |
|||
print("3) After Bernard's revelation, Albert now knows, so month must be unique, leaving only:") |
|||
apply(remaining(), function(k, v) |
|||
local subset = filter(remaining(), function(k2, v2) return v.mon==v2.mon end) |
|||
if count(subset) > 1 then apply(subset, invalidate) end |
|||
end) |
|||
listValidChoices()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Cheryl offers these ten choices: |
|||
May 15, May 16, May 19, June 17, June 18, July 14, July 16, August 14, August 15, August 17 |
|||
1) Albert knows that Bernard also cannot yet know, so cannot be a month with a unique day, leaving: |
|||
July 14, July 16, August 14, August 15, August 17 |
|||
2) After Albert's revelation, Bernard now knows, so day must be unique, leaving: |
|||
July 16, August 15, August 17 |
|||
3) After Bernard's revelation, Albert now knows, so month must be unique, leaving only: |
|||
July 16</pre> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="mathematica">opts = Tuples[{{"May"}, {15, 16, 19}}]~Join~Tuples[{{"June"}, {17, 18}}]~Join~Tuples[{{"July"}, {14, 16}}]~Join~Tuples[{{"August"}, {14, 15, 17}}]; |
|||
monthsdelete = Select[GatherBy[opts, Last], Length /* EqualTo[1]][[All, 1, 1]]; |
|||
opts = DeleteCases[opts, {Alternatives @@ monthsdelete, _}] |
|||
removedates = Catenate@Select[GatherBy[opts, Last], Length /* GreaterThan[1]]; |
|||
opts = DeleteCases[opts, Alternatives @@ removedates] |
|||
Select[GatherBy[opts, First], Length /* EqualTo[1]]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{{"July", 14}, {"July", 16}, {"August", 14}, {"August", 15}, {"August", 17}} |
|||
{{"July", 16}, {"August", 15}, {"August", 17}} |
|||
{{{"July", 16}}}</pre> |
|||
=={{header|Nim}}== |
|||
<syntaxhighlight lang="nim">import tables |
|||
import sets |
|||
import strformat |
|||
type Date = tuple[month: string, day: int] |
|||
const Dates = [Date ("May", 15), ("May", 16), ("May", 19), ("June", 17), ("June", 18), |
|||
("July", 14), ("July", 16), ("August", 14), ("August", 15), ("August", 17)] |
|||
const |
|||
MonthTable: Table[int, HashSet[string]] = |
|||
static: |
|||
var t: Table[int, HashSet[string]] |
|||
for date in Dates: |
|||
t.mgetOrPut(date.day, initHashSet[string]()).incl(date.month) |
|||
t |
|||
DayTable: Table[string, HashSet[int]] = |
|||
static: |
|||
var t: Table[string, HashSet[int]] |
|||
for date in Dates: |
|||
t.mgetOrPut(date.month, initHashSet[int]()).incl(date.day) |
|||
t |
|||
var possibleMonths: HashSet[string] # Set of possible months. |
|||
var possibleDays: HashSet[int] # Set of possible days. |
|||
# Albert: I don't know when Cheryl's birthday is, ... |
|||
# => eliminate months with a single possible day. |
|||
for month, days in DayTable.pairs: |
|||
if days.len > 1: |
|||
possibleMonths.incl(month) |
|||
# ... but I know that Bernard does not know too. |
|||
# => eliminate months with one day present only in this month. |
|||
for month, days in DayTable.pairs: |
|||
for day in days: |
|||
if MonthTable[day].len == 1: |
|||
possibleMonths.excl(month) |
|||
echo fmt"After first Albert's sentence, possible months are {possibleMonths}." |
|||
# Bernard: At first I don't know when Cheryl's birthday is, ... |
|||
# => eliminate days with a single possible month. |
|||
for day, months in MonthTable.pairs: |
|||
if months.len > 1: |
|||
possibleDays.incl(day) |
|||
# ... but I know now. |
|||
# => eliminate days which are compatible with several months in "possibleMonths". |
|||
var impossibleDays: HashSet[int] # Days which are eliminated by this sentence. |
|||
for day in possibleDays: |
|||
if (MonthTable[day] * possibleMonths).len > 1: |
|||
impossibleDays.incl(day) |
|||
possibleDays.excl(impossibleDays) |
|||
echo fmt"After Bernard's sentence, possible days are {possibleDays}." |
|||
# Albert: Then I also know when Cheryl's birthday is. |
|||
# => eliminate months which are compatible with several days in "possibleDays". |
|||
var impossibleMonths: HashSet[string] # Months which are eliminated by this sentence. |
|||
for month in possibleMonths: |
|||
if (DayTable[month] * possibleDays).len > 1: |
|||
impossibleMonths.incl(month) |
|||
possibleMonths.excl(impossibleMonths) |
|||
doAssert possibleMonths.len == 1 |
|||
let month = possibleMonths.pop() |
|||
echo fmt"After second Albert's sentence, remaining month is {month}..." |
|||
possibleDays = possibleDays * DayTable[month] |
|||
doAssert possibleDays.len == 1 |
|||
let day = possibleDays.pop() |
|||
echo fmt"and thus remaining day is {day}." |
|||
echo "" |
|||
echo fmt"So birthday date is {month} {day}."</syntaxhighlight> |
|||
{{out}} |
|||
<pre>After first Albert's sentence, possible months are {"August", "July"}. |
|||
After Bernard's sentence, possible days are {16, 17, 15}. |
|||
After second Albert's sentence, remaining month is July... |
|||
and thus remaining day is 16. |
|||
So birthday date is July 16</pre> |
|||
=={{header|Perl}}== |
|||
<syntaxhighlight lang="perl">sub filter { |
|||
my($test,@dates) = @_; |
|||
my(%M,%D,@filtered); |
|||
# analysis of potential birthdays, keyed by month and by day |
|||
for my $date (@dates) { |
|||
my($mon,$day) = split '-', $date; |
|||
$M{$mon}{cnt}++; |
|||
$D{$day}{cnt}++; |
|||
push @{$M{$mon}{day}}, $day; |
|||
push @{$D{$day}{mon}}, $mon; |
|||
push @{$M{$mon}{bday}}, "$mon-$day"; |
|||
push @{$D{$day}{bday}}, "$mon-$day"; |
|||
} |
|||
# eliminates May/Jun dates based on 18th and 19th being singletons |
|||
if ($test eq 'singleton') { |
|||
my %skip; |
|||
for my $day (grep { $D{$_}{cnt} == 1 } keys %D) { $skip{ @{$D{$day}{mon}}[0] }++ } |
|||
for my $mon (grep { ! $skip{$_} } keys %M) { push @filtered, @{$M{$mon}{bday}} } |
|||
# eliminates Jul/Aug 14th because day count > 1 across months |
|||
} elsif ($test eq 'duplicate') { |
|||
for my $day (grep { $D{$_}{cnt} == 1 } keys %D) { push @filtered, @{$D{$day}{bday}} } |
|||
# eliminates Aug 15th/17th because day count > 1, within month |
|||
} elsif ($test eq 'multiple') { |
|||
for my $day (grep { $M{$_}{cnt} == 1 } keys %M) { push @filtered, @{$M{$day}{bday}} } |
|||
} |
|||
return @filtered; |
|||
} |
|||
# doesn't matter what order singleton/duplicate tests are run, but 'multiple' must be last; |
|||
my @dates = qw<5-15 5-16 5-19 6-17 6-18 7-14 7-16 8-14 8-15 8-17>; |
|||
@dates = filter($_, @dates) for qw<singleton duplicate multiple>; |
|||
my @months = qw<_ January February March April May June July August September October November December>; |
|||
my ($m, $d) = split '-', $dates[0]; |
|||
print "Cheryl's birthday is $months[$m] $d.\n";</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Cheryl's birthday is July 16.</pre> |
|||
=={{header|Phix}}== |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #000080;font-style:italic;">-- demo\rosetta\Cheryls_Birthday.exw</span> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">choices</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">15</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">16</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">19</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">18</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">16</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">15</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">}}</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">mwud</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #004600;">false</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- months with unique days</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">step</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">4</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">months</span><span style="color: #0000FF;">,</span><span style="color: #000000;">days</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">columnize</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">bool</span> <span style="color: #000000;">impossible</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">m</span><span style="color: #0000FF;">,</span><span style="color: #000000;">d</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">choices</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">switch</span> <span style="color: #000000;">step</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">mwud</span><span style="color: #0000FF;">[</span><span style="color: #000000;">m</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_eq</span><span style="color: #0000FF;">(</span><span style="color: #000000;">days</span><span style="color: #0000FF;">,</span><span style="color: #000000;">d</span><span style="color: #0000FF;">))=</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">impossible</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">mwud</span><span style="color: #0000FF;">[</span><span style="color: #000000;">m</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">impossible</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_eq</span><span style="color: #0000FF;">(</span><span style="color: #000000;">days</span><span style="color: #0000FF;">,</span><span style="color: #000000;">d</span><span style="color: #0000FF;">))!=</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">case</span> <span style="color: #000000;">4</span><span style="color: #0000FF;">:</span> <span style="color: #000000;">impossible</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_eq</span><span style="color: #0000FF;">(</span><span style="color: #000000;">months</span><span style="color: #0000FF;">,</span><span style="color: #000000;">m</span><span style="color: #0000FF;">))!=</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">switch</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">impossible</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">choices</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #000000;">choices</span> |
|||
<!--</syntaxhighlight>--> |
|||
Iterating backwards down the choices array simplifies element removal, or more accurately removes the need for "not increment i".<br> |
|||
Step 1&2 is months with unique days, step 3 is days with unique months, step 4 is unique months. |
|||
{{out}} |
|||
<pre> |
|||
{{7,16}} |
|||
</pre> |
|||
=== functional/filter === |
|||
(this can also be found in demo\rosetta\Cheryls_Birthday.exw) |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #008080;">enum</span> <span style="color: #000000;">MONTH</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">DAY</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">unique_month</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">si</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">months</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_eq</span><span style="color: #0000FF;">(</span><span style="color: #000000;">months</span><span style="color: #0000FF;">,</span><span style="color: #000000;">si</span><span style="color: #0000FF;">[</span><span style="color: #000000;">MONTH</span><span style="color: #0000FF;">]))=</span><span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">unique_day</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">si</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">days</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_eq</span><span style="color: #0000FF;">(</span><span style="color: #000000;">days</span><span style="color: #0000FF;">,</span><span style="color: #000000;">si</span><span style="color: #0000FF;">[</span><span style="color: #000000;">DAY</span><span style="color: #0000FF;">]))=</span><span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">month_without_unique_day</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">si</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">months_with_unique_day</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">si</span><span style="color: #0000FF;">[</span><span style="color: #000000;">MONTH</span><span style="color: #0000FF;">],</span><span style="color: #000000;">months_with_unique_day</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">choices</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">15</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">16</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">19</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">6</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">18</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">16</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">15</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">}}</span> |
|||
<span style="color: #000080;font-style:italic;">-- Albert knows the month but does not know the day. |
|||
-- So the month cannot be unique within the choices. |
|||
-- However this step would change nothing, hence omit it. |
|||
-- (obvs. non_unique_month() would be as above, but !=1) |
|||
--choices = filter(choices,non_unique_month,vslice(choices,MONTH)) |
|||
-- Albert also knows that Bernard doesn't know the answer. |
|||
-- So the month cannot have a unique day.</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">unique_days</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">unique_day</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">vslice</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">DAY</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">months_with_unique_day</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">unique</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">vslice</span><span style="color: #0000FF;">(</span><span style="color: #000000;">unique_days</span><span style="color: #0000FF;">,</span><span style="color: #000000;">MONTH</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #000000;">choices</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">month_without_unique_day</span><span style="color: #0000FF;">,</span><span style="color: #000000;">months_with_unique_day</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">-- Bernard now knows the answer. |
|||
-- So the day must be unique within the remaining choices.</span> |
|||
<span style="color: #000000;">choices</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">unique_day</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">vslice</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">DAY</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #000080;font-style:italic;">-- Albert now knows the answer too. |
|||
-- So the month must be unique within the remaining choices.</span> |
|||
<span style="color: #000000;">choices</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">unique_month</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">vslice</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">,</span><span style="color: #000000;">MONTH</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">choices</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Something went wrong!"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #004080;">timedate</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
<span style="color: #004080;">timedate</span> <span style="color: #000000;">td</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">td</span><span style="color: #0000FF;">[</span><span style="color: #004600;">DT_MONTH</span><span style="color: #0000FF;">],</span><span style="color: #000000;">td</span><span style="color: #0000FF;">[</span><span style="color: #004600;">DT_DAY</span><span style="color: #0000FF;">]}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">choices</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Cheryl's birthday is %s\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">format_timedate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">td</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Mmmm ddth"</span><span style="color: #0000FF;">)})</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl's birthday is July 16th |
|||
</pre> |
|||
=={{header|Python}}== |
|||
===Functional=== |
|||
{{Works with|Python|3}} |
|||
<syntaxhighlight lang="python">'''Cheryl's Birthday''' |
|||
from itertools import groupby |
|||
from re import split |
|||
# main :: IO () |
|||
def main(): |
|||
'''Derivation of the date.''' |
|||
month, day = 0, 1 |
|||
print( |
|||
# (3 :: A "Then I also know") |
|||
# (A's month contains only one remaining day) |
|||
uniquePairing(month)( |
|||
# (2 :: B "I know now") |
|||
# (B's day is paired with only one remaining month) |
|||
uniquePairing(day)( |
|||
# (1 :: A "I know that Bernard does not know") |
|||
# (A's month is not among those with unique days) |
|||
monthsWithUniqueDays(False)([ |
|||
# 0 :: Cheryl's list: |
|||
tuple(x.split()) for x in |
|||
split( |
|||
', ', |
|||
'May 15, May 16, May 19, ' + |
|||
'June 17, June 18, ' + |
|||
'July 14, July 16, ' + |
|||
'Aug 14, Aug 15, Aug 17' |
|||
) |
|||
]) |
|||
) |
|||
) |
|||
) |
|||
# ------------------- QUERY FUNCTIONS -------------------- |
|||
# monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)] |
|||
def monthsWithUniqueDays(blnInclude): |
|||
'''The subset of months with (or without) unique days. |
|||
''' |
|||
def go(xs): |
|||
month, day = 0, 1 |
|||
months = [fst(x) for x in uniquePairing(day)(xs)] |
|||
return [ |
|||
md for md in xs |
|||
if blnInclude or not (md[month] in months) |
|||
] |
|||
return go |
|||
# uniquePairing :: DatePart -> [(Month, Day)] -> [(Month, Day)] |
|||
def uniquePairing(i): |
|||
'''Subset of months (or days) with a unique intersection. |
|||
''' |
|||
def go(xs): |
|||
def inner(md): |
|||
dct = md[i] |
|||
uniques = [ |
|||
k for k in dct.keys() |
|||
if 1 == len(dct[k]) |
|||
] |
|||
return [tpl for tpl in xs if tpl[i] in uniques] |
|||
return inner |
|||
return ap(bindPairs)(go) |
|||
# bindPairs :: [(Month, Day)] -> |
|||
# ((Dict String [String], Dict String [String]) |
|||
# -> [(Month, Day)]) -> [(Month, Day)] |
|||
def bindPairs(xs): |
|||
'''List monad injection operator for lists |
|||
of (Month, Day) pairs. |
|||
''' |
|||
return lambda f: f( |
|||
( |
|||
dictFromPairs(xs), |
|||
dictFromPairs( |
|||
[(b, a) for (a, b) in xs] |
|||
) |
|||
) |
|||
) |
|||
# dictFromPairs :: [(Month, Day)] -> Dict Text [Text] |
|||
def dictFromPairs(xs): |
|||
'''A dictionary derived from a list of |
|||
month day pairs. |
|||
''' |
|||
return { |
|||
k: [snd(x) for x in m] for k, m in groupby( |
|||
sorted(xs, key=fst), key=fst |
|||
) |
|||
} |
|||
# ----------------------- GENERIC ------------------------ |
|||
# ap :: (a -> b -> c) -> (a -> b) -> a -> c |
|||
def ap(f): |
|||
'''Applicative instance for functions. |
|||
''' |
|||
def go(g): |
|||
def fxgx(x): |
|||
return f(x)( |
|||
g(x) |
|||
) |
|||
return fxgx |
|||
return go |
|||
# fst :: (a, b) -> a |
|||
def fst(tpl): |
|||
'''First component of a pair. |
|||
''' |
|||
return tpl[0] |
|||
# snd :: (a, b) -> b |
|||
def snd(tpl): |
|||
'''Second component of a pair. |
|||
''' |
|||
return tpl[1] |
|||
if __name__ == '__main__': |
|||
main()</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>[('July', '16')]</pre> |
|||
=={{header|R}}== |
|||
{{libheader|dplyr}} |
|||
<syntaxhighlight lang="r">options <- dplyr::tibble(mon = rep(c("May", "June", "July", "August"),times = c(3,2,2,3)), |
|||
day = c(15, 16, 19, 17, 18, 14, 16, 14, 15, 17)) |
|||
okMonths <- c() |
|||
# Albert's first clue - it is a month with no unique day |
|||
for (i in unique(options$mon)){ |
|||
if(all(options$day[options$mon == i] %in% options$day[options$mon != i])) {okMonths <- c(okMonths, i)} |
|||
} |
|||
okDays <- c() |
|||
# Bernard's clue - it is a day that only occurs once in the remaining dates |
|||
for (i in unique(options$day)){ |
|||
if(!all(options$mon[options$day == i] %in% options$mon[(options$mon %in% okMonths)])) {okDays <- c(okDays, i)} |
|||
} |
|||
remaining <- options[(options$mon %in% okMonths) & (options$day %in% okDays), ] |
|||
# Albert's second clue - must be a month with only one un-eliminated date |
|||
for(i in unique(remaining$mon)){ |
|||
if(sum(remaining$mon == i) == 1) {print(remaining[remaining$mon == i,])} |
|||
}</syntaxhighlight> |
|||
{{Out}} |
|||
<pre># A tibble: 1 x 2 |
|||
mon day |
|||
<chr> <dbl> |
|||
1 July 16</pre> |
|||
=={{header|Racket}}== |
|||
{{trans|Kotlin}} |
|||
<syntaxhighlight lang="racket">#lang racket |
|||
(define ((is x #:key [key identity]) y) (equal? (key x) (key y))) |
|||
(define albert first) |
|||
(define bernard second) |
|||
(define (((unique who) chs) date) (= 1 (count (is date #:key who) chs))) |
|||
(define (((unique-fix who-fix who) chs) date) |
|||
(ormap (conjoin (is date #:key who-fix) ((unique who) chs)) chs)) |
|||
(define-syntax-rule (solve <chs> [<act> <arg>] ...) |
|||
(let* ([chs <chs>] [chs (<act> (<arg> chs) chs)] ...) chs)) |
|||
(solve '((May 15) (May 16) (May 19) (June 17) (June 18) |
|||
(July 14) (July 16) (August 14) (August 15) (August 17)) |
|||
;; Albert knows the month but doesn't know the day. |
|||
;; So the month can't be unique within the choices. |
|||
[filter-not (unique albert)] |
|||
;; Albert also knows that Bernard doesn't know the answer. |
|||
;; So the month can't have a unique day. |
|||
[filter-not (unique-fix albert bernard)] |
|||
;; Bernard now knows the answer. |
|||
;; So the day must be unique within the remaining choices. |
|||
[filter (unique bernard)] |
|||
;; Albert now knows the answer too. |
|||
;; So the month must be unique within the remaining choices |
|||
[filter (unique albert)])</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
'((July 16)) |
|||
</pre> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
<syntaxhighlight lang="raku" line>my @dates = |
|||
{ :15day, :5month }, |
{ :15day, :5month }, |
||
{ :16day, :5month }, |
{ :16day, :5month }, |
||
Line 368: | Line 3,088: | ||
.first(*.value.elems == 1).value[0]; |
.first(*.value.elems == 1).value[0]; |
||
# convenience |
# convenience array |
||
my |
my @months = <'' January February March April May June July August September October November December>; |
||
August September October November December>.kv.pairup; |
|||
say "Cheryl's birthday is { %months{$birthday<month>} } {$birthday<day>}.";</lang> |
|||
say "Cheryl's birthday is { @months[$birthday<month>] } {$birthday<day>}.";</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Cheryl's birthday is July 16.</pre> |
<pre>Cheryl's birthday is July 16.</pre> |
||
=={{header|REXX}}== |
|||
<syntaxhighlight lang="rexx">/*REXX pgm finds Cheryl's birth date based on a person knowing the birth month, another */ |
|||
/*──────────────────────── person knowing the birth day, given a list of possible dates.*/ |
|||
$= 'May-15 May-16 May-19 June-17 June-18 July-14 July-16 August-14 August-15 August-17' |
|||
call delDays unique('day') |
|||
$= unique('day') |
|||
$= unique('month') |
|||
if words($)==1 then say "Cheryl's birthday is" translate($, , '-') |
|||
else say "error in the program's logic." |
|||
exit 0 |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
unique: arg u 2, dups; #= words($); $$= $ |
|||
do j=# to 2 by -1 |
|||
if u=='D' then parse value word($, j) with '-' x |
|||
else parse value word($, j) with x '-' |
|||
do k=1 for j-1 |
|||
if u=='D' then parse value word($, k) with '-' y |
|||
else parse value word($, k) with y '-' |
|||
if x==y then dups= dups k j |
|||
end /*k*/ |
|||
end /*j*/ |
|||
do d=# for # by -1 |
|||
do p=1 for words(dups) until ?==d; ?= word(dups,p) |
|||
if ?==d then $$= delword($$, ?, 1) |
|||
end /*d*/ |
|||
end /*d*/ |
|||
if words($$)==0 then return $ |
|||
else return $$ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
delDays: parse arg days; #= words(days) |
|||
do j=# for # by -1; parse value word(days, j) with x '-'; ##= words($) |
|||
do k=## for ## by -1; parse value word($, k) with y '-' |
|||
if x\==y then iterate; $= delword($, k, 1) |
|||
end /*k*/ |
|||
end /*j*/ |
|||
return $</syntaxhighlight> |
|||
{{out|output|text= when using the internal default input:}} |
|||
<pre> |
|||
Cheryl's birthday is July 16 |
|||
</pre> |
|||
=={{header|Ruby}}== |
|||
{{trans|C#}} |
|||
<syntaxhighlight lang="ruby">dates = [ |
|||
["May", 15], |
|||
["May", 16], |
|||
["May", 19], |
|||
["June", 17], |
|||
["June", 18], |
|||
["July", 14], |
|||
["July", 16], |
|||
["August", 14], |
|||
["August", 15], |
|||
["August", 17], |
|||
] |
|||
print dates.length, " remaining\n" |
|||
# the month cannot have a unique day |
|||
uniqueMonths = dates.group_by { |m,d| d } |
|||
.select { |k,v| v.size == 1 } |
|||
.map { |k,v| v.flatten } |
|||
.map { |m,d| m } |
|||
dates.delete_if { |m,d| uniqueMonths.include? m } |
|||
print dates.length, " remaining\n" |
|||
# the day must be unique |
|||
dates = dates .group_by { |m,d| d } |
|||
.select { |k,v| v.size == 1 } |
|||
.map { |k,v| v.flatten } |
|||
print dates.length, " remaining\n" |
|||
# the month must now be unique |
|||
dates = dates .group_by { |m,d| m } |
|||
.select { |k,v| v.size == 1 } |
|||
.map { |k,v| v } |
|||
.flatten |
|||
print dates</syntaxhighlight> |
|||
{{out}} |
|||
<pre>10 remaining |
|||
5 remaining |
|||
3 remaining |
|||
["July", 16]</pre> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight lang="rust"> |
|||
// This version is based on the Go version on Rosettacode |
|||
#[derive(PartialEq, Debug, Copy, Clone)] |
|||
enum Month { |
|||
May, |
|||
June, |
|||
July, |
|||
August, |
|||
} |
|||
#[derive(PartialEq, Debug, Copy, Clone)] |
|||
struct Birthday { |
|||
month: Month, |
|||
day: u8, |
|||
} |
|||
impl Birthday { |
|||
fn month_unique_in(&self, birthdays: &[Birthday]) -> bool { |
|||
birthdays |
|||
.iter() |
|||
.filter(|birthday| birthday.month == self.month) |
|||
.count() |
|||
== 1 |
|||
} |
|||
fn day_unique_in(&self, birthdays: &[Birthday]) -> bool { |
|||
birthdays |
|||
.iter() |
|||
.filter(|birthday| birthday.day == self.day) |
|||
.count() |
|||
== 1 |
|||
} |
|||
fn month_with_unique_day_in(&self, birthdays: &[Birthday]) -> bool { |
|||
birthdays |
|||
.iter() |
|||
.any(|birthday| self.month == birthday.month && birthday.day_unique_in(birthdays)) |
|||
} |
|||
} |
|||
fn solution() -> Option<Birthday> { |
|||
let mut choices: Vec<Birthday> = vec![ |
|||
Birthday { |
|||
month: Month::May, |
|||
day: 15, |
|||
}, |
|||
Birthday { |
|||
month: Month::May, |
|||
day: 16, |
|||
}, |
|||
Birthday { |
|||
month: Month::May, |
|||
day: 19, |
|||
}, |
|||
Birthday { |
|||
month: Month::June, |
|||
day: 17, |
|||
}, |
|||
Birthday { |
|||
month: Month::June, |
|||
day: 18, |
|||
}, |
|||
Birthday { |
|||
month: Month::July, |
|||
day: 14, |
|||
}, |
|||
Birthday { |
|||
month: Month::July, |
|||
day: 16, |
|||
}, |
|||
Birthday { |
|||
month: Month::August, |
|||
day: 14, |
|||
}, |
|||
Birthday { |
|||
month: Month::August, |
|||
day: 15, |
|||
}, |
|||
Birthday { |
|||
month: Month::August, |
|||
day: 17, |
|||
}, |
|||
]; |
|||
// Albert knows the month but doesn't know the day. |
|||
// So the month can't be unique within the choices. |
|||
let choices_copy = choices.clone(); |
|||
choices.retain(|birthday| !(&birthday.month_unique_in(&choices_copy))); |
|||
// Albert also knows that Bernard doesn't know the answer. |
|||
// So the month can't have a unique day. |
|||
let choices_copy = choices.clone(); |
|||
choices.retain(|birthday| !(birthday.month_with_unique_day_in(&choices_copy))); |
|||
// Bernard now knows the answer. |
|||
// So the day must be unique within the remaining choices. |
|||
let choices_copy = choices.clone(); |
|||
choices.retain(|birthday| birthday.day_unique_in(&choices_copy)); |
|||
// Albert now knows the answer too. |
|||
// So the month must be unique within the remaining choices. |
|||
let choices_copy = choices.clone(); |
|||
choices.retain(|birthday| birthday.month_unique_in(&choices_copy)); |
|||
if choices.len() == 1 { |
|||
Some(choices[0]) |
|||
} else { |
|||
None |
|||
} |
|||
} |
|||
fn main() { |
|||
match solution() { |
|||
Some(solution) => println!("Cheryl's birthday is {:?}", solution), |
|||
None => panic!("Didn't work!"), |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl's birthday is Birthday { month: July, day: 16 } |
|||
</pre> |
|||
=={{header|Scala}}== |
|||
==={{trans|D}}=== |
|||
<syntaxhighlight lang="scala">import java.time.format.DateTimeFormatter |
|||
import java.time.{LocalDate, Month} |
|||
object Cheryl { |
|||
def main(args: Array[String]): Unit = { |
|||
val choices = List( |
|||
LocalDate.of(2019, Month.MAY, 15), |
|||
LocalDate.of(2019, Month.MAY, 16), |
|||
LocalDate.of(2019, Month.MAY, 19), |
|||
LocalDate.of(2019, Month.JUNE, 17), |
|||
LocalDate.of(2019, Month.JUNE, 18), |
|||
LocalDate.of(2019, Month.JULY, 14), |
|||
LocalDate.of(2019, Month.JULY, 16), |
|||
LocalDate.of(2019, Month.AUGUST, 14), |
|||
LocalDate.of(2019, Month.AUGUST, 15), |
|||
LocalDate.of(2019, Month.AUGUST, 17) |
|||
) |
|||
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer |
|||
val uniqueMonths = choices.groupBy(_.getDayOfMonth) |
|||
.filter(a => a._2.length == 1) |
|||
.flatMap(a => a._2) |
|||
.map(a => a.getMonth) |
|||
val filter1 = choices.filterNot(a => uniqueMonths.exists(b => a.getMonth == b)) |
|||
// Bernard now knows the answer, so the day must be unique within the remaining choices |
|||
val uniqueDays = filter1.groupBy(_.getDayOfMonth) |
|||
.filter(a => a._2.length == 1) |
|||
.flatMap(a => a._2) |
|||
.map(a => a.getDayOfMonth) |
|||
val filter2 = filter1.filter(a => uniqueDays.exists(b => a.getDayOfMonth == b)) |
|||
// Albert knows the answer too, so the month must be unique within the remaining choices |
|||
val birthDay = filter2.groupBy(_.getMonth) |
|||
.filter(a => a._2.length == 1) |
|||
.flatMap(a => a._2) |
|||
.head |
|||
// print the result |
|||
printf(birthDay.format(DateTimeFormatter.ofPattern("MMMM dd"))) |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>July 16</pre> |
|||
===Scala-ish approach=== |
|||
{{Out}}See it yourself by running in your browser either by [https://scalafiddle.io/sf/AiS6u7B/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/hoBvwq5fSkSRk0vnr6NKmA Scastie (remote JVM)]. |
|||
{{libheader|Scala Math Puzzle}} |
|||
{{libheader|Scala Scala-ish}} |
|||
{{libheader|Scala Idiomatic}} |
|||
{{libheader|Scala Type parameters}} |
|||
{{libheader|ScalaFiddle qualified}} |
|||
{{libheader|Scastie qualified}} |
|||
{{works with|Scala|2.13}} |
|||
<syntaxhighlight lang="scala">object Cheryl_sBirthday extends App { |
|||
private val possiblerDates = Set( |
|||
Date("May", 15), Date("May", 16), Date("May", 19), |
|||
Date("June", 17), Date("June", 18), |
|||
Date("July", 14), Date("July", 16), |
|||
Date("August", 14), Date("August", 15), Date("August", 17) |
|||
) |
|||
private def clou3: Date = { |
|||
// Find the dates with ONE unique once and only occurrence of the day of the month. |
|||
def onceDates[K](toBeExcluded: Set[Date], selector: Date => K): Seq[Date] = |
|||
toBeExcluded.groupBy(selector).filter { case (_, multiSet) => multiSet.size == 1 }.values.flatten.toSeq |
|||
// 1) Albert tells us that Bernard doesn't know the answer, |
|||
// so we know the answer must be in months that does NOT have a same day of month. |
|||
val uniqueMonths = onceDates(possiblerDates, (date: Date) => date.dayOfMonth).map(_.month) |
|||
// Remove the dates with those months. The dates remain which has NOT those months. |
|||
val clou1 = possiblerDates.filterNot(p => uniqueMonths.contains(p.month)) |
|||
// 2) Since Bernard now knows the answer, that tells us that the day MUST be unique among the remaining birthdays. |
|||
val uniqueDays = onceDates(clou1, (date: Date) => date.dayOfMonth).map(_.dayOfMonth) |
|||
// 3) Since Albert now knows the answer, that tells us the answer has to be unique by month. |
|||
// First, as the first parameter, intersect clou1 (Albert) with uniqueDays (Bernard) |
|||
onceDates(clou1.filter(date => uniqueDays.contains(date.dayOfMonth)), (date: Date) => date.month).head |
|||
} |
|||
case class Date(month: String, dayOfMonth: Int) { |
|||
override def toString: String = s"${"🎂 " * 3}$dayOfMonth $month${" 🎂" * 3}" |
|||
} |
|||
println(clou3) |
|||
}</syntaxhighlight> |
|||
=={{header|Sidef}}== |
|||
{{trans|Raku}} |
|||
<syntaxhighlight lang="ruby">func f(day, month) { |
|||
Date.parse("#{day} #{month}", "%d %B") |
|||
} |
|||
var dates = [ |
|||
f(15, "May"), |
|||
f(16, "May"), |
|||
f(19, "May"), |
|||
f(17, "June"), |
|||
f(18, "June"), |
|||
f(14, "July"), |
|||
f(16, "July"), |
|||
f(14, "August"), |
|||
f(15, "August"), |
|||
f(17, "August") |
|||
] |
|||
var filtered = dates.grep { |
|||
dates.grep { |
|||
dates.map{ .day }.count(.day) == 1 |
|||
}.map{ .month }.count(.month) != 1 |
|||
} |
|||
var birthday = filtered.grep { |
|||
filtered.map{ .day }.count(.day) == 1 |
|||
}.group_by{ .month }.values.first_by { .len == 1 }[0] |
|||
say "Cheryl's birthday is #{birthday.fullmonth} #{birthday.day}."</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl's birthday is July 16. |
|||
</pre> |
|||
=={{header|Swift}}== |
|||
{{trans|Kotlin}} |
|||
<syntaxhighlight lang="swift">struct MonthDay: CustomStringConvertible { |
|||
static let months = [ |
|||
"January", "February", "March", "April", "May", "June", |
|||
"July", "August", "September", "October", "November", "December" |
|||
] |
|||
var month: Int |
|||
var day: Int |
|||
var description: String { "\(MonthDay.months[month - 1]) \(day)" } |
|||
private func isUniqueIn(months: [MonthDay], by prop: KeyPath<MonthDay, Int>) -> Bool { |
|||
return months.lazy.filter({ $0[keyPath: prop] == self[keyPath: prop] }).count == 1 |
|||
} |
|||
func monthIsUniqueIn(months: [MonthDay]) -> Bool { |
|||
return isUniqueIn(months: months, by: \.month) |
|||
} |
|||
func dayIsUniqueIn(months: [MonthDay]) -> Bool { |
|||
return isUniqueIn(months: months, by: \.day) |
|||
} |
|||
func monthWithUniqueDayIn(months: [MonthDay]) -> Bool { |
|||
return months.firstIndex(where: { $0.month == month && $0.dayIsUniqueIn(months: months) }) != nil |
|||
} |
|||
} |
|||
let choices = [ |
|||
MonthDay(month: 5, day: 15), |
|||
MonthDay(month: 5, day: 16), |
|||
MonthDay(month: 5, day: 19), |
|||
MonthDay(month: 6, day: 17), |
|||
MonthDay(month: 6, day: 18), |
|||
MonthDay(month: 7, day: 14), |
|||
MonthDay(month: 7, day: 16), |
|||
MonthDay(month: 8, day: 14), |
|||
MonthDay(month: 8, day: 15), |
|||
MonthDay(month: 8, day: 17) |
|||
] |
|||
// Albert knows the month, but not the day, so he doesn't have a gimmie month |
|||
let albertKnows = choices.filter({ !$0.monthIsUniqueIn(months: choices) }) |
|||
// Albert also knows that Bernard doesn't know, so it can't be a gimmie day |
|||
let bernardKnows = albertKnows.filter({ !$0.monthWithUniqueDayIn(months: albertKnows) }) |
|||
// Bernard now knows the birthday, so it must be a unique day within the remaining choices |
|||
let bernardKnowsMore = bernardKnows.filter({ $0.dayIsUniqueIn(months: bernardKnows) }) |
|||
// Albert knows the birthday now, so it must be a unique month within the remaining choices |
|||
guard let birthday = bernardKnowsMore.filter({ $0.monthIsUniqueIn(months: bernardKnowsMore) }).first else { |
|||
fatalError() |
|||
} |
|||
print("Cheryl's birthday is \(birthday)")</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Cheryl's birthday is July 16</pre> |
|||
=={{header|uBasic/4tH}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="ubasic/4th">Dim @d(30) |
|||
Dim @m(13) |
|||
Push 5,15,1, 5,16,1, 5,19,1, 6,17,1 ,6,18,1, 7,14,1, 7,16,1, 8,14,1, 8,15,1, 8,17,1 |
|||
For x = 29 To 0 Step -1 : @d(x) = Pop() : Next |
|||
Push "ERR", "Jan", "Feb", "Mar", "Apr", "May" |
|||
Push "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
|||
For x = 12 To 0 Step -1 : @m(x) = Pop() : Next |
|||
Proc _printRemaining ' the month cannot have a unique day |
|||
Proc _firstPass |
|||
Proc _printRemaining ' the day must now be unique |
|||
Proc _secondPass |
|||
Proc _printRemaining ' the month must now be unique |
|||
Proc _thirdPass |
|||
Proc _printAnswer |
|||
End |
|||
_printRemaining |
|||
Local (2) |
|||
b@ = 0 |
|||
For a@ = 0 To 29 Step 3 |
|||
If @d(a@+2) Then b@ = b@ + 1 |
|||
Next |
|||
Print b@; " remaining." |
|||
Return |
|||
_printAnswer |
|||
Local (1) |
|||
For a@ = 0 To 29 Step 3 |
|||
If @d(a@+2) Then Print Show (@m(@d(a@))); ", "; @d(a@+1) |
|||
Next |
|||
Return |
|||
_firstPass ' the month cannot have a unique day |
|||
Local (3) |
|||
For a@ = 0 To 29 Step 3 |
|||
c@ = 0 |
|||
For b@ = 0 To 29 Step 3 |
|||
If @d(b@+1) = @d(a@+1) Then c@ = c@ + 1 |
|||
Next |
|||
If c@ = 1 Then |
|||
For b@ = 0 To 29 Step 3 |
|||
If @d(b@+2) = 0 Then |
|||
Continue |
|||
EndIf |
|||
If @d(b@) = @d(a@) Then |
|||
@d(b@+2) = 0 |
|||
EndIf |
|||
Next |
|||
EndIf |
|||
Next |
|||
Return |
|||
_secondPass ' the day must now be unique |
|||
Local (3) |
|||
For a@ = 0 To 29 Step 3 |
|||
If @d(a@+2) = 0 Then Continue |
|||
c@ = 0 |
|||
For b@ = 0 To 29 Step 3 |
|||
If @d(b@+2) = 0 Then Continue |
|||
If @d(b@+1) = @d(a@+1) Then c@ = c@ + 1 |
|||
Next |
|||
If c@ > 1 Then |
|||
For b@ = 0 To 29 Step 3 |
|||
If @d(b@+2) = 0 Then |
|||
Continue |
|||
EndIf |
|||
If @d(b@+1) = @d(a@+1) Then |
|||
@d(b@+2) = 0 |
|||
EndIf |
|||
Next |
|||
EndIf |
|||
Next |
|||
Return |
|||
_thirdPass ' the month must now be unique |
|||
Local (3) |
|||
For a@ = 0 To 29 Step 3 |
|||
If @d(a@+2) = 0 Then Continue |
|||
c@ = 0 |
|||
For b@ = 0 To 29 Step 3 |
|||
If @d(b@+2) = 0 Then Continue |
|||
If @d(b@) = @d(a@) Then c@ = c@ + 1 |
|||
Next |
|||
If c@ > 1 Then |
|||
For b@ = 0 To 29 Step 3 |
|||
If @d(b@+2) = 0 Then |
|||
Continue |
|||
EndIf |
|||
If @d(b@) = @d(a@) Then |
|||
@d(b@+2) = 0 |
|||
EndIf |
|||
Next |
|||
EndIf |
|||
Next |
|||
Return</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>10 remaining. |
|||
5 remaining. |
|||
3 remaining. |
|||
Jul, 16 |
|||
0 OK, 0:657</pre> |
|||
=={{header|VBA}}== |
|||
<syntaxhighlight lang="vb">Private Sub exclude_unique_days(w As Collection) |
|||
Dim number_of_dates(31) As Integer |
|||
Dim months_to_exclude As New Collection |
|||
For Each v In w |
|||
number_of_dates(v(1)) = number_of_dates(v(1)) + 1 |
|||
Next v |
|||
For i = w.Count To 1 Step -1 |
|||
If number_of_dates(w(i)(1)) = 1 Then |
|||
months_to_exclude.Add w(i)(0) |
|||
w.Remove i |
|||
End If |
|||
Next i |
|||
For Each m In months_to_exclude |
|||
exclude_month w, m |
|||
Next m |
|||
End Sub |
|||
Private Sub exclude_month(x As Collection, v As Variant) |
|||
For i = x.Count To 1 Step -1 |
|||
If x(i)(0) = v Then x.Remove i |
|||
Next i |
|||
End Sub |
|||
Private Sub exclude_non_unique_days(w As Collection) |
|||
Dim number_of_dates(31) As Integer |
|||
For Each v In w |
|||
number_of_dates(v(1)) = number_of_dates(v(1)) + 1 |
|||
Next v |
|||
For i = w.Count To 1 Step -1 |
|||
If number_of_dates(w(i)(1)) > 1 Then |
|||
w.Remove i |
|||
End If |
|||
Next i |
|||
End Sub |
|||
Private Sub exclude_non_unique_months(w As Collection) |
|||
Dim months As New Collection |
|||
For Each v In w |
|||
On Error GoTo 1 |
|||
months.Add v(0), v(0) |
|||
Next v |
|||
1: |
|||
For i = w.Count To 1 Step -1 |
|||
If w(i)(0) = v(0) Then |
|||
w.Remove i |
|||
End If |
|||
Next i |
|||
End Sub |
|||
Public Sub cherylsbirthday() |
|||
Dim v As New Collection |
|||
s = "May 15, May 16, May 19, June 17, June 18, July 14, July 16, August 14, August 15, August 17" |
|||
t = Split(s, ",") |
|||
For Each u In t |
|||
v.Add Split(Trim(u), " ") |
|||
Next u |
|||
'1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. |
|||
exclude_unique_days v |
|||
'2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. |
|||
exclude_non_unique_days v |
|||
'3) Albert: Then I also know when Cheryl's birthday is. |
|||
exclude_non_unique_months v |
|||
Debug.Print v(1)(0); " "; v(1)(1) |
|||
End Sub</syntaxhighlight>{{out}} |
|||
<pre>July 16</pre> |
|||
=={{header|Visual Basic .NET}}== |
|||
{{trans|C#}} |
|||
<syntaxhighlight lang="vbnet">Module Module1 |
|||
Structure MonDay |
|||
Dim month As String |
|||
Dim day As Integer |
|||
Sub New(m As String, d As Integer) |
|||
month = m |
|||
day = d |
|||
End Sub |
|||
Public Overrides Function ToString() As String |
|||
Return String.Format("({0}, {1})", month, day) |
|||
End Function |
|||
End Structure |
|||
Sub Main() |
|||
Dim dates = New HashSet(Of MonDay) From { |
|||
New MonDay("May", 15), |
|||
New MonDay("May", 16), |
|||
New MonDay("May", 19), |
|||
New MonDay("June", 17), |
|||
New MonDay("June", 18), |
|||
New MonDay("July", 14), |
|||
New MonDay("July", 16), |
|||
New MonDay("August", 14), |
|||
New MonDay("August", 15), |
|||
New MonDay("August", 17) |
|||
} |
|||
Console.WriteLine("{0} remaining.", dates.Count) |
|||
' The month cannot have a unique day. |
|||
Dim monthsWithUniqueDays = dates.GroupBy(Function(d) d.day).Where(Function(g) g.Count() = 1).Select(Function(g) g.First().month).ToHashSet() |
|||
dates.RemoveWhere(Function(d) monthsWithUniqueDays.Contains(d.month)) |
|||
Console.WriteLine("{0} remaining.", dates.Count) |
|||
' The day must now be unique. |
|||
dates.IntersectWith(dates.GroupBy(Function(d) d.day).Where(Function(g) g.Count() = 1).Select(Function(g) g.First())) |
|||
Console.WriteLine("{0} remaining.", dates.Count) |
|||
' The month must now be unique. |
|||
dates.IntersectWith(dates.GroupBy(Function(d) d.month).Where(Function(g) g.Count() = 1).Select(Function(g) g.First())) |
|||
Console.WriteLine(dates.Single()) |
|||
End Sub |
|||
End Module</syntaxhighlight> |
|||
{{out}} |
|||
<pre>10 remaining. |
|||
5 remaining. |
|||
3 remaining. |
|||
(July, 16)</pre> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="v (vlang)">import time |
|||
struct Birthday { |
|||
month int |
|||
day int |
|||
} |
|||
fn (b Birthday) str() string { |
|||
return "${time.long_months[b.month-1]} $b.day" |
|||
} |
|||
fn (b Birthday) month_uniquie_in(bds []Birthday) bool { |
|||
mut count := 0 |
|||
for bd in bds { |
|||
if bd.month == b.month { |
|||
count++ |
|||
} |
|||
} |
|||
if count == 1 { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
fn (b Birthday) day_unique_in(bds []Birthday) bool { |
|||
mut count := 0 |
|||
for bd in bds { |
|||
if bd.day == b.day { |
|||
count++ |
|||
} |
|||
} |
|||
if count == 1 { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
fn (b Birthday) month_with_unique_day_in(bds []Birthday) bool { |
|||
for bd in bds { |
|||
if bd.month == b.month && bd.day_unique_in(bds) { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
fn main() { |
|||
choices := [ |
|||
Birthday{5, 15}, Birthday{5, 16}, Birthday{5, 19}, Birthday{6, 17}, Birthday{6, 18}, |
|||
Birthday{7, 14}, Birthday{7, 16}, Birthday{8, 14}, Birthday{8, 15}, Birthday{8, 17}, |
|||
] |
|||
// Albert knows the month but doesn't know the day. |
|||
// So the month can't be unique within the choices. |
|||
mut filtered := []Birthday{} |
|||
for bd in choices { |
|||
if !bd.month_uniquie_in(choices) { |
|||
filtered << bd |
|||
} |
|||
} |
|||
// Albert also knows that Bernard doesn't know the answer. |
|||
// So the month can't have a unique day. |
|||
mut filtered2 := []Birthday{} |
|||
for bd in filtered { |
|||
if !bd.month_with_unique_day_in(filtered) { |
|||
filtered2 << bd |
|||
} |
|||
} |
|||
// Bernard now knows the answer. |
|||
// So the day must be unique within the remaining choices. |
|||
mut filtered3 := []Birthday{} |
|||
for bd in filtered2 { |
|||
if bd.day_unique_in(filtered2) { |
|||
filtered3 << bd |
|||
} |
|||
} |
|||
// Albert now knows the answer too. |
|||
// So the month must be unique within the remaining choices. |
|||
mut filtered4 := []Birthday{} |
|||
for bd in filtered3 { |
|||
if bd.month_uniquie_in(filtered3) { |
|||
filtered4 << bd |
|||
} |
|||
} |
|||
if filtered4.len == 1 { |
|||
println("Cheryl's Birthday is ${filtered4[0]}") |
|||
} else { |
|||
println("Something went wrong!") |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl's birthday is July 16 |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Kotlin}} |
|||
<syntaxhighlight lang="wren">var Months = [ |
|||
"January", "February", "March", "April", "May", "June", |
|||
"July", "August", "September", "October", "November", "December" |
|||
] |
|||
class Birthday { |
|||
construct new(month, day) { |
|||
_month = month |
|||
_day = day |
|||
} |
|||
month { _month } |
|||
day { _day } |
|||
toString { "%(Months[_month-1]) %(day)" } |
|||
monthUniqueIn(bds) { bds.count { |bd| _month == bd.month } == 1 } |
|||
dayUniqueIn(bds) { bds.count { |bd| _day == bd.day } == 1 } |
|||
monthWithUniqueDayIn(bds) { bds.any { |bd| (_month == bd.month) && bd.dayUniqueIn(bds) } } |
|||
} |
|||
var choices = [ |
|||
Birthday.new(5, 15), Birthday.new(5, 16), Birthday.new(5, 19), Birthday.new(6, 17), |
|||
Birthday.new(6, 18), Birthday.new(7, 14), Birthday.new(7, 16), Birthday.new(8, 14), |
|||
Birthday.new(8, 15), Birthday.new(8, 17) |
|||
] |
|||
// Albert knows the month but doesn't know the day. |
|||
// So the month can't be unique within the choices. |
|||
var filtered = choices.where { |bd| !bd.monthUniqueIn(choices) }.toList |
|||
// Albert also knows that Bernard doesn't know the answer. |
|||
// So the month can't have a unique day. |
|||
filtered = filtered.where { |bd| !bd.monthWithUniqueDayIn(filtered) }.toList |
|||
// Bernard now knows the answer. |
|||
// So the day must be unique within the remaining choices. |
|||
filtered = filtered.where { |bd| bd.dayUniqueIn(filtered) }.toList |
|||
// Albert now knows the answer too. |
|||
// So the month must be unique within the remaining choices. |
|||
filtered = filtered.where { |bd| bd.monthUniqueIn(filtered) }.toList |
|||
if (filtered.count == 1) { |
|||
System.print("Cheryl's birthday is %(filtered[0])") |
|||
} else { |
|||
System.print("Something went wrong!") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Cheryl's birthday is July 16 |
|||
</pre> |
|||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
<syntaxhighlight lang="zkl">dates:=T(T("May", 15), T("May", 16), T("May", 19), |
|||
<lang zkl>var D=Time.Date, |
|||
T("June", 17), T("June", 18), |
|||
T("July", 14), T("July", 16), |
|||
T("August",14), T("August",15), T("August",17) ); |
|||
mDs:=dates.pump(Dictionary().appendKV); // "June":(15,16,19), ... |
|||
T(D.August,14), T(D.August,15), T(D.August,17) ), |
|||
dMs:=dates.pump(Dictionary().appendKV,"reverse"); // 15:"May", 16:"May", 19:"May", ... |
|||
// remove unique days (18,19) --> "July":(14,16),"August":(14,15,17) |
|||
dMs.values.apply2('wrap(ms){ if(ms.len()==1) mDs.del(ms[0]) }); |
|||
// find intersection of above days --> (14) |
|||
dayCnt:=bDays.pump(Dictionary().incV,T("get",1)); // 15:2, 14:2, 19:1, 18:1 .. |
|||
fcn intersection(l1,l2){ l1.pump(List,l2.holds,'==(True),Void.Filter) } |
|||
udays :=dayCnt.keys.filter('wrap(d){ dayCnt[d]==1 }).apply("toInt"); # (19,18) |
|||
badDs:=mDs.values.reduce(intersection); |
|||
days :=pDates.keys.apply2('wrap(m) // remove months that have a unique day |
|||
{ if(interset(pDates[m],udays)) pDates.del(m) }); // 7:(14,16), 8:(14,15,17) |
|||
days :=pDates.values.reduce(intersection); // (16), len>1 --> ambiguous result |
|||
mons :=pDates.filter(fcn([(m,ds)],d,ms){ // find months that hold day |
|||
ms.holds(m) and ds.holds(d) }.fp1(days[0],pDates.keys)); # ((7,(14,16))) |
|||
if(days.len()>1 or mons.len()>1) throw(Exception.BadDay); // can't reduce |
|||
// --> July:(16),August:(15,17) --> ( ("July",(16)) ) |
|||
println("Cheryl's birthday is ",D.monthNames[mons[0][0]]," ",days[0]); |
|||
theDay:=mDs.filter('wrap([(m,ds)]){ ds.removeEach(badDs).len()==1 }); |
|||
// print birthday such that muliples are shown, if any |
|||
fcn interset(l1,l2){ foreach i in (l1){ if(l2.holds(i)) return(True) } False } |
|||
println("Cheryl's birthday is ",theDay.flatten().flatten().concat(" "));</syntaxhighlight> |
|||
fcn intersection(l1,l2){ l1.pump(List,l2.holds,'==(False),Void.Filter) }</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
Latest revision as of 11:37, 18 May 2024
You are encouraged to solve this task according to the task description, using any language you may know.
Albert and Bernard just became friends with Cheryl, and they want to know when her birthday is.
Cheryl gave them a list of ten possible dates:
May 15, May 16, May 19 June 17, June 18 July 14, July 16 August 14, August 15, August 17
Cheryl then tells Albert the month of birth, and Bernard the day (of the month) of birth.
1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. 2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. 3) Albert: Then I also know when Cheryl's birthday is.
- Task
Write a computer program to deduce, by successive elimination, Cheryl's birthday.
- Related task
- References
- Wikipedia article of the same name.
- Tuple Relational Calculus
11l
T Date = (String month, Int day)
V dates = [Date(‘May’, 15), Date(‘May’, 16), Date(‘May’, 19), Date(‘June’, 17), Date(‘June’, 18),
Date(‘July’, 14), Date(‘July’, 16), Date(‘August’, 14), Date(‘August’, 15), Date(‘August’, 17)]
DefaultDict[Int, Set[String]] monthTable
L(date) dates
monthTable[date.day].add(date.month)
DefaultDict[String, Set[Int]] dayTable
L(date) dates
dayTable[date.month].add(date.day)
Set[String] possibleMonths
Set[Int] possibleDays
L(month, days) dayTable
I days.len > 1
possibleMonths.add(month)
L(month, days) dayTable
L(day) days
I monthTable[day].len == 1
possibleMonths.remove(month)
print(‘After first Albert's sentence, possible months are ’Array(possibleMonths).join(‘, ’)‘.’)
L(day, months) monthTable
I months.len > 1
possibleDays.add(day)
Set[Int] impossibleDays
L(day) possibleDays
I monthTable[day].intersection(possibleMonths).len > 1
impossibleDays.add(day)
L(day) impossibleDays
possibleDays.remove(day)
print(‘After Bernard's sentence, possible days are ’Array(possibleDays).join(‘, ’)‘.’)
Set[String] impossibleMonths
L(month) possibleMonths
I dayTable[month].intersection(possibleDays).len > 1
impossibleMonths.add(month)
L(month) impossibleMonths
possibleMonths.remove(month)
assert(possibleMonths.len == 1)
V month = possibleMonths.pop()
print(‘After second Albert's sentence, remaining month is ’month‘...’)
possibleDays = possibleDays.intersection(dayTable[month])
assert(possibleDays.len == 1)
V day = possibleDays.pop()
print(‘and thus remaining day is ’day‘.’)
print()
print(‘So birthday date is ’month‘ ’day‘.’)
- Output:
After first Albert's sentence, possible months are August, July. After Bernard's sentence, possible days are 15, 16, 17. After second Albert's sentence, remaining month is July... and thus remaining day is 16. So birthday date is July 16.
Ada
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type Months is
(January, February, March, April, May, June, July, August, September,
November, December);
type day_num is range 1 .. 31;
type birthdate is record
Month : Months;
Day : day_num;
Active : Boolean;
end record;
type birthday_list is array (Positive range <>) of birthdate;
Possible_birthdates : birthday_list :=
((May, 15, True), (May, 16, True), (May, 19, True), (June, 17, True),
(June, 18, True), (July, 14, True), (July, 16, True), (August, 14, True),
(August, 15, True), (August, 17, True));
procedure print_answer is
begin
for the_day of Possible_birthdates loop
if the_day.Active then
Put_Line (the_day.Month'Image & "," & the_day.Day'Image);
end if;
end loop;
end print_answer;
procedure print_remaining is
count : Natural := 0;
begin
for date of Possible_birthdates loop
if date.Active then
count := count + 1;
end if;
end loop;
Put_Line (count'Image & " remaining.");
end print_remaining;
-- the month cannot have a unique day
procedure first_pass is
count : Natural;
begin
for first_day of Possible_birthdates loop
count := 0;
for next_day of Possible_birthdates loop
if first_day.Day = next_day.Day then
count := count + 1;
end if;
end loop;
if count = 1 then
for the_day of Possible_birthdates loop
if the_day.Active and then first_day.Month = the_day.Month then
the_day.Active := False;
end if;
end loop;
end if;
end loop;
end first_pass;
-- the day must now be unique
procedure second_pass is
count : Natural;
begin
for first_day of Possible_birthdates loop
if first_day.Active then
count := 0;
for next_day of Possible_birthdates loop
if next_day.Active then
if next_day.Day = first_day.Day then
count := count + 1;
end if;
end if;
end loop;
if count > 1 then
for next_day of Possible_birthdates loop
if next_day.Active and then next_day.Day = first_day.Day then
next_day.Active := False;
end if;
end loop;
end if;
end if;
end loop;
end second_pass;
-- the month must now be unique
procedure third_pass is
count : Natural;
begin
for first_day of Possible_birthdates loop
if first_day.Active then
count := 0;
for next_day of Possible_birthdates loop
if next_day.Active and then next_day.Month = first_day.Month
then
count := count + 1;
end if;
end loop;
if count > 1 then
for next_day of Possible_birthdates loop
if next_day.Active and then next_day.Month = first_day.Month
then
next_day.Active := False;
end if;
end loop;
end if;
end if;
end loop;
end third_pass;
begin
print_remaining;
first_pass;
print_remaining;
second_pass;
print_remaining;
third_pass;
print_answer;
end Main;
- Output:
10 remaining. 5 remaining. 3 remaining. JULY, 16
ALGOL 68
BEGIN # Cheryl's birthday puzzle #
[ 1 : 4, 1 : 6 ]INT dates # non-zero indicates a possible date #
:= ( ( 0, 15, 16, 0, 0, 19 ) # may #
, ( 0, 0, 0, 17, 18, 0 ) # june #
, ( 14, 0, 16, 0, 0, 0 ) # july #
, ( 14, 15, 0, 17, 0, 0 ) # august #
);
[]STRING month name = ( "May", "June", "July", "August" );
print( ( "Cheryl tells Albert the month and Bernard the day", newline ) );
print( ( "Albert doesn't know the date and knows Bernard doesn't either", newline ) );
FOR d TO 2 UPB dates DO # elimiate the months with unique days #
INT day count := 0;
INT day := 0;
INT month := 0;
FOR m TO 1 UPB dates DO
IF dates[ m, d ] /= 0 THEN
day count +:= 1;
day := dates[ m, d ];
month := m
FI
OD;
IF day count = 1 THEN
print( ( " Eliminating ", month name[ month ], ", ", whole( day, 0 ), "th is unique", newline ) );
FOR p TO 2 UPB dates DO dates[ month, p ] := 0 OD
FI
OD;
print( ( "Bernard now knows the date", newline ) );
FOR d TO 2 UPB dates DO # eliminate the days that aren't unique #
INT day count := 0;
INT day := 0;
INT month := 0;
FOR m TO 1 UPB dates DO
IF dates[ m, d ] /= 0 THEN
day count +:= 1;
day := dates[ m, d ];
month := m
FI
OD;
IF day count > 1 THEN
print( ( " Eliminating ", whole( day, 0 ), "th, it is non-unique", newline ) );
FOR p TO 1 UPB dates DO dates[ p, d ] := 0 OD
FI
OD;
print( ( "Albert now knows the date", newline ) );
FOR m TO 1 UPB dates DO # eliminate months with non-unique days #
INT day count := 0;
INT day := 0;
INT month := 0;
FOR d TO 2 UPB dates DO
IF dates[ m, d ] /= 0 THEN
day count +:= 1;
day := dates[ m, d ];
month := m
FI
OD;
IF day count > 1 THEN
print( ( " Eliminating ", month name[ m ], ", it has multiple days", newline ) );
FOR p TO 2 UPB dates DO dates[ m, p ] := 0 OD
FI
OD;
print( ( "Cheryl's birthday: " ) ); # show the solution(s) #
FOR m TO 1 UPB dates DO
FOR d TO 2 UPB dates DO
IF dates[ m, d ] /= 0 THEN
print( ( " ", month name[ m ], " ", whole( dates[ m, d ], 0 ), "th" ) )
FI
OD
OD;
print( ( newline ) )
END
- Output:
Cheryl tells Albert the month and Bernard the day Albert doesn't know the date and knows Bernard doesn't either Eliminating June, 18th is unique Eliminating May, 19th is unique Bernard now knows the date Eliminating 14th, it is non-unique Albert now knows the date Eliminating August, it has multiple days Cheryl's birthday: July 16th
AppleScript
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
property M : 1 -- Month
property D : 2 -- Day
on run
-- The MONTH with only one remaining day
-- among the DAYs with unique months,
-- EXCLUDING months with unique days,
-- in Cheryl's list:
showList(uniquePairing(M, ¬
uniquePairing(D, ¬
monthsWithUniqueDays(false, ¬
map(composeList({tupleFromList, |words|, toLower}), ¬
splitOn(", ", ¬
"May 15, May 16, May 19, June 17, June 18, " & ¬
"July 14, July 16, Aug 14, Aug 15, Aug 17"))))))
--> "[('july', '16')]"
end run
-- QUERY FUNCTIONS ----------------------------------------
-- monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)]
on monthsWithUniqueDays(blnInclude, xs)
set _months to map(my fst, uniquePairing(D, xs))
script uniqueDay
on |λ|(md)
set bln to elem(fst(md), _months)
if blnInclude then
bln
else
not bln
end if
end |λ|
end script
filter(uniqueDay, xs)
end monthsWithUniqueDays
-- uniquePairing :: DatePart -> [(M, D)] -> [(M, D)]
on uniquePairing(dp, xs)
script go
property f : my mReturn(item dp of {my fst, my snd})
on |λ|(md)
set dct to f's |λ|(md)
script unique
on |λ|(k)
set mb to lookupDict(k, dct)
if Nothing of mb then
false
else
1 = length of (Just of mb)
end if
end |λ|
end script
set uniques to filter(unique, keys(dct))
script found
on |λ|(tpl)
elem(f's |λ|(tpl), uniques)
end |λ|
end script
filter(found, xs)
end |λ|
end script
bindPairs(xs, go)
end uniquePairing
-- bindPairs :: [(M, D)] -> ((Dict Text [Text], Dict Text [Text])
-- -> [(M, D)]) -> [(M, D)]
on bindPairs(xs, f)
tell mReturn(f)
|λ|(Tuple(dictFromPairs(xs), ¬
dictFromPairs(map(my swap, xs))))
end tell
end bindPairs
-- dictFromPairs :: [(M, D)] -> Dict Text [Text]
on dictFromPairs(mds)
set gps to groupBy(|on|(my eq, my fst), ¬
sortBy(comparing(my fst), mds))
script kv
on |λ|(gp)
Tuple(fst(item 1 of gp), map(my snd, gp))
end |λ|
end script
mapFromList(map(kv, gps))
end dictFromPairs
-- LIBRARY GENERICS ---------------------------------------
-- comparing :: (a -> b) -> (a -> a -> Ordering)
on comparing(f)
script
on |λ|(a, b)
tell mReturn(f)
set fa to |λ|(a)
set fb to |λ|(b)
if fa < fb then
-1
else if fa > fb then
1
else
0
end if
end tell
end |λ|
end script
end comparing
-- composeList :: [(a -> a)] -> (a -> a)
on composeList(fs)
script
on |λ|(x)
script
on |λ|(f, a)
mReturn(f)'s |λ|(a)
end |λ|
end script
foldr(result, x, fs)
end |λ|
end script
end composeList
-- drop :: Int -> [a] -> [a]
-- drop :: Int -> String -> String
on drop(n, xs)
set c to class of xs
if c is not script then
if c is not string then
if n < length of xs then
items (1 + n) thru -1 of xs
else
{}
end if
else
if n < length of xs then
text (1 + n) thru -1 of xs
else
""
end if
end if
else
take(n, xs) -- consumed
return xs
end if
end drop
-- dropAround :: (a -> Bool) -> [a] -> [a]
-- dropAround :: (Char -> Bool) -> String -> String
on dropAround(p, xs)
dropWhile(p, dropWhileEnd(p, xs))
end dropAround
-- dropWhile :: (a -> Bool) -> [a] -> [a]
-- dropWhile :: (Char -> Bool) -> String -> String
on dropWhile(p, xs)
set lng to length of xs
set i to 1
tell mReturn(p)
repeat while i ≤ lng and |λ|(item i of xs)
set i to i + 1
end repeat
end tell
drop(i - 1, xs)
end dropWhile
-- dropWhileEnd :: (a -> Bool) -> [a] -> [a]
-- dropWhileEnd :: (Char -> Bool) -> String -> String
on dropWhileEnd(p, xs)
set i to length of xs
tell mReturn(p)
repeat while i > 0 and |λ|(item i of xs)
set i to i - 1
end repeat
end tell
take(i, xs)
end dropWhileEnd
-- elem :: Eq a => a -> [a] -> Bool
on elem(x, xs)
considering case
xs contains x
end considering
end elem
-- enumFromToInt :: Int -> Int -> [Int]
on enumFromToInt(M, n)
if M ≤ n then
set lst to {}
repeat with i from M to n
set end of lst to i
end repeat
return lst
else
return {}
end if
end enumFromToInt
-- eq (==) :: Eq a => a -> a -> Bool
on eq(a, b)
a = b
end eq
-- filter :: (a -> Bool) -> [a] -> [a]
on filter(f, xs)
tell mReturn(f)
set lst to {}
set lng to length of xs
repeat with i from 1 to lng
set v to item i of xs
if |λ|(v, i, xs) then set end of lst to v
end repeat
return lst
end tell
end filter
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- foldr :: (a -> b -> b) -> b -> [a] -> b
on foldr(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from lng to 1 by -1
set v to |λ|(item i of xs, v, i, xs)
end repeat
return v
end tell
end foldr
-- fst :: (a, b) -> a
on fst(tpl)
if class of tpl is record then
|1| of tpl
else
item 1 of tpl
end if
end fst
-- Typical usage: groupBy(on(eq, f), xs)
-- groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
on groupBy(f, xs)
set mf to mReturn(f)
script enGroup
on |λ|(a, x)
if length of (active of a) > 0 then
set h to item 1 of active of a
else
set h to missing value
end if
if h is not missing value and mf's |λ|(h, x) then
{active:(active of a) & {x}, sofar:sofar of a}
else
{active:{x}, sofar:(sofar of a) & {active of a}}
end if
end |λ|
end script
if length of xs > 0 then
set dct to foldl(enGroup, {active:{item 1 of xs}, sofar:{}}, rest of xs)
if length of (active of dct) > 0 then
sofar of dct & {active of dct}
else
sofar of dct
end if
else
{}
end if
end groupBy
-- insertMap :: Dict -> String -> a -> Dict
on insertMap(rec, k, v)
tell (current application's NSMutableDictionary's ¬
dictionaryWithDictionary:rec)
its setValue:v forKey:(k as string)
return it as record
end tell
end insertMap
-- intercalateS :: String -> [String] -> String
on intercalateS(sep, xs)
set {dlm, my text item delimiters} to {my text item delimiters, sep}
set s to xs as text
set my text item delimiters to dlm
return s
end intercalateS
-- Just :: a -> Maybe a
on Just(x)
{type:"Maybe", Nothing:false, Just:x}
end Just
-- keys :: Dict -> [String]
on keys(rec)
(current application's NSDictionary's dictionaryWithDictionary:rec)'s allKeys() as list
end keys
-- lookupDict :: a -> Dict -> Maybe b
on lookupDict(k, dct)
set ca to current application
set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s objectForKey:k
if v ≠ missing value then
Just(item 1 of ((ca's NSArray's arrayWithObject:v) as list))
else
Nothing()
end if
end lookupDict
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- mapFromList :: [(k, v)] -> Dict
on mapFromList(kvs)
set tpl to unzip(kvs)
script
on |λ|(x)
x as string
end |λ|
end script
(current application's NSDictionary's ¬
dictionaryWithObjects:(|2| of tpl) ¬
forKeys:map(result, |1| of tpl)) as record
end mapFromList
-- min :: Ord a => a -> a -> a
on min(x, y)
if y < x then
y
else
x
end if
end min
-- Nothing :: Maybe a
on Nothing()
{type:"Maybe", Nothing:true}
end Nothing
-- e.g. sortBy(|on|(compare, |length|), ["epsilon", "mu", "gamma", "beta"])
-- on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
on |on|(f, g)
script
on |λ|(a, b)
tell mReturn(g) to set {va, vb} to {|λ|(a), |λ|(b)}
tell mReturn(f) to |λ|(va, vb)
end |λ|
end script
end |on|
-- partition :: predicate -> List -> (Matches, nonMatches)
-- partition :: (a -> Bool) -> [a] -> ([a], [a])
on partition(f, xs)
tell mReturn(f)
set ys to {}
set zs to {}
repeat with x in xs
set v to contents of x
if |λ|(v) then
set end of ys to v
else
set end of zs to v
end if
end repeat
end tell
Tuple(ys, zs)
end partition
-- show :: a -> String
on show(e)
set c to class of e
if c = list then
showList(e)
else if c = record then
set mb to lookupDict("type", e)
if Nothing of mb then
showDict(e)
else
script
on |λ|(t)
if "Either" = t then
set f to my showLR
else if "Maybe" = t then
set f to my showMaybe
else if "Ordering" = t then
set f to my showOrdering
else if "Ratio" = t then
set f to my showRatio
else if class of t is text and t begins with "Tuple" then
set f to my showTuple
else
set f to my showDict
end if
tell mReturn(f) to |λ|(e)
end |λ|
end script
tell result to |λ|(Just of mb)
end if
else if c = date then
"\"" & showDate(e) & "\""
else if c = text then
"'" & e & "'"
else if (c = integer or c = real) then
e as text
else if c = class then
"null"
else
try
e as text
on error
("«" & c as text) & "»"
end try
end if
end show
-- showList :: [a] -> String
on showList(xs)
"[" & intercalateS(", ", map(my show, xs)) & "]"
end showList
-- showTuple :: Tuple -> String
on showTuple(tpl)
set ca to current application
script
on |λ|(n)
set v to (ca's NSDictionary's dictionaryWithDictionary:tpl)'s objectForKey:(n as string)
if v ≠ missing value then
unQuoted(show(item 1 of ((ca's NSArray's arrayWithObject:v) as list)))
else
missing value
end if
end |λ|
end script
"(" & intercalateS(", ", map(result, enumFromToInt(1, length of tpl))) & ")"
end showTuple
-- snd :: (a, b) -> b
on snd(tpl)
if class of tpl is record then
|2| of tpl
else
item 2 of tpl
end if
end snd
-- Enough for small scale sorts.
-- Use instead sortOn :: Ord b => (a -> b) -> [a] -> [a]
-- which is equivalent to the more flexible sortBy(comparing(f), xs)
-- and uses a much faster ObjC NSArray sort method
-- sortBy :: (a -> a -> Ordering) -> [a] -> [a]
on sortBy(f, xs)
if length of xs > 1 then
set h to item 1 of xs
set f to mReturn(f)
script
on |λ|(x)
f's |λ|(x, h) ≤ 0
end |λ|
end script
set lessMore to partition(result, rest of xs)
sortBy(f, |1| of lessMore) & {h} & ¬
sortBy(f, |2| of lessMore)
else
xs
end if
end sortBy
-- splitOn :: String -> String -> [String]
on splitOn(pat, src)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, pat}
set xs to text items of src
set my text item delimiters to dlm
return xs
end splitOn
-- swap :: (a, b) -> (b, a)
on swap(ab)
if class of ab is record then
Tuple(|2| of ab, |1| of ab)
else
{item 2 of ab, item 1 of ab}
end if
end swap
-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
set c to class of xs
if list is c then
if 0 < n then
items 1 thru min(n, length of xs) of xs
else
{}
end if
else if string is c then
if 0 < n then
text 1 thru min(n, length of xs) of xs
else
""
end if
else if script is c then
set ys to {}
repeat with i from 1 to n
set v to xs's |λ|()
if missing value is v then
return ys
else
set end of ys to v
end if
end repeat
return ys
else
missing value
end if
end take
-- toLower :: String -> String
on toLower(str)
set ca to current application
((ca's NSString's stringWithString:(str))'s ¬
lowercaseStringWithLocale:(ca's NSLocale's currentLocale())) as text
end toLower
-- Tuple (,) :: a -> b -> (a, b)
on Tuple(a, b)
{type:"Tuple", |1|:a, |2|:b, length:2}
end Tuple
-- tupleFromList :: [a] -> (a, a ...)
on tupleFromList(xs)
set lng to length of xs
if 1 < lng then
if 2 < lng then
set strSuffix to lng as string
else
set strSuffix to ""
end if
script kv
on |λ|(a, x, i)
insertMap(a, (i as string), x)
end |λ|
end script
foldl(kv, {type:"Tuple" & strSuffix}, xs) & {length:lng}
else
missing value
end if
end tupleFromList
-- unQuoted :: String -> String
on unQuoted(s)
script p
on |λ|(x)
--{34, 39} contains id of x
34 = id of x
end |λ|
end script
dropAround(p, s)
end unQuoted
-- unzip :: [(a,b)] -> ([a],[b])
on unzip(xys)
set xs to {}
set ys to {}
repeat with xy in xys
set end of xs to |1| of xy
set end of ys to |2| of xy
end repeat
return Tuple(xs, ys)
end unzip
-- words :: String -> [String]
on |words|(s)
set ca to current application
(((ca's NSString's stringWithString:(s))'s ¬
componentsSeparatedByCharactersInSet:(ca's ¬
NSCharacterSet's whitespaceAndNewlineCharacterSet()))'s ¬
filteredArrayUsingPredicate:(ca's ¬
NSPredicate's predicateWithFormat:"0 < length")) as list
end |words|
- Output:
"[('july', '16')]"
Arturo
dates: [
[May 15] [May 16] [May 19]
[June 17] [June 18]
[July 14] [July 16]
[August 14] [August 15] [August 17]
]
print ["possible dates:" dates]
print "\n(1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too."
print "\t-> meaning: the month cannot have a unique day"
dates: filter dates 'd [
in? d\0 map select dates 'dd [
1 = size select dates 'pd -> pd\1=dd\1
] 'dd -> dd\0
]
print ["\t-> remaining:" dates]
print "\n(2) Bernard: At first I don't know when Cheryl's birthday is, but I know now."
print "\t-> meaning: the day must be unique"
dates: select dates 'd [
1 = size select dates 'pd -> pd\1=d\1
]
print ["\t-> remaining:" dates]
print "\n(3) Albert: Then I also know when Cheryl's birthday is."
print "\t-> meaning: the month must be unique"
dates: select dates 'd [
1 = size select dates 'pd -> pd\0=d\0
]
print ["\t-> remaining:" dates]
print ["\nCheryl's birthday:" first dates]
- Output:
possible dates: [[May 15] [May 16] [May 19] [June 17] [June 18] [July 14] [July 16] [August 14] [August 15] [August 17]] (1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. -> meaning: the month cannot have a unique day -> remaining: [[July 14] [July 16] [August 14] [August 15] [August 17]] (2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. -> meaning: the day must be unique -> remaining: [[July 16] [August 15] [August 17]] (3) Albert: Then I also know when Cheryl's birthday is. -> meaning: the month must be unique -> remaining: [[July 16]] Cheryl's birthday: [July 16]
AutoHotkey
oDates:= {"May" : [ 15, 16, 19]
,"Jun" : [ 17, 18]
,"Jul" : [14, 16]
,"Aug" : [14, 15, 17]}
filter1(oDates)
filter2(oDates)
filter3(oDates)
MsgBox % result := checkAnswer(oDates)
return
filter1(ByRef oDates){ ; remove months that has a unique day in it.
for d, obj in MonthsOfDay(oDates)
if (obj.count() = 1)
for m, bool in obj
oDates.Remove(m)
}
filter2(ByRef oDates){ ; remove non-unique days from remaining months.
for d, obj in MonthsOfDay(oDates)
if (obj.count() > 1)
for m, bool in obj
for i, day in oDates[m]
if (day=d)
oDates[m].Remove(i)
}
filter3(ByRef oDates){ ; remove months that has multiple days from remaining months.
oRemove := []
for m, obj in oDates
if obj.count() > 1
oRemove.Push(m)
for i, m in oRemove
oDates.Remove(m)
}
MonthsOfDay(oDates){ ; create a list of months per day.
MonthsOfDay := []
for m, obj in oDates
for i, d in obj
MonthsOfDay[d, m] := 1
return MonthsOfDay
}
checkAnswer(oDates){ ; check unique answer if any.
if oDates.count()>1
return false
for m, obj in oDates
if obj.count() > 1
return false
else
return m " " obj.1
}
- Output:
Jul 16
AWK
# syntax: GAWK -f CHERYLS_BIRTHDAY.AWK [-v debug={0|1}]
#
# sorting:
# PROCINFO["sorted_in"] is used by GAWK
# SORTTYPE is used by Thompson Automation's TAWK
#
BEGIN {
debug += 0
PROCINFO["sorted_in"] = "@ind_num_asc" ; SORTTYPE = 1
n = split("05/15,05/16,05/19,06/17,06/18,07/14,07/16,08/14,08/15,08/17",arr,",")
for (i=1; i<=n; i++) { # move dates to a more friendly structure
mmdd_arr[arr[i]] = ""
}
print("Cheryl offers these ten MM/DD choices:")
cb_show_dates()
printf("Cheryl then tells Albert her birth 'month' and Bernard her birth 'day'.\n\n")
print("1. Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.")
cb_filter1()
print("2. Bernard: At first I don't know when Cheryl's birthday is, but I know now.")
cb_filter2()
print("3. Albert: Then I also know when Cheryl's birthday is.")
cb_filter3()
exit(0)
}
function cb_filter1( i,j) {
print("deduction: the month cannot have a unique day, leaving:")
cb_load_arrays(4)
for (j in arr1) {
if (arr1[j] == 1) {
if (debug) { printf("unique day %s\n",j) }
arr3[arr2[j]] = ""
}
}
cb_remove_dates()
}
function cb_filter2( i,j) {
print("deduction: the day must be unique, leaving:")
cb_load_arrays(4)
for (j in arr1) {
if (arr1[j] > 1) {
if (debug) { printf("non-unique day %s\n",j) }
arr3[j] = ""
}
}
cb_remove_dates("...")
}
function cb_filter3( i,j) {
print("deduction: the month must be unique, leaving:")
cb_load_arrays(1)
for (j in arr1) {
if (arr1[j] > 1) {
if (debug) { printf("non-unique month %s\n",j) }
arr3[j] = ""
}
}
cb_remove_dates()
}
function cb_load_arrays(col, i,key) {
delete arr1
delete arr2
delete arr3
for (i in mmdd_arr) {
key = substr(i,col,2)
arr1[key]++
arr2[key] = substr(i,1,2)
}
}
function cb_remove_dates(pattern, i,j) {
for (j in arr3) {
for (i in mmdd_arr) {
if (i ~ ("^" pattern j)) {
if (debug) { printf("removing %s\n",i) }
delete mmdd_arr[i]
}
}
}
cb_show_dates()
}
function cb_show_dates( i) {
if (debug) { printf("%d remaining\n",length(mmdd_arr)) }
for (i in mmdd_arr) {
printf("%s ",i)
}
printf("\n\n")
}
- Output:
Cheryl offers these ten MM/DD choices: 05/15 05/16 05/19 06/17 06/18 07/14 07/16 08/14 08/15 08/17 Cheryl then tells Albert her birth 'month' and Bernard her birth 'day'. 1. Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. deduction: the month cannot have a unique day, leaving: 07/14 07/16 08/14 08/15 08/17 2. Bernard: At first I don't know when Cheryl's birthday is, but I know now. deduction: the day must be unique, leaving: 07/16 08/15 08/17 3. Albert: Then I also know when Cheryl's birthday is. deduction: the month must be unique, leaving: 07/16
C
#include <stdbool.h>
#include <stdio.h>
char *months[] = {
"ERR", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
struct Date {
int month, day;
bool active;
} dates[] = {
{5,15,true}, {5,16,true}, {5,19,true},
{6,17,true}, {6,18,true},
{7,14,true}, {7,16,true},
{8,14,true}, {8,15,true}, {8,17,true}
};
#define UPPER_BOUND (sizeof(dates) / sizeof(struct Date))
void printRemaining() {
int i, c;
for (i = 0, c = 0; i < UPPER_BOUND; i++) {
if (dates[i].active) {
c++;
}
}
printf("%d remaining.\n", c);
}
void printAnswer() {
int i;
for (i = 0; i < UPPER_BOUND; i++) {
if (dates[i].active) {
printf("%s, %d\n", months[dates[i].month], dates[i].day);
}
}
}
void firstPass() {
// the month cannot have a unique day
int i, j, c;
for (i = 0; i < UPPER_BOUND; i++) {
c = 0;
for (j = 0; j < UPPER_BOUND; j++) {
if (dates[j].day == dates[i].day) {
c++;
}
}
if (c == 1) {
for (j = 0; j < UPPER_BOUND; j++) {
if (!dates[j].active) continue;
if (dates[j].month == dates[i].month) {
dates[j].active = false;
}
}
}
}
}
void secondPass() {
// the day must now be unique
int i, j, c;
for (i = 0; i < UPPER_BOUND; i++) {
if (!dates[i].active) continue;
c = 0;
for (j = 0; j < UPPER_BOUND; j++) {
if (!dates[j].active) continue;
if (dates[j].day == dates[i].day) {
c++;
}
}
if (c > 1) {
for (j = 0; j < UPPER_BOUND; j++) {
if (!dates[j].active) continue;
if (dates[j].day == dates[i].day) {
dates[j].active = false;
}
}
}
}
}
void thirdPass() {
// the month must now be unique
int i, j, c;
for (i = 0; i < UPPER_BOUND; i++) {
if (!dates[i].active) continue;
c = 0;
for (j = 0; j < UPPER_BOUND; j++) {
if (!dates[j].active) continue;
if (dates[j].month == dates[i].month) {
c++;
}
}
if (c > 1) {
for (j = 0; j < UPPER_BOUND; j++) {
if (!dates[j].active) continue;
if (dates[j].month == dates[i].month) {
dates[j].active = false;
}
}
}
}
}
int main() {
printRemaining();
// the month cannot have a unique day
firstPass();
printRemaining();
// the day must now be unique
secondPass();
printRemaining();
// the month must now be unique
thirdPass();
printAnswer();
return 0;
}
- Output:
10 remaining. 5 remaining. 3 remaining. Jul, 16
C#
public static class CherylsBirthday
{
public static void Main() {
var dates = new HashSet<(string month, int day)> {
("May", 15),
("May", 16),
("May", 19),
("June", 17),
("June", 18),
("July", 14),
("July", 16),
("August", 14),
("August", 15),
("August", 17)
};
Console.WriteLine(dates.Count + " remaining.");
//The month cannot have a unique day.
var monthsWithUniqueDays = dates.GroupBy(d => d.day).Where(g => g.Count() == 1).Select(g => g.First().month).ToHashSet();
dates.RemoveWhere(d => monthsWithUniqueDays.Contains(d.month));
Console.WriteLine(dates.Count + " remaining.");
//The day must now be unique.
dates.IntersectWith(dates.GroupBy(d => d.day).Where(g => g.Count() == 1).Select(g => g.First()));
Console.WriteLine(dates.Count + " remaining.");
//The month must now be unique.
dates.IntersectWith(dates.GroupBy(d => d.month).Where(g => g.Count() == 1).Select(g => g.First()));
Console.WriteLine(dates.Single());
}
}
- Output:
10 remaining. 5 remaining. 3 remaining. (July, 16)
C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const vector<string> MONTHS = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
struct Birthday {
int month, day;
friend ostream &operator<<(ostream &, const Birthday &);
};
ostream &operator<<(ostream &out, const Birthday &birthday) {
return out << MONTHS[birthday.month - 1] << ' ' << birthday.day;
}
template <typename C>
bool monthUniqueIn(const Birthday &b, const C &container) {
auto it = cbegin(container);
auto end = cend(container);
int count = 0;
while (it != end) {
if (it->month == b.month) {
count++;
}
it = next(it);
}
return count == 1;
}
template <typename C>
bool dayUniqueIn(const Birthday &b, const C &container) {
auto it = cbegin(container);
auto end = cend(container);
int count = 0;
while (it != end) {
if (it->day == b.day) {
count++;
}
it = next(it);
}
return count == 1;
}
template <typename C>
bool monthWithUniqueDayIn(const Birthday &b, const C &container) {
auto it = cbegin(container);
auto end = cend(container);
while (it != end) {
if (it->month == b.month && dayUniqueIn(*it, container)) {
return true;
}
it = next(it);
}
return false;
}
int main() {
vector<Birthday> choices = {
{5, 15}, {5, 16}, {5, 19}, {6, 17}, {6, 18},
{7, 14}, {7, 16}, {8, 14}, {8, 15}, {8, 17},
};
// Albert knows the month but doesn't know the day.
// So the month can't be unique within the choices.
vector<Birthday> filtered;
for (auto bd : choices) {
if (!monthUniqueIn(bd, choices)) {
filtered.push_back(bd);
}
}
// Albert also knows that Bernard doesn't know the answer.
// So the month can't have a unique day.
vector<Birthday> filtered2;
for (auto bd : filtered) {
if (!monthWithUniqueDayIn(bd, filtered)) {
filtered2.push_back(bd);
}
}
// Bernard now knows the answer.
// So the day must be unique within the remaining choices.
vector<Birthday> filtered3;
for (auto bd : filtered2) {
if (dayUniqueIn(bd, filtered2)) {
filtered3.push_back(bd);
}
}
// Albert now knows the answer too.
// So the month must be unique within the remaining choices.
vector<Birthday> filtered4;
for (auto bd : filtered3) {
if (monthUniqueIn(bd, filtered3)) {
filtered4.push_back(bd);
}
}
if (filtered4.size() == 1) {
cout << "Cheryl's birthday is " << filtered4[0] << '\n';
} else {
cout << "Something went wrong!\n";
}
return 0;
}
- Output:
Cheryl's birthday is Jul 16
Common Lisp
;; Author: Amir Teymuri, Saturday 20.10.2018
(defparameter *possible-dates*
'((15 . may) (16 . may) (19 . may)
(17 . june) (18 . june)
(14 . july) (16 . july)
(14 . august) (15 . august) (17 . august)))
(defun unique-date-parts (possible-dates &key (alist-look-at #'car) (alist-r-assoc #'assoc))
(let* ((date-parts (mapcar alist-look-at possible-dates))
(unique-date-parts (remove-if #'(lambda (part) (> (count part date-parts) 1)) date-parts)))
(mapcar #'(lambda (part) (funcall alist-r-assoc part possible-dates))
unique-date-parts)))
(defun person (person possible-dates)
"Who's turn is it to think?"
(case person
('albert (unique-date-parts possible-dates :alist-look-at #'cdr :alist-r-assoc #'rassoc))
('bernard (unique-date-parts possible-dates :alist-look-at #'car :alist-r-assoc #'assoc))))
(defun cheryls-birthday (possible-dates)
(person 'albert
(person 'bernard
(set-difference
possible-dates
(person 'bernard possible-dates)
:key #'cdr))))
(cheryls-birthday *possible-dates*) ;; => ((16 . JULY))
D
import std.algorithm.iteration : filter, joiner, map;
import std.algorithm.searching : canFind;
import std.algorithm.sorting : sort;
import std.array : array;
import std.datetime.date : Date, Month;
import std.stdio : writeln;
void main() {
auto choices = [
// Month.jan
Date(2019, Month.may, 15),
Date(2019, Month.may, 16),
Date(2019, Month.may, 19), // unique day (1)
Date(2019, Month.jun, 17),
Date(2019, Month.jun, 18), // unique day (1)
Date(2019, Month.jul, 14),
Date(2019, Month.jul, 16), // final answer
Date(2019, Month.aug, 14),
Date(2019, Month.aug, 15),
Date(2019, Month.aug, 17),
];
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer
auto uniqueMonths = choices.sort!"a.day < b.day".groupBy.filter!"a.array.length == 1".joiner.map!"a.month";
// writeln(uniqueMonths.save);
auto filter1 = choices.filter!(a => !canFind(uniqueMonths.save, a.month)).array;
// Bernard now knows the answer, so the day must be unique within the remaining choices
auto uniqueDays = filter1.sort!"a.day < b.day".groupBy.filter!"a.array.length == 1".joiner.map!"a.day";
auto filter2 = filter1.filter!(a => canFind(uniqueDays.save, a.day)).array;
// Albert knows the answer too, so the month must be unique within the remaining choices
auto birthDay = filter2.sort!"a.month < b.month".groupBy.filter!"a.array.length == 1".joiner.front;
// print the result
writeln(birthDay.month, " ", birthDay.day);
}
- Output:
jul 16
F#
//Find Cheryl's Birthday. Nigel Galloway: October 23rd., 2018
type Month = |May |June |July |August
let fN n= n |> List.filter(fun (_,n)->(List.length n) < 2) |> List.unzip
let dates = [(May,15);(May,16);(May,19);(June,17);(June,18);(July,14);(July,16);(August,14);(August,15);(August,17)]
let _,n = dates |> List.groupBy snd |> fN
let i = n |> List.concat |> List.map fst |> Set.ofList
let _,g = dates |> List.filter(fun (n,_)->not (Set.contains n i)) |> List.groupBy snd |> fN
let _,e = List.concat g |> List.groupBy fst |> fN
printfn "%A" e
- Output:
[[(July, 16)]]
Factor
USING: assocs calendar.english fry io kernel prettyprint
sequences sets.extras ;
: unique-by ( seq quot -- newseq )
2dup map non-repeating '[ @ _ member? ] filter ; inline
ALIAS: day first
ALIAS: month second
{
{ 15 5 } { 16 5 } { 19 5 } { 17 6 } { 18 6 }
{ 14 7 } { 16 7 } { 14 8 } { 15 8 } { 17 8 }
}
! the month cannot have a unique day
dup [ day ] map non-repeating over extract-keys values
'[ month _ member? ] reject
! of the remaining dates, day must be unique
[ day ] unique-by
! of the remaining dates, month must be unique
[ month ] unique-by
! print a date that looks like { { 16 7 } }
first first2 month-name write bl .
- Output:
July 16
FreeBASIC
Dim As Integer i, j, contarDias, dia, mes
Dim fechas(1 To 4, 1 To 6) As Integer => {{0, 15, 16, 0, 0, 19}, {0, 0, 0, 17, 18, 0}, {14, 0, 16, 0, 0, 0}, {14, 15, 0, 17, 0, 0}}
Dim nombreMes(1 To 4) As String => {"May", "June", "July", "August"}
Print "Cheryl tells Albert the month and Bernard the day"
Print "Albert doesn't know the date and knows Bernard doesn't either"
' elimiate the months with unique days
For i = 1 To 6
contarDias = 0
dia = 0
mes = 0
For j = 1 To 4
If fechas(j, i) <> 0 Then
contarDias += 1
dia = fechas(j, i)
mes = j
End If
Next j
If contarDias = 1 Then
Print " Eliminating "; nombreMes(mes); ", "; Str(dia); "th is unique"
For j = 1 To 6
fechas(mes, j) = 0
Next j
End If
Next i
Print "Bernard now knows the date"
' eliminate the days that aren't unique
For i = 1 To 6
contarDias = 0
dia = 0
mes = 0
For j = 1 To 4
If fechas(j, i) <> 0 Then
contarDias += 1
dia = fechas(j, i)
mes = j
End If
Next j
If contarDias > 1 Then
Print " Eliminating "; Str(dia); "th, it is non-unique"
For j = 1 To 4
fechas(j, i) = 0
Next j
End If
Next i
Print "Albert now knows the date"
' eliminate months with non-unique days
For i = 1 To 4
contarDias = 0
dia = 0
mes = 0
For j = 1 To 6
If fechas(i, j) <> 0 Then
contarDias += 1
dia = fechas(i, j)
mes = i
End If
Next j
If contarDias > 1 Then
Print " Eliminating "; nombreMes(i); ", it has multiple days"
For j = 1 To 6
fechas(i, j) = 0
Next j
End If
Next i
Print "Cheryl's birthday: ";
For i = 1 To 4
For j = 1 To 6
If fechas(i, j) <> 0 Then
Print " "; nombreMes(i); " "; Str(fechas(i, j)); "th"
End If
Next j
Next i
Sleep
- Output:
Same as ALGOL 68 entry.
Go
package main
import (
"fmt"
"time"
)
type birthday struct{ month, day int }
func (b birthday) String() string {
return fmt.Sprintf("%s %d", time.Month(b.month), b.day)
}
func (b birthday) monthUniqueIn(bds []birthday) bool {
count := 0
for _, bd := range bds {
if bd.month == b.month {
count++
}
}
if count == 1 {
return true
}
return false
}
func (b birthday) dayUniqueIn(bds []birthday) bool {
count := 0
for _, bd := range bds {
if bd.day == b.day {
count++
}
}
if count == 1 {
return true
}
return false
}
func (b birthday) monthWithUniqueDayIn(bds []birthday) bool {
for _, bd := range bds {
if bd.month == b.month && bd.dayUniqueIn(bds) {
return true
}
}
return false
}
func main() {
choices := []birthday{
{5, 15}, {5, 16}, {5, 19}, {6, 17}, {6, 18},
{7, 14}, {7, 16}, {8, 14}, {8, 15}, {8, 17},
}
// Albert knows the month but doesn't know the day.
// So the month can't be unique within the choices.
var filtered []birthday
for _, bd := range choices {
if !bd.monthUniqueIn(choices) {
filtered = append(filtered, bd)
}
}
// Albert also knows that Bernard doesn't know the answer.
// So the month can't have a unique day.
var filtered2 []birthday
for _, bd := range filtered {
if !bd.monthWithUniqueDayIn(filtered) {
filtered2 = append(filtered2, bd)
}
}
// Bernard now knows the answer.
// So the day must be unique within the remaining choices.
var filtered3 []birthday
for _, bd := range filtered2 {
if bd.dayUniqueIn(filtered2) {
filtered3 = append(filtered3, bd)
}
}
// Albert now knows the answer too.
// So the month must be unique within the remaining choices.
var filtered4 []birthday
for _, bd := range filtered3 {
if bd.monthUniqueIn(filtered3) {
filtered4 = append(filtered4, bd)
}
}
if len(filtered4) == 1 {
fmt.Println("Cheryl's birthday is", filtered4[0])
} else {
fmt.Println("Something went wrong!")
}
}
- Output:
Cheryl's birthday is July 16
Fortran
program code_translation
implicit none
character(len=3), dimension(13) :: months = ["ERR", "Jan", "Feb", "Mar", "Apr", "May",&
"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
type :: Date
integer :: month, day
logical :: active
end type Date
type(Date), dimension(10) :: dates = [Date(5,15,.true.), Date(5,16,.true.), Date(5,19,.true.), &
Date(6,17,.true.), Date(6,18,.true.), &
Date(7,14,.true.), Date(7,16,.true.), &
Date(8,14,.true.), Date(8,15,.true.), Date(8,17,.true.)]
integer, parameter :: UPPER_BOUND = size(dates)
write(*,*) 'possible dates: [[May 15] [May 16] [May 19] [June 17] [June 18] [July 14] [July 16] [August 14] [August 15] [August &
17]]'
write(*,*)
write(*,*) '(1) Albert: I don''t know when Cheryl''s birthday is, but I know that Bernard does not know too.'
write(*,*) ' -> meaning: the month cannot have a unique day'
write(*,*) ' -> remaining: [[July 14] [July 16] [August 14] [August 15] [August 17]] '
write(*,*)
write(*,*) "(2) Bernard: At first I don't know when Cheryl's birthday is, but I know now."
write(*,*) ' -> meaning: the day must be unique'
write(*,*) ' -> remaining: [[July 16] [August 15] [August 17]] '
write(*,*)
write(*,*) '(3) Albert: Then I also know when Cheryl''s birthday is.'
write(*,*) ' -> meaning: the month must be unique'
write(*,*) ' -> remaining: [[July 16]] '
call printRemaining()
! the month cannot have a unique day
call firstPass()
call printRemaining()
! the day must now be unique
call secondPass()
call printRemaining()
! the month must now be unique
call thirdPass()
call printAnswer()
contains
subroutine printRemaining()
integer :: i, c
do i = 1, UPPER_BOUND
if (dates(i)%active) then
write(*,'(a,1x,i0,1x)',advance="no") months(dates(i)%month+1),dates(i)%day
c = c + 1
end if
end do
!
write(*,*)
end subroutine printRemaining
subroutine printAnswer()
integer :: i
write(*,'(a)',advance ='no') 'Cheryl''s birtday is on '
do i = 1, UPPER_BOUND
if (dates(i)%active) then
write(*,'(a,1a1,i0)') trim(months(dates(i)%month+1)), ",", dates(i)%day
end if
end do
end subroutine printAnswer
subroutine firstPass()
! the month cannot have a unique day
integer :: i, j, c
do i = 1, UPPER_BOUND
c = 0
do j = 1, UPPER_BOUND
if (dates(j)%day == dates(i)%day) then
c = c + 1
end if
end do
if (c == 1) then
do j = 1, UPPER_BOUND
if (.not. dates(j)%active) cycle
if (dates(j)%month == dates(i)%month) then
dates(j)%active = .false.
end if
end do
end if
end do
end subroutine firstPass
subroutine secondPass()
! the day must now be unique
integer :: i, j, c
do i = 1, UPPER_BOUND
if (.not. dates(i)%active) cycle
c = 0
do j = 1, UPPER_BOUND
if (.not. dates(j)%active) cycle
if (dates(j)%day == dates(i)%day) then
c = c + 1
end if
end do
if (c > 1) then
do j = 1, UPPER_BOUND
if (.not. dates(j)%active) cycle
if (dates(j)%day == dates(i)%day) then
dates(j)%active = .false.
end if
end do
end if
end do
end subroutine secondPass
subroutine thirdPass()
! the month must now be unique
integer :: i, j, c
do i = 1, UPPER_BOUND
if (.not. dates(i)%active) cycle
c = 0
do j = 1, UPPER_BOUND
if (.not. dates(j)%active) cycle
if (dates(j)%month == dates(i)%month) then
c = c + 1
end if
end do
if (c > 1) then
do j = 1, UPPER_BOUND
if (.not. dates(j)%active) cycle
if (dates(j)%month == dates(i)%month) then
dates(j)%active = .false.
end if
end do
end if
end do
end subroutine thirdPass
end program code_translation
- Output:
possible dates: [[May 15] [May 16] [May 19] [June 17] [June 18] [July 14] [July 16] [August 14] [August 15] [August 17]] (1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too. -> meaning: the month cannot have a unique day -> remaining: [[July 14] [July 16] [August 14] [August 15] [August 17]] (2) Bernard: At first I don't know when Cheryl's birthday is, but I know now. -> meaning: the day must be unique -> remaining: [[July 16] [August 15] [August 17]] (3) Albert: Then I also know when Cheryl's birthday is. -> meaning: the month must be unique -> remaining: [[July 16]] May 15 May 16 May 19 Jun 17 Jun 18 Jul 14 Jul 16 Aug 14 Aug 15 Aug 17 Jul 14 Jul 16 Aug 14 Aug 15 Aug 17 Jul 16 Aug 15 Aug 17 Cheryl's birthday is on Jul,16
Groovy
import java.time.Month
class Main {
private static class Birthday {
private Month month
private int day
Birthday(Month month, int day) {
this.month = month
this.day = day
}
Month getMonth() {
return month
}
int getDay() {
return day
}
@Override
String toString() {
return month.toString() + " " + day
}
}
static void main(String[] args) {
List<Birthday> choices = [
new Birthday(Month.MAY, 15),
new Birthday(Month.MAY, 16),
new Birthday(Month.MAY, 19),
new Birthday(Month.JUNE, 17),
new Birthday(Month.JUNE, 18),
new Birthday(Month.JULY, 14),
new Birthday(Month.JULY, 16),
new Birthday(Month.AUGUST, 14),
new Birthday(Month.AUGUST, 15),
new Birthday(Month.AUGUST, 17)
]
println("There are ${choices.size()} candidates remaining.")
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer
Set<Birthday> uniqueMonths = choices.groupBy { it.getDay() }
.values()
.findAll() { it.size() == 1 }
.flatten()
.collect { ((Birthday) it).getMonth() }
.toSet() as Set<Birthday>
def f1List = choices.findAll { !uniqueMonths.contains(it.getMonth()) }
println("There are ${f1List.size()} candidates remaining.")
// Bernard now knows the answer, so the day must be unique within the remaining choices
List<Birthday> f2List = f1List.groupBy { it.getDay() }
.values()
.findAll { it.size() == 1 }
.flatten()
.toList() as List<Birthday>
println("There are ${f2List.size()} candidates remaining.")
// Albert knows the answer too, so the month must be unique within the remaining choices
List<Birthday> f3List = f2List.groupBy { it.getMonth() }
.values()
.findAll { it.size() == 1 }
.flatten()
.toList() as List<Birthday>
println("There are ${f3List.size()} candidates remaining.")
if (f3List.size() == 1) {
println("Cheryl's birthday is ${f3List.head()}")
} else {
System.out.println("No unique choice found")
}
}
}
- Output:
There are 10 candidates remaining. There are 5 candidates remaining. There are 3 candidates remaining. There are 1 candidates remaining. Cheryl's birthday is JULY 16
Haskell
{-# LANGUAGE OverloadedStrings #-}
import Data.List as L (filter, groupBy, head, length, sortOn)
import Data.Map.Strict as M (Map, fromList, keys, lookup)
import Data.Text as T (Text, splitOn, words)
import Data.Maybe (fromJust)
import Data.Ord (comparing)
import Data.Function (on)
import Data.Tuple (swap)
import Data.Bool (bool)
data DatePart
= Month
| Day
type M = Text
type D = Text
main :: IO ()
main =
print $
-- 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)
(\(x:y:_) -> (x, y)) . T.words <$>
splitOn
", "
"May 15, May 16, May 19, June 17, June 18, \
\July 14, July 16, Aug 14, Aug 15, Aug 17"
----------------------QUERY FUNCTIONS----------------------
monthsWithUniqueDays :: Bool -> [(M, D)] -> [(M, D)]
monthsWithUniqueDays bln xs =
let months = fst <$> uniquePairing Day xs
in L.filter (bool not id bln . (`elem` months) . fst) xs
uniquePairing :: DatePart -> [(M, D)] -> [(M, D)]
uniquePairing dp xs =
let f =
case dp of
Month -> fst
Day -> snd
in (\md ->
let dct = f md
uniques =
L.filter
((1 ==) . L.length . fromJust . flip M.lookup dct)
(keys dct)
in L.filter ((`elem` uniques) . f) xs)
((((,) . mapFromPairs) <*> mapFromPairs . fmap swap) xs)
mapFromPairs :: [(M, D)] -> Map Text [Text]
mapFromPairs xs =
M.fromList $
((,) . fst . L.head) <*> fmap snd <$>
L.groupBy (on (==) fst) (L.sortOn fst xs)
- Output:
[("July","16")]
J
Solution:
Dates=: cutLF noun define
15 May
16 May
19 May
17 June
18 June
14 July
16 July
14 August
15 August
17 August
)
getDayMonth=: |:@:(cut&>) NB. retrieve lists of days and months from dates
keep=: adverb def '] #~ u' NB. apply mask to filter dates
monthsWithUniqueDay=: {./. #~ (1=#)/. NB. list months that have a unique day
isMonthWithoutUniqueDay=: (] -.@e. monthsWithUniqueDay)/@getDayMonth NB. mask of dates with a month that doesn't have a unique day
uniqueDayInMonth=: ~.@[ #~ (1=#)/. NB. list of days that are unique to 1 month
isUniqueDayInMonth=: ([ e. uniqueDayInMonth)/@getDayMonth NB. mask of dates with a day that is unique to 1 month
uniqueMonth=: ~.@] #~ (1=#)/.~ NB. list of months with 1 unique day
isUniqueMonth=: (] e. uniqueMonth)/@getDayMonth NB. mask of dates with a month that has 1 unique day
Usage:
isUniqueMonth keep isUniqueDayInMonth keep isMonthWithoutUniqueDay keep Dates
+-------+
|16 July|
+-------+
Alternative Approach
The concepts here are the same, of course, it's just the presentation that's different.
possible=: cut;._2 'May 15, May 16, May 19, June 17, June 18, July 14, July 16, August 14, August 15, August 17,'
Albert=: {."1 NB. Albert knows month
Bernard=: {:"1 NB. Bernard knows day
NB. Bernard's understanding of Albert's first pass
days=: {:"1 possible
invaliddays=: (1=#/.~ days)#~.days
months=: {."1 possible
validmonths=: months -. (days e. invaliddays)#months
possibleA=. (months e. validmonths)# possible
NB. Albert's understanding of Bernard's pass
days=: {:"1 possibleA
invaliddays=: (1<#/.~ days)#~.days
possibleB=. (days e. days-.invaliddays)# possibleA
NB. our understanding of Albert's understanding of Bernard's understanding of Albert's first pass
months=: {."1 possibleB
invalidmonths=: (1<#/.~months)#~.months
echo ;:inv (months e. months -. invalidmonths)#possibleB
This gives us the July 16 result we were expecting
Java
import java.time.Month;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
private static class Birthday {
private Month month;
private int day;
public Birthday(Month month, int day) {
this.month = month;
this.day = day;
}
public Month getMonth() {
return month;
}
public int getDay() {
return day;
}
@Override
public String toString() {
return month + " " + day;
}
}
public static void main(String[] args) {
List<Birthday> choices = List.of(
new Birthday(Month.MAY, 15),
new Birthday(Month.MAY, 16),
new Birthday(Month.MAY, 19),
new Birthday(Month.JUNE, 17),
new Birthday(Month.JUNE, 18),
new Birthday(Month.JULY, 14),
new Birthday(Month.JULY, 16),
new Birthday(Month.AUGUST, 14),
new Birthday(Month.AUGUST, 15),
new Birthday(Month.AUGUST, 17)
);
System.out.printf("There are %d candidates remaining.\n", choices.size());
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer
Set<Month> uniqueMonths = choices.stream()
.collect(Collectors.groupingBy(Birthday::getDay))
.values()
.stream()
.filter(g -> g.size() == 1)
.flatMap(Collection::stream)
.map(Birthday::getMonth)
.collect(Collectors.toSet());
List<Birthday> f1List = choices.stream()
.filter(birthday -> !uniqueMonths.contains(birthday.month))
.collect(Collectors.toList());
System.out.printf("There are %d candidates remaining.\n", f1List.size());
// Bernard now knows the answer, so the day must be unique within the remaining choices
List<Birthday> f2List = f1List.stream()
.collect(Collectors.groupingBy(Birthday::getDay))
.values()
.stream()
.filter(g -> g.size() == 1)
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.printf("There are %d candidates remaining.\n", f2List.size());
// Albert knows the answer too, so the month must be unique within the remaining choices
List<Birthday> f3List = f2List.stream()
.collect(Collectors.groupingBy(Birthday::getMonth))
.values()
.stream()
.filter(g -> g.size() == 1)
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.printf("There are %d candidates remaining.\n", f3List.size());
if (f3List.size() == 1) {
System.out.printf("Cheryl's birthday is %s\n", f3List.get(0));
} else {
System.out.println("No unique choice found");
}
}
}
- Output:
There are 10 candidates remaining. There are 5 candidates remaining. There are 3 candidates remaining. There are 1 candidates remaining. Cheryl's birthday is JULY 16
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();
})();
- Output:
[["July","16"]]
jq
Adapted from Wren
Works with gojq, the Go implementation of jq
A Birthday is represented by a JSON object {month, day} where {month:0} represents January.
def count(stream; cond):
reduce stream as $i (0; if $i|cond then .+1 else . end);
def Months: [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
# tostring
def birthday: "\(Months[.month-1]) \(.day)";
# Input: a Birthday
def monthUniqueIn($bds):
.month as $thisMonth
| count( $bds[]; .month == $thisMonth) == 1;
# Input: a Birthday
def dayUniqueIn($bds):
.day as $thisDay
| count( $bds[]; .day == $thisDay) == 1;
# Input: a Birthday
def monthWithUniqueDayIn($bds):
.month as $thisMonth
| any( $bds[]; $thisMonth == .month and dayUniqueIn($bds));
def choices: [
{month: 5, day: 15}, {month: 5, day: 16}, {month: 5, day: 19}, {month: 6, day: 17},
{month: 6, day: 18}, {month: 7, day: 14}, {month: 7, day: 16}, {month: 8, day: 14},
{month: 8, day: 15}, {month: 8, day: 17}
];
# Albert knows the month but doesn't know the day,
# so the month can't be unique within the choices.
def filter1:
. as $in
| map(select( monthUniqueIn($in) | not));
# Albert also knows that Bernard doesn't know the answer,
# so the month can't have a unique day.
def filter2:
. as $in
| map(select( monthWithUniqueDayIn($in) | not));
# Bernard now knows the answer,
# so the day must be unique within the remaining choices.
def filter3:
. as $in
| map(select( dayUniqueIn($in) ));
# Albert now knows the answer too.
# So the month must be unique within the remaining choices.
def filter4:
. as $in
| map(select( monthUniqueIn($in) ));
def solve:
(choices | filter1 | filter2 | filter3 | filter4) as $bds
| if $bds|length == 1
then "Cheryl's birthday is \($bds[0]|birthday)."
else "Whoops!"
end;
solve
- Output:
Cheryl's birthday is July 16.
Julia
const dates = [[15, "May"], [16, "May"], [19, "May"], [17, "June"], [18, "June"],
[14, "July"], [16, "July"], [14, "August"], [15, "August"], [17, "August"]]
uniqueday(parr) = filter(x -> count(y -> y[1] == x[1], parr) == 1, parr)
# At the start, they come to know that they have no unique day of month to identify.
const f1 = filter(m -> !(m[2] in [d[2] for d in uniqueday(dates)]), dates)
# After cutting months with unique dates, get months remaining that now have a unique date.
const f2 = uniqueday(f1)
# filter for those of the finally remaining months that have only one date left.
const bday = filter(x -> count(m -> m[2] == x[2], f2) == 1, f2)[]
println("Cheryl's birthday is $(bday[2]) $(bday[1]).")
- Output:
Cheryl's birthday is July 16.
Kotlin
// Version 1.2.71
val months = listOf(
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
)
class Birthday(val month: Int, val day: Int) {
public override fun toString() = "${months[month - 1]} $day"
public fun monthUniqueIn(bds: List<Birthday>): Boolean {
return bds.count { this.month == it.month } == 1
}
public fun dayUniqueIn(bds: List<Birthday>): Boolean {
return bds.count { this.day == it.day } == 1
}
public fun monthWithUniqueDayIn(bds: List<Birthday>): Boolean {
return bds.any { (this.month == it.month) && it.dayUniqueIn(bds) }
}
}
fun main(args: Array<String>) {
val choices = listOf(
Birthday(5, 15), Birthday(5, 16), Birthday(5, 19), Birthday(6, 17),
Birthday(6, 18), Birthday(7, 14), Birthday(7, 16), Birthday(8, 14),
Birthday(8, 15), Birthday(8, 17)
)
// Albert knows the month but doesn't know the day.
// So the month can't be unique within the choices.
var filtered = choices.filterNot { it.monthUniqueIn(choices) }
// Albert also knows that Bernard doesn't know the answer.
// So the month can't have a unique day.
filtered = filtered.filterNot { it.monthWithUniqueDayIn(filtered) }
// Bernard now knows the answer.
// So the day must be unique within the remaining choices.
filtered = filtered.filter { it.dayUniqueIn(filtered) }
// Albert now knows the answer too.
// So the month must be unique within the remaining choices.
filtered = filtered.filter { it.monthUniqueIn(filtered) }
if (filtered.size == 1)
println("Cheryl's birthday is ${filtered[0]}")
else
println("Something went wrong!")
}
- Output:
Cheryl's birthday is July 16
Lua
-- Cheryl's Birthday in Lua 6/15/2020 db
local function Date(mon,day)
return { mon=mon, day=day, valid=true }
end
local choices = {
Date("May", 15), Date("May", 16), Date("May", 19),
Date("June", 17), Date("June", 18),
Date("July", 14), Date("July", 16),
Date("August", 14), Date("August", 15), Date("August", 17)
}
local function apply(t, f)
for k, v in ipairs(t) do
f(k, v)
end
end
local function filter(t, f)
local result = {}
for k, v in ipairs(t) do
if f(k, v) then
result[#result+1] = v
end
end
return result
end
local function map(t, f)
local result = {}
for k, v in ipairs(t) do
result[#result+1] = f(k, v)
end
return result
end
local function count(t) return #t end
local function isvalid(k, v) return v.valid end
local function invalidate(k, v) v.valid = false end
local function remaining() return filter(choices, isvalid) end
local function listValidChoices()
print(" " .. table.concat(map(remaining(), function(k, v) return v.mon .. " " .. v.day end), ", "))
print()
end
print("Cheryl offers these ten choices:")
listValidChoices()
print("1) Albert knows that Bernard also cannot yet know, so cannot be a month with a unique day, leaving:")
apply(remaining(), function(k, v)
if count(filter(choices, function(k2, v2) return v.day==v2.day end)) == 1 then
apply(filter(remaining(), function(k2, v2) return v.mon==v2.mon end), invalidate)
end
end)
listValidChoices()
print("2) After Albert's revelation, Bernard now knows, so day must be unique, leaving:")
apply(remaining(), function(k, v)
local subset = filter(remaining(), function(k2, v2) return v.day==v2.day end)
if count(subset) > 1 then apply(subset, invalidate) end
end)
listValidChoices()
print("3) After Bernard's revelation, Albert now knows, so month must be unique, leaving only:")
apply(remaining(), function(k, v)
local subset = filter(remaining(), function(k2, v2) return v.mon==v2.mon end)
if count(subset) > 1 then apply(subset, invalidate) end
end)
listValidChoices()
- Output:
Cheryl offers these ten choices: May 15, May 16, May 19, June 17, June 18, July 14, July 16, August 14, August 15, August 17 1) Albert knows that Bernard also cannot yet know, so cannot be a month with a unique day, leaving: July 14, July 16, August 14, August 15, August 17 2) After Albert's revelation, Bernard now knows, so day must be unique, leaving: July 16, August 15, August 17 3) After Bernard's revelation, Albert now knows, so month must be unique, leaving only: July 16
Mathematica / Wolfram Language
opts = Tuples[{{"May"}, {15, 16, 19}}]~Join~Tuples[{{"June"}, {17, 18}}]~Join~Tuples[{{"July"}, {14, 16}}]~Join~Tuples[{{"August"}, {14, 15, 17}}];
monthsdelete = Select[GatherBy[opts, Last], Length /* EqualTo[1]][[All, 1, 1]];
opts = DeleteCases[opts, {Alternatives @@ monthsdelete, _}]
removedates = Catenate@Select[GatherBy[opts, Last], Length /* GreaterThan[1]];
opts = DeleteCases[opts, Alternatives @@ removedates]
Select[GatherBy[opts, First], Length /* EqualTo[1]]
- Output:
{{"July", 14}, {"July", 16}, {"August", 14}, {"August", 15}, {"August", 17}} {{"July", 16}, {"August", 15}, {"August", 17}} {{{"July", 16}}}
Nim
import tables
import sets
import strformat
type Date = tuple[month: string, day: int]
const Dates = [Date ("May", 15), ("May", 16), ("May", 19), ("June", 17), ("June", 18),
("July", 14), ("July", 16), ("August", 14), ("August", 15), ("August", 17)]
const
MonthTable: Table[int, HashSet[string]] =
static:
var t: Table[int, HashSet[string]]
for date in Dates:
t.mgetOrPut(date.day, initHashSet[string]()).incl(date.month)
t
DayTable: Table[string, HashSet[int]] =
static:
var t: Table[string, HashSet[int]]
for date in Dates:
t.mgetOrPut(date.month, initHashSet[int]()).incl(date.day)
t
var possibleMonths: HashSet[string] # Set of possible months.
var possibleDays: HashSet[int] # Set of possible days.
# Albert: I don't know when Cheryl's birthday is, ...
# => eliminate months with a single possible day.
for month, days in DayTable.pairs:
if days.len > 1:
possibleMonths.incl(month)
# ... but I know that Bernard does not know too.
# => eliminate months with one day present only in this month.
for month, days in DayTable.pairs:
for day in days:
if MonthTable[day].len == 1:
possibleMonths.excl(month)
echo fmt"After first Albert's sentence, possible months are {possibleMonths}."
# Bernard: At first I don't know when Cheryl's birthday is, ...
# => eliminate days with a single possible month.
for day, months in MonthTable.pairs:
if months.len > 1:
possibleDays.incl(day)
# ... but I know now.
# => eliminate days which are compatible with several months in "possibleMonths".
var impossibleDays: HashSet[int] # Days which are eliminated by this sentence.
for day in possibleDays:
if (MonthTable[day] * possibleMonths).len > 1:
impossibleDays.incl(day)
possibleDays.excl(impossibleDays)
echo fmt"After Bernard's sentence, possible days are {possibleDays}."
# Albert: Then I also know when Cheryl's birthday is.
# => eliminate months which are compatible with several days in "possibleDays".
var impossibleMonths: HashSet[string] # Months which are eliminated by this sentence.
for month in possibleMonths:
if (DayTable[month] * possibleDays).len > 1:
impossibleMonths.incl(month)
possibleMonths.excl(impossibleMonths)
doAssert possibleMonths.len == 1
let month = possibleMonths.pop()
echo fmt"After second Albert's sentence, remaining month is {month}..."
possibleDays = possibleDays * DayTable[month]
doAssert possibleDays.len == 1
let day = possibleDays.pop()
echo fmt"and thus remaining day is {day}."
echo ""
echo fmt"So birthday date is {month} {day}."
- Output:
After first Albert's sentence, possible months are {"August", "July"}. After Bernard's sentence, possible days are {16, 17, 15}. After second Albert's sentence, remaining month is July... and thus remaining day is 16. So birthday date is July 16
Perl
sub filter {
my($test,@dates) = @_;
my(%M,%D,@filtered);
# analysis of potential birthdays, keyed by month and by day
for my $date (@dates) {
my($mon,$day) = split '-', $date;
$M{$mon}{cnt}++;
$D{$day}{cnt}++;
push @{$M{$mon}{day}}, $day;
push @{$D{$day}{mon}}, $mon;
push @{$M{$mon}{bday}}, "$mon-$day";
push @{$D{$day}{bday}}, "$mon-$day";
}
# eliminates May/Jun dates based on 18th and 19th being singletons
if ($test eq 'singleton') {
my %skip;
for my $day (grep { $D{$_}{cnt} == 1 } keys %D) { $skip{ @{$D{$day}{mon}}[0] }++ }
for my $mon (grep { ! $skip{$_} } keys %M) { push @filtered, @{$M{$mon}{bday}} }
# eliminates Jul/Aug 14th because day count > 1 across months
} elsif ($test eq 'duplicate') {
for my $day (grep { $D{$_}{cnt} == 1 } keys %D) { push @filtered, @{$D{$day}{bday}} }
# eliminates Aug 15th/17th because day count > 1, within month
} elsif ($test eq 'multiple') {
for my $day (grep { $M{$_}{cnt} == 1 } keys %M) { push @filtered, @{$M{$day}{bday}} }
}
return @filtered;
}
# doesn't matter what order singleton/duplicate tests are run, but 'multiple' must be last;
my @dates = qw<5-15 5-16 5-19 6-17 6-18 7-14 7-16 8-14 8-15 8-17>;
@dates = filter($_, @dates) for qw<singleton duplicate multiple>;
my @months = qw<_ January February March April May June July August September October November December>;
my ($m, $d) = split '-', $dates[0];
print "Cheryl's birthday is $months[$m] $d.\n";
- Output:
Cheryl's birthday is July 16.
Phix
-- demo\rosetta\Cheryls_Birthday.exw with javascript_semantics sequence choices = {{5, 15}, {5, 16}, {5, 19}, {6, 17}, {6, 18}, {7, 14}, {7, 16}, {8, 14}, {8, 15}, {8, 17}} sequence mwud = repeat(false,12) -- months with unique days for step=1 to 4 do sequence {months,days} = columnize(choices) bool impossible = false for i=length(choices) to 1 by -1 do integer {m,d} = choices[i] switch step do case 1: mwud[m] += (sum(sq_eq(days,d))=1) case 2: impossible = mwud[m] case 3: impossible = (sum(sq_eq(days,d))!=1) case 4: impossible = (sum(sq_eq(months,m))!=1) end switch if impossible then choices[i..i] = {} end if end for end for ?choices
Iterating backwards down the choices array simplifies element removal, or more accurately removes the need for "not increment i".
Step 1&2 is months with unique days, step 3 is days with unique months, step 4 is unique months.
- Output:
{{7,16}}
functional/filter
(this can also be found in demo\rosetta\Cheryls_Birthday.exw)
with javascript_semantics enum MONTH, DAY function unique_month(sequence si, months) return sum(sq_eq(months,si[MONTH]))=1 end function function unique_day(sequence si, days) return sum(sq_eq(days,si[DAY]))=1 end function function month_without_unique_day(sequence si, months_with_unique_day) return not find(si[MONTH],months_with_unique_day) end function sequence choices = {{5, 15}, {5, 16}, {5, 19}, {6, 17}, {6, 18}, {7, 14}, {7, 16}, {8, 14}, {8, 15}, {8, 17}} -- Albert knows the month but does not know the day. -- So the month cannot be unique within the choices. -- However this step would change nothing, hence omit it. -- (obvs. non_unique_month() would be as above, but !=1) --choices = filter(choices,non_unique_month,vslice(choices,MONTH)) -- Albert also knows that Bernard doesn't know the answer. -- So the month cannot have a unique day. sequence unique_days = filter(choices,unique_day,vslice(choices,DAY)) sequence months_with_unique_day = unique(vslice(unique_days,MONTH)) choices = filter(choices,month_without_unique_day,months_with_unique_day) -- Bernard now knows the answer. -- So the day must be unique within the remaining choices. choices = filter(choices,unique_day,vslice(choices,DAY)) -- Albert now knows the answer too. -- So the month must be unique within the remaining choices. choices = filter(choices,unique_month,vslice(choices,MONTH)) if length(choices)!=1 then crash("Something went wrong!") end if include builtins\timedate.e timedate td = repeat(0,6) {td[DT_MONTH],td[DT_DAY]} = choices[1] printf(1,"Cheryl's birthday is %s\n", {format_timedate(td,"Mmmm ddth")})
- Output:
Cheryl's birthday is July 16th
Python
Functional
'''Cheryl's Birthday'''
from itertools import groupby
from re import split
# main :: IO ()
def main():
'''Derivation of the date.'''
month, day = 0, 1
print(
# (3 :: A "Then I also know")
# (A's month contains only one remaining day)
uniquePairing(month)(
# (2 :: B "I know now")
# (B's day is paired with only one remaining month)
uniquePairing(day)(
# (1 :: A "I know that Bernard does not know")
# (A's month is not among those with unique days)
monthsWithUniqueDays(False)([
# 0 :: Cheryl's list:
tuple(x.split()) for x in
split(
', ',
'May 15, May 16, May 19, ' +
'June 17, June 18, ' +
'July 14, July 16, ' +
'Aug 14, Aug 15, Aug 17'
)
])
)
)
)
# ------------------- QUERY FUNCTIONS --------------------
# monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)]
def monthsWithUniqueDays(blnInclude):
'''The subset of months with (or without) unique days.
'''
def go(xs):
month, day = 0, 1
months = [fst(x) for x in uniquePairing(day)(xs)]
return [
md for md in xs
if blnInclude or not (md[month] in months)
]
return go
# uniquePairing :: DatePart -> [(Month, Day)] -> [(Month, Day)]
def uniquePairing(i):
'''Subset of months (or days) with a unique intersection.
'''
def go(xs):
def inner(md):
dct = md[i]
uniques = [
k for k in dct.keys()
if 1 == len(dct[k])
]
return [tpl for tpl in xs if tpl[i] in uniques]
return inner
return ap(bindPairs)(go)
# bindPairs :: [(Month, Day)] ->
# ((Dict String [String], Dict String [String])
# -> [(Month, Day)]) -> [(Month, Day)]
def bindPairs(xs):
'''List monad injection operator for lists
of (Month, Day) pairs.
'''
return lambda f: f(
(
dictFromPairs(xs),
dictFromPairs(
[(b, a) for (a, b) in xs]
)
)
)
# dictFromPairs :: [(Month, Day)] -> Dict Text [Text]
def dictFromPairs(xs):
'''A dictionary derived from a list of
month day pairs.
'''
return {
k: [snd(x) for x in m] for k, m in groupby(
sorted(xs, key=fst), key=fst
)
}
# ----------------------- GENERIC ------------------------
# ap :: (a -> b -> c) -> (a -> b) -> a -> c
def ap(f):
'''Applicative instance for functions.
'''
def go(g):
def fxgx(x):
return f(x)(
g(x)
)
return fxgx
return go
# fst :: (a, b) -> a
def fst(tpl):
'''First component of a pair.
'''
return tpl[0]
# snd :: (a, b) -> b
def snd(tpl):
'''Second component of a pair.
'''
return tpl[1]
if __name__ == '__main__':
main()
- Output:
[('July', '16')]
R
options <- dplyr::tibble(mon = rep(c("May", "June", "July", "August"),times = c(3,2,2,3)),
day = c(15, 16, 19, 17, 18, 14, 16, 14, 15, 17))
okMonths <- c()
# Albert's first clue - it is a month with no unique day
for (i in unique(options$mon)){
if(all(options$day[options$mon == i] %in% options$day[options$mon != i])) {okMonths <- c(okMonths, i)}
}
okDays <- c()
# Bernard's clue - it is a day that only occurs once in the remaining dates
for (i in unique(options$day)){
if(!all(options$mon[options$day == i] %in% options$mon[(options$mon %in% okMonths)])) {okDays <- c(okDays, i)}
}
remaining <- options[(options$mon %in% okMonths) & (options$day %in% okDays), ]
# Albert's second clue - must be a month with only one un-eliminated date
for(i in unique(remaining$mon)){
if(sum(remaining$mon == i) == 1) {print(remaining[remaining$mon == i,])}
}
- Output:
# A tibble: 1 x 2 mon day <chr> <dbl> 1 July 16
Racket
#lang racket
(define ((is x #:key [key identity]) y) (equal? (key x) (key y)))
(define albert first)
(define bernard second)
(define (((unique who) chs) date) (= 1 (count (is date #:key who) chs)))
(define (((unique-fix who-fix who) chs) date)
(ormap (conjoin (is date #:key who-fix) ((unique who) chs)) chs))
(define-syntax-rule (solve <chs> [<act> <arg>] ...)
(let* ([chs <chs>] [chs (<act> (<arg> chs) chs)] ...) chs))
(solve '((May 15) (May 16) (May 19) (June 17) (June 18)
(July 14) (July 16) (August 14) (August 15) (August 17))
;; Albert knows the month but doesn't know the day.
;; So the month can't be unique within the choices.
[filter-not (unique albert)]
;; Albert also knows that Bernard doesn't know the answer.
;; So the month can't have a unique day.
[filter-not (unique-fix albert bernard)]
;; Bernard now knows the answer.
;; So the day must be unique within the remaining choices.
[filter (unique bernard)]
;; Albert now knows the answer too.
;; So the month must be unique within the remaining choices
[filter (unique albert)])
- Output:
'((July 16))
Raku
(formerly Perl 6)
my @dates =
{ :15day, :5month },
{ :16day, :5month },
{ :19day, :5month },
{ :17day, :6month },
{ :18day, :6month },
{ :14day, :7month },
{ :16day, :7month },
{ :14day, :8month },
{ :15day, :8month },
{ :17day, :8month }
;
# Month can't have a unique day
my @filtered = @dates.grep(*.<month> != one(@dates.grep(*.<day> == one(@dates».<day>))».<month>));
# Day must be unique and unambiguous in remaining months
my $birthday = @filtered.grep(*.<day> == one(@filtered».<day>)).classify({.<month>})\
.first(*.value.elems == 1).value[0];
# convenience array
my @months = <'' January February March April May June July August September October November December>;
say "Cheryl's birthday is { @months[$birthday<month>] } {$birthday<day>}.";
- Output:
Cheryl's birthday is July 16.
REXX
/*REXX pgm finds Cheryl's birth date based on a person knowing the birth month, another */
/*──────────────────────── person knowing the birth day, given a list of possible dates.*/
$= 'May-15 May-16 May-19 June-17 June-18 July-14 July-16 August-14 August-15 August-17'
call delDays unique('day')
$= unique('day')
$= unique('month')
if words($)==1 then say "Cheryl's birthday is" translate($, , '-')
else say "error in the program's logic."
exit 0
/*──────────────────────────────────────────────────────────────────────────────────────*/
unique: arg u 2, dups; #= words($); $$= $
do j=# to 2 by -1
if u=='D' then parse value word($, j) with '-' x
else parse value word($, j) with x '-'
do k=1 for j-1
if u=='D' then parse value word($, k) with '-' y
else parse value word($, k) with y '-'
if x==y then dups= dups k j
end /*k*/
end /*j*/
do d=# for # by -1
do p=1 for words(dups) until ?==d; ?= word(dups,p)
if ?==d then $$= delword($$, ?, 1)
end /*d*/
end /*d*/
if words($$)==0 then return $
else return $$
/*──────────────────────────────────────────────────────────────────────────────────────*/
delDays: parse arg days; #= words(days)
do j=# for # by -1; parse value word(days, j) with x '-'; ##= words($)
do k=## for ## by -1; parse value word($, k) with y '-'
if x\==y then iterate; $= delword($, k, 1)
end /*k*/
end /*j*/
return $
- output when using the internal default input:
Cheryl's birthday is July 16
Ruby
dates = [
["May", 15],
["May", 16],
["May", 19],
["June", 17],
["June", 18],
["July", 14],
["July", 16],
["August", 14],
["August", 15],
["August", 17],
]
print dates.length, " remaining\n"
# the month cannot have a unique day
uniqueMonths = dates.group_by { |m,d| d }
.select { |k,v| v.size == 1 }
.map { |k,v| v.flatten }
.map { |m,d| m }
dates.delete_if { |m,d| uniqueMonths.include? m }
print dates.length, " remaining\n"
# the day must be unique
dates = dates .group_by { |m,d| d }
.select { |k,v| v.size == 1 }
.map { |k,v| v.flatten }
print dates.length, " remaining\n"
# the month must now be unique
dates = dates .group_by { |m,d| m }
.select { |k,v| v.size == 1 }
.map { |k,v| v }
.flatten
print dates
- Output:
10 remaining 5 remaining 3 remaining ["July", 16]
Rust
// This version is based on the Go version on Rosettacode
#[derive(PartialEq, Debug, Copy, Clone)]
enum Month {
May,
June,
July,
August,
}
#[derive(PartialEq, Debug, Copy, Clone)]
struct Birthday {
month: Month,
day: u8,
}
impl Birthday {
fn month_unique_in(&self, birthdays: &[Birthday]) -> bool {
birthdays
.iter()
.filter(|birthday| birthday.month == self.month)
.count()
== 1
}
fn day_unique_in(&self, birthdays: &[Birthday]) -> bool {
birthdays
.iter()
.filter(|birthday| birthday.day == self.day)
.count()
== 1
}
fn month_with_unique_day_in(&self, birthdays: &[Birthday]) -> bool {
birthdays
.iter()
.any(|birthday| self.month == birthday.month && birthday.day_unique_in(birthdays))
}
}
fn solution() -> Option<Birthday> {
let mut choices: Vec<Birthday> = vec![
Birthday {
month: Month::May,
day: 15,
},
Birthday {
month: Month::May,
day: 16,
},
Birthday {
month: Month::May,
day: 19,
},
Birthday {
month: Month::June,
day: 17,
},
Birthday {
month: Month::June,
day: 18,
},
Birthday {
month: Month::July,
day: 14,
},
Birthday {
month: Month::July,
day: 16,
},
Birthday {
month: Month::August,
day: 14,
},
Birthday {
month: Month::August,
day: 15,
},
Birthday {
month: Month::August,
day: 17,
},
];
// Albert knows the month but doesn't know the day.
// So the month can't be unique within the choices.
let choices_copy = choices.clone();
choices.retain(|birthday| !(&birthday.month_unique_in(&choices_copy)));
// Albert also knows that Bernard doesn't know the answer.
// So the month can't have a unique day.
let choices_copy = choices.clone();
choices.retain(|birthday| !(birthday.month_with_unique_day_in(&choices_copy)));
// Bernard now knows the answer.
// So the day must be unique within the remaining choices.
let choices_copy = choices.clone();
choices.retain(|birthday| birthday.day_unique_in(&choices_copy));
// Albert now knows the answer too.
// So the month must be unique within the remaining choices.
let choices_copy = choices.clone();
choices.retain(|birthday| birthday.month_unique_in(&choices_copy));
if choices.len() == 1 {
Some(choices[0])
} else {
None
}
}
fn main() {
match solution() {
Some(solution) => println!("Cheryl's birthday is {:?}", solution),
None => panic!("Didn't work!"),
}
}
- Output:
Cheryl's birthday is Birthday { month: July, day: 16 }
Scala
import java.time.format.DateTimeFormatter
import java.time.{LocalDate, Month}
object Cheryl {
def main(args: Array[String]): Unit = {
val choices = List(
LocalDate.of(2019, Month.MAY, 15),
LocalDate.of(2019, Month.MAY, 16),
LocalDate.of(2019, Month.MAY, 19),
LocalDate.of(2019, Month.JUNE, 17),
LocalDate.of(2019, Month.JUNE, 18),
LocalDate.of(2019, Month.JULY, 14),
LocalDate.of(2019, Month.JULY, 16),
LocalDate.of(2019, Month.AUGUST, 14),
LocalDate.of(2019, Month.AUGUST, 15),
LocalDate.of(2019, Month.AUGUST, 17)
)
// The month cannot have a unique day because Albert knows the month, and knows that Bernard does not know the answer
val uniqueMonths = choices.groupBy(_.getDayOfMonth)
.filter(a => a._2.length == 1)
.flatMap(a => a._2)
.map(a => a.getMonth)
val filter1 = choices.filterNot(a => uniqueMonths.exists(b => a.getMonth == b))
// Bernard now knows the answer, so the day must be unique within the remaining choices
val uniqueDays = filter1.groupBy(_.getDayOfMonth)
.filter(a => a._2.length == 1)
.flatMap(a => a._2)
.map(a => a.getDayOfMonth)
val filter2 = filter1.filter(a => uniqueDays.exists(b => a.getDayOfMonth == b))
// Albert knows the answer too, so the month must be unique within the remaining choices
val birthDay = filter2.groupBy(_.getMonth)
.filter(a => a._2.length == 1)
.flatMap(a => a._2)
.head
// print the result
printf(birthDay.format(DateTimeFormatter.ofPattern("MMMM dd")))
}
}
- Output:
July 16
Scala-ish approach
- Output:
See it yourself by running in your browser either by ScalaFiddle (ES aka JavaScript, non JVM) or Scastie (remote JVM).
object Cheryl_sBirthday extends App {
private val possiblerDates = Set(
Date("May", 15), Date("May", 16), Date("May", 19),
Date("June", 17), Date("June", 18),
Date("July", 14), Date("July", 16),
Date("August", 14), Date("August", 15), Date("August", 17)
)
private def clou3: Date = {
// Find the dates with ONE unique once and only occurrence of the day of the month.
def onceDates[K](toBeExcluded: Set[Date], selector: Date => K): Seq[Date] =
toBeExcluded.groupBy(selector).filter { case (_, multiSet) => multiSet.size == 1 }.values.flatten.toSeq
// 1) Albert tells us that Bernard doesn't know the answer,
// so we know the answer must be in months that does NOT have a same day of month.
val uniqueMonths = onceDates(possiblerDates, (date: Date) => date.dayOfMonth).map(_.month)
// Remove the dates with those months. The dates remain which has NOT those months.
val clou1 = possiblerDates.filterNot(p => uniqueMonths.contains(p.month))
// 2) Since Bernard now knows the answer, that tells us that the day MUST be unique among the remaining birthdays.
val uniqueDays = onceDates(clou1, (date: Date) => date.dayOfMonth).map(_.dayOfMonth)
// 3) Since Albert now knows the answer, that tells us the answer has to be unique by month.
// First, as the first parameter, intersect clou1 (Albert) with uniqueDays (Bernard)
onceDates(clou1.filter(date => uniqueDays.contains(date.dayOfMonth)), (date: Date) => date.month).head
}
case class Date(month: String, dayOfMonth: Int) {
override def toString: String = s"${"🎂 " * 3}$dayOfMonth $month${" 🎂" * 3}"
}
println(clou3)
}
Sidef
func f(day, month) {
Date.parse("#{day} #{month}", "%d %B")
}
var dates = [
f(15, "May"),
f(16, "May"),
f(19, "May"),
f(17, "June"),
f(18, "June"),
f(14, "July"),
f(16, "July"),
f(14, "August"),
f(15, "August"),
f(17, "August")
]
var filtered = dates.grep {
dates.grep {
dates.map{ .day }.count(.day) == 1
}.map{ .month }.count(.month) != 1
}
var birthday = filtered.grep {
filtered.map{ .day }.count(.day) == 1
}.group_by{ .month }.values.first_by { .len == 1 }[0]
say "Cheryl's birthday is #{birthday.fullmonth} #{birthday.day}."
- Output:
Cheryl's birthday is July 16.
Swift
struct MonthDay: CustomStringConvertible {
static let months = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
]
var month: Int
var day: Int
var description: String { "\(MonthDay.months[month - 1]) \(day)" }
private func isUniqueIn(months: [MonthDay], by prop: KeyPath<MonthDay, Int>) -> Bool {
return months.lazy.filter({ $0[keyPath: prop] == self[keyPath: prop] }).count == 1
}
func monthIsUniqueIn(months: [MonthDay]) -> Bool {
return isUniqueIn(months: months, by: \.month)
}
func dayIsUniqueIn(months: [MonthDay]) -> Bool {
return isUniqueIn(months: months, by: \.day)
}
func monthWithUniqueDayIn(months: [MonthDay]) -> Bool {
return months.firstIndex(where: { $0.month == month && $0.dayIsUniqueIn(months: months) }) != nil
}
}
let choices = [
MonthDay(month: 5, day: 15),
MonthDay(month: 5, day: 16),
MonthDay(month: 5, day: 19),
MonthDay(month: 6, day: 17),
MonthDay(month: 6, day: 18),
MonthDay(month: 7, day: 14),
MonthDay(month: 7, day: 16),
MonthDay(month: 8, day: 14),
MonthDay(month: 8, day: 15),
MonthDay(month: 8, day: 17)
]
// Albert knows the month, but not the day, so he doesn't have a gimmie month
let albertKnows = choices.filter({ !$0.monthIsUniqueIn(months: choices) })
// Albert also knows that Bernard doesn't know, so it can't be a gimmie day
let bernardKnows = albertKnows.filter({ !$0.monthWithUniqueDayIn(months: albertKnows) })
// Bernard now knows the birthday, so it must be a unique day within the remaining choices
let bernardKnowsMore = bernardKnows.filter({ $0.dayIsUniqueIn(months: bernardKnows) })
// Albert knows the birthday now, so it must be a unique month within the remaining choices
guard let birthday = bernardKnowsMore.filter({ $0.monthIsUniqueIn(months: bernardKnowsMore) }).first else {
fatalError()
}
print("Cheryl's birthday is \(birthday)")
- Output:
Cheryl's birthday is July 16
uBasic/4tH
Dim @d(30)
Dim @m(13)
Push 5,15,1, 5,16,1, 5,19,1, 6,17,1 ,6,18,1, 7,14,1, 7,16,1, 8,14,1, 8,15,1, 8,17,1
For x = 29 To 0 Step -1 : @d(x) = Pop() : Next
Push "ERR", "Jan", "Feb", "Mar", "Apr", "May"
Push "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
For x = 12 To 0 Step -1 : @m(x) = Pop() : Next
Proc _printRemaining ' the month cannot have a unique day
Proc _firstPass
Proc _printRemaining ' the day must now be unique
Proc _secondPass
Proc _printRemaining ' the month must now be unique
Proc _thirdPass
Proc _printAnswer
End
_printRemaining
Local (2)
b@ = 0
For a@ = 0 To 29 Step 3
If @d(a@+2) Then b@ = b@ + 1
Next
Print b@; " remaining."
Return
_printAnswer
Local (1)
For a@ = 0 To 29 Step 3
If @d(a@+2) Then Print Show (@m(@d(a@))); ", "; @d(a@+1)
Next
Return
_firstPass ' the month cannot have a unique day
Local (3)
For a@ = 0 To 29 Step 3
c@ = 0
For b@ = 0 To 29 Step 3
If @d(b@+1) = @d(a@+1) Then c@ = c@ + 1
Next
If c@ = 1 Then
For b@ = 0 To 29 Step 3
If @d(b@+2) = 0 Then
Continue
EndIf
If @d(b@) = @d(a@) Then
@d(b@+2) = 0
EndIf
Next
EndIf
Next
Return
_secondPass ' the day must now be unique
Local (3)
For a@ = 0 To 29 Step 3
If @d(a@+2) = 0 Then Continue
c@ = 0
For b@ = 0 To 29 Step 3
If @d(b@+2) = 0 Then Continue
If @d(b@+1) = @d(a@+1) Then c@ = c@ + 1
Next
If c@ > 1 Then
For b@ = 0 To 29 Step 3
If @d(b@+2) = 0 Then
Continue
EndIf
If @d(b@+1) = @d(a@+1) Then
@d(b@+2) = 0
EndIf
Next
EndIf
Next
Return
_thirdPass ' the month must now be unique
Local (3)
For a@ = 0 To 29 Step 3
If @d(a@+2) = 0 Then Continue
c@ = 0
For b@ = 0 To 29 Step 3
If @d(b@+2) = 0 Then Continue
If @d(b@) = @d(a@) Then c@ = c@ + 1
Next
If c@ > 1 Then
For b@ = 0 To 29 Step 3
If @d(b@+2) = 0 Then
Continue
EndIf
If @d(b@) = @d(a@) Then
@d(b@+2) = 0
EndIf
Next
EndIf
Next
Return
- Output:
10 remaining. 5 remaining. 3 remaining. Jul, 16 0 OK, 0:657
VBA
Private Sub exclude_unique_days(w As Collection)
Dim number_of_dates(31) As Integer
Dim months_to_exclude As New Collection
For Each v In w
number_of_dates(v(1)) = number_of_dates(v(1)) + 1
Next v
For i = w.Count To 1 Step -1
If number_of_dates(w(i)(1)) = 1 Then
months_to_exclude.Add w(i)(0)
w.Remove i
End If
Next i
For Each m In months_to_exclude
exclude_month w, m
Next m
End Sub
Private Sub exclude_month(x As Collection, v As Variant)
For i = x.Count To 1 Step -1
If x(i)(0) = v Then x.Remove i
Next i
End Sub
Private Sub exclude_non_unique_days(w As Collection)
Dim number_of_dates(31) As Integer
For Each v In w
number_of_dates(v(1)) = number_of_dates(v(1)) + 1
Next v
For i = w.Count To 1 Step -1
If number_of_dates(w(i)(1)) > 1 Then
w.Remove i
End If
Next i
End Sub
Private Sub exclude_non_unique_months(w As Collection)
Dim months As New Collection
For Each v In w
On Error GoTo 1
months.Add v(0), v(0)
Next v
1:
For i = w.Count To 1 Step -1
If w(i)(0) = v(0) Then
w.Remove i
End If
Next i
End Sub
Public Sub cherylsbirthday()
Dim v As New Collection
s = "May 15, May 16, May 19, June 17, June 18, July 14, July 16, August 14, August 15, August 17"
t = Split(s, ",")
For Each u In t
v.Add Split(Trim(u), " ")
Next u
'1) Albert: I don't know when Cheryl's birthday is, but I know that Bernard does not know too.
exclude_unique_days v
'2) Bernard: At first I don't know when Cheryl's birthday is, but I know now.
exclude_non_unique_days v
'3) Albert: Then I also know when Cheryl's birthday is.
exclude_non_unique_months v
Debug.Print v(1)(0); " "; v(1)(1)
End Sub
- Output:
July 16
Visual Basic .NET
Module Module1
Structure MonDay
Dim month As String
Dim day As Integer
Sub New(m As String, d As Integer)
month = m
day = d
End Sub
Public Overrides Function ToString() As String
Return String.Format("({0}, {1})", month, day)
End Function
End Structure
Sub Main()
Dim dates = New HashSet(Of MonDay) From {
New MonDay("May", 15),
New MonDay("May", 16),
New MonDay("May", 19),
New MonDay("June", 17),
New MonDay("June", 18),
New MonDay("July", 14),
New MonDay("July", 16),
New MonDay("August", 14),
New MonDay("August", 15),
New MonDay("August", 17)
}
Console.WriteLine("{0} remaining.", dates.Count)
' The month cannot have a unique day.
Dim monthsWithUniqueDays = dates.GroupBy(Function(d) d.day).Where(Function(g) g.Count() = 1).Select(Function(g) g.First().month).ToHashSet()
dates.RemoveWhere(Function(d) monthsWithUniqueDays.Contains(d.month))
Console.WriteLine("{0} remaining.", dates.Count)
' The day must now be unique.
dates.IntersectWith(dates.GroupBy(Function(d) d.day).Where(Function(g) g.Count() = 1).Select(Function(g) g.First()))
Console.WriteLine("{0} remaining.", dates.Count)
' The month must now be unique.
dates.IntersectWith(dates.GroupBy(Function(d) d.month).Where(Function(g) g.Count() = 1).Select(Function(g) g.First()))
Console.WriteLine(dates.Single())
End Sub
End Module
- Output:
10 remaining. 5 remaining. 3 remaining. (July, 16)
V (Vlang)
import time
struct Birthday {
month int
day int
}
fn (b Birthday) str() string {
return "${time.long_months[b.month-1]} $b.day"
}
fn (b Birthday) month_uniquie_in(bds []Birthday) bool {
mut count := 0
for bd in bds {
if bd.month == b.month {
count++
}
}
if count == 1 {
return true
}
return false
}
fn (b Birthday) day_unique_in(bds []Birthday) bool {
mut count := 0
for bd in bds {
if bd.day == b.day {
count++
}
}
if count == 1 {
return true
}
return false
}
fn (b Birthday) month_with_unique_day_in(bds []Birthday) bool {
for bd in bds {
if bd.month == b.month && bd.day_unique_in(bds) {
return true
}
}
return false
}
fn main() {
choices := [
Birthday{5, 15}, Birthday{5, 16}, Birthday{5, 19}, Birthday{6, 17}, Birthday{6, 18},
Birthday{7, 14}, Birthday{7, 16}, Birthday{8, 14}, Birthday{8, 15}, Birthday{8, 17},
]
// Albert knows the month but doesn't know the day.
// So the month can't be unique within the choices.
mut filtered := []Birthday{}
for bd in choices {
if !bd.month_uniquie_in(choices) {
filtered << bd
}
}
// Albert also knows that Bernard doesn't know the answer.
// So the month can't have a unique day.
mut filtered2 := []Birthday{}
for bd in filtered {
if !bd.month_with_unique_day_in(filtered) {
filtered2 << bd
}
}
// Bernard now knows the answer.
// So the day must be unique within the remaining choices.
mut filtered3 := []Birthday{}
for bd in filtered2 {
if bd.day_unique_in(filtered2) {
filtered3 << bd
}
}
// Albert now knows the answer too.
// So the month must be unique within the remaining choices.
mut filtered4 := []Birthday{}
for bd in filtered3 {
if bd.month_uniquie_in(filtered3) {
filtered4 << bd
}
}
if filtered4.len == 1 {
println("Cheryl's Birthday is ${filtered4[0]}")
} else {
println("Something went wrong!")
}
}
- Output:
Cheryl's birthday is July 16
Wren
var Months = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
]
class Birthday {
construct new(month, day) {
_month = month
_day = day
}
month { _month }
day { _day }
toString { "%(Months[_month-1]) %(day)" }
monthUniqueIn(bds) { bds.count { |bd| _month == bd.month } == 1 }
dayUniqueIn(bds) { bds.count { |bd| _day == bd.day } == 1 }
monthWithUniqueDayIn(bds) { bds.any { |bd| (_month == bd.month) && bd.dayUniqueIn(bds) } }
}
var choices = [
Birthday.new(5, 15), Birthday.new(5, 16), Birthday.new(5, 19), Birthday.new(6, 17),
Birthday.new(6, 18), Birthday.new(7, 14), Birthday.new(7, 16), Birthday.new(8, 14),
Birthday.new(8, 15), Birthday.new(8, 17)
]
// Albert knows the month but doesn't know the day.
// So the month can't be unique within the choices.
var filtered = choices.where { |bd| !bd.monthUniqueIn(choices) }.toList
// Albert also knows that Bernard doesn't know the answer.
// So the month can't have a unique day.
filtered = filtered.where { |bd| !bd.monthWithUniqueDayIn(filtered) }.toList
// Bernard now knows the answer.
// So the day must be unique within the remaining choices.
filtered = filtered.where { |bd| bd.dayUniqueIn(filtered) }.toList
// Albert now knows the answer too.
// So the month must be unique within the remaining choices.
filtered = filtered.where { |bd| bd.monthUniqueIn(filtered) }.toList
if (filtered.count == 1) {
System.print("Cheryl's birthday is %(filtered[0])")
} else {
System.print("Something went wrong!")
}
- Output:
Cheryl's birthday is July 16
zkl
dates:=T(T("May", 15), T("May", 16), T("May", 19),
T("June", 17), T("June", 18),
T("July", 14), T("July", 16),
T("August",14), T("August",15), T("August",17) );
mDs:=dates.pump(Dictionary().appendKV); // "June":(15,16,19), ...
dMs:=dates.pump(Dictionary().appendKV,"reverse"); // 15:"May", 16:"May", 19:"May", ...
// remove unique days (18,19) --> "July":(14,16),"August":(14,15,17)
dMs.values.apply2('wrap(ms){ if(ms.len()==1) mDs.del(ms[0]) });
// find intersection of above days --> (14)
fcn intersection(l1,l2){ l1.pump(List,l2.holds,'==(True),Void.Filter) }
badDs:=mDs.values.reduce(intersection);
// --> July:(16),August:(15,17) --> ( ("July",(16)) )
theDay:=mDs.filter('wrap([(m,ds)]){ ds.removeEach(badDs).len()==1 });
// print birthday such that muliples are shown, if any
println("Cheryl's birthday is ",theDay.flatten().flatten().concat(" "));
- Output:
Cheryl's birthday is July 16
- Programming Tasks
- Solutions by Programming Task
- 11l
- Ada
- ALGOL 68
- AppleScript
- Arturo
- AutoHotkey
- AWK
- C
- C sharp
- C++
- Common Lisp
- D
- F Sharp
- Factor
- FreeBASIC
- Go
- Fortran
- Groovy
- Haskell
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Lua
- Mathematica
- Wolfram Language
- Nim
- Perl
- Phix
- Python
- R
- Dplyr
- Racket
- Raku
- REXX
- Ruby
- Rust
- Scala
- Scala Math Puzzle
- Scala Scala-ish
- Scala Idiomatic
- Scala Type parameters
- ScalaFiddle qualified
- Scastie qualified
- Sidef
- Swift
- UBasic/4tH
- VBA
- Visual Basic .NET
- V (Vlang)
- Wren
- Zkl