Сегодня я хотел бы подвести итоги под результатами августовского конкурса по функциональному программированию, который ежемесячно проводится под эгидой ФП(ФП). На этот раз в качестве задачи участникам была предложена несколько переформулированная задача из книги Р. М. Смаллиана «Алиса в стране смекалки»: Тройка думает, что Туз не в своём уме. Четвёрка думает, что Тройка и Двойка обе не могут быть не в своём уме. Пятёрка думает, что Туз и Четвёрка либо оба не в своём уме, либо оба в своём уме. Шестёрка думает, что Туз и Двойка оба в своём уме. Семёрка думает, что Пятёрка не в своём уме. Валет думает, что Шестёрка и Семёрка обе не могут быть не в своём уме. В своём ли уме Валет?Как видно, это задача на формальную логику. Несмотря на то, что некоторые участники применили интуиционистский подход к решению, задача решается в простейшей булевой логике, причём результат не отличается от того, что получается в интуиционистской логике. В любом случае, на этот раз решение оценивалось с точки зрения его выразительности, и мне хотелось, чтобы участники показали своё умение в оформлении программ в виде текста задачи. К сожалению, этого никто не сделал, поэтому далее будет показана программа на языке Haskell, которая решает задачу и выводит ответ на экран, причём написана она в очень своеобразном стиле, который многим может показаться негодным.
(X || not X), поскольку при любом значении X (которое может быть True или False соответственно), эта формула истинна. Соответственно, тождественно ложной формулой называется такая, которая принимает значение «ЛОЖЬ» при любых значениях входящих в неё переменных. Например, формула (X && not X) является тождественно ложной.X && Y не является ни тождественно истинной, ни тождественно ложной, потому как может принимать значения как «ИСТИНА», так и «ЛОЖЬ» в зависимости от значений своих переменных.валет туз двойка = думает что (шестёрка туз двойка) и (семёрка туз двойка) обеНеМогутБытьНеВСвоёмУме
do, в которой можно было описать всё, что необходимо, и именно в той форме, в которой хотелось. В итоге на свет появилось такое определение:вСвоёмЛиУмеВалет = выводРезультата $
do туз <- варианты душевного состояния
двойка <- варианты душевного состояния
let тройка = думает что туз неВСвоёмУме
четвёрка = думaeт что тройка и двойка обеНеМогутБытьНеВСвоёмУме
пятёрка = думaeт что туз и четвёрка либоОбаНеВСвоёмУмеЛибоОбаВСвоёмУме
шестёрка = думaeт что туз и двойка обаВСвоёмУме
семёрка = думает что пятёрка неВСвоёмУме
валет = думaeт что шестёрка и семёрка обеНеМогутБытьНеВСвоёмУме
return валет
что, неВСвоёмУме, и и т. д. Интуиция сразу подсказывает, что эти комбинаторы частью будут «заглушками», которые никогда использоваться не будут, а частью будут просто синонимами для уже имеющихся стандартных функций. Ну а во-вторых, проницательный читатель уже заметил, что в этом определении терм думает используется двумя различными способами — использования для тройки и семёрки отличается от использования для остальных субъектов. Что это? Разное количество аргументов при одинаковом типе результата свидетельствует о том, что это две разных функции с разными сигнатурами типов. Как такое может быть?думает все символы кириллические, а в другом — буква e взята из латинского алфавита. Да, это ужасно-ужасно-ужасно! Это более, чем ужасно. Это невозможно. За это надо, как минимум, бить по рукам, ведь такая программа получается абсолютно неподдерживаемой. Но, поскольку в данном случае мы имеем «программу одного запуска», то можно. Хоть и выглядит катастрофично.выводРезультата является служебной и необходима только для вывода на экран заключения о душевном состоянии валета. Она написана крайне конкретно:выводРезультата x | and x = putStrLn "Валет в своём уме."
| all not x = putStrLn "Валет не в своём уме."
| otherwise = putStrLn "Невозможно сделать вывод о душевном состоянии валета."
x. Во втором охранном выражении, соответственно, — на тождественную ложность то же формулы. Ну а последнее охранное выражение соответствует тому, что формула x не является ни общезначимой, ни тождественно ложной. Здесь, конечно, опять можно было бы переопределить все эти стандартные функции and, all и т. д., но этого можно и не делать, поскольку эта функция не является описанием задачи. Её саму уже даже можно было не называть по-русски.[], то есть в определении функции вСвоёмЛиУмеВалет нотация do относится к спискам. Это значит, что последнее выражение в do вернёт список, в котором в данном случае должно быть ровно 4 элемента — по одному элементу для рассчитанного значения душевного состояния валета на 4 возможных комбинации душевных состояний туза и двойки. Как видно в определении, термы туз и двойка определяются через генерацию из элементов списка, и из определения функции варианты будет ясно, что и туз, и двойка последовательно принимают значения от «ИСТИНА» до «ЛОЖЬ».вСвоёмУме = True
и = (&&)
или = (||)
не = not
неВСвоёмУме = not
либоОбаНеВСвоёмУмеЛибоОбаВСвоёмУме = (==)
обеНеМогутБытьНеВСвоёмУме = или
обаВСвоёмУме = и
думает _ x f = f x
думaeт _ x _ y f = f x y
что = undefined
душевного = undefined
состояния = undefined
undefined), используются только лишь в качестве декораций для приведения программы в соответствие с условием задачи. Ну а два ужасных комбинатора с одинаково-разными названиями думает и думaeт являются именно комбинаторами в точном смысле — они комбинируют свои аргументы.вСвоёмЛиУмеВалет отлично решает поставленную задачу и выводит на экран удивлённому пользователю ответ:Валет в своём уме.
guard из модуля Control.Monad, даже, возможно, определив для неё синоним в виде терма думает. Но решение этой задачи я оставляю вдумчивому читателю. Кто заинтересован — прошу писать в комментариях. Это действительно несложно.либоОбаНеВСвоёмУмеЛибоОбаВСвоёмУме непосредственно на слова русского языка, разделённые пробелами.Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.
комментарии (12)