(Это не дает прямого ответа на вопрос, но сделает ваш код более идиоматичным и, следовательно, более легким для чтения.)
Вы часто используете шаблон \x -> f x >>= ...
, это может (и должно быть)) быть исключенным: это (в основном) ненужный шум, который затемняет смысл кода.Я не буду использовать ваш код, так как это домашнее задание, но рассмотрим этот пример (обратите внимание, что я использую return
, как подсказывает другой ответ):
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (lines str) >>=
\lns -> return (length lns) >>=
\num -> print num
(он читает имя файла изпользователь, а затем печатает количество строк в этом файле.)
Самая простая оптимизация - это раздел, в котором мы подсчитываем количество строк (это соответствует той части, где вы разделяете слова и получаете второеодин): количество строк в строке str
равно length (lines str)
(что совпадает с length . lines $ str
), поэтому у нас нет причин для вызова length
и вызова lines
отдельный.Наш код теперь:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (length . lines $ str) >>=
\num -> print num
Теперь следующая оптимизация на \num -> print num
.Это можно записать как print
.(Это называется эта конвертация ).(Вы можете думать об этом как о «функции, которая принимает аргумент и вызывает print
для него, аналогично самой print
»).Теперь у нас есть:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (length . lines $ str) >>= print
Следующая оптимизация, которую мы можем сделать, основана на законах монады .Используя первый, мы можем превратить return (length . lines $ str) >>= print
в print (length . lines $ str)
(т. Е. «Создать контейнер, содержащий значение (это делается с помощью return
), а затем передать это значение в print
так же, как просто передатьзначение до print
").Опять же, мы можем удалить скобки, поэтому мы имеем:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> print . length . lines $ str
И посмотрите!У нас есть eta-преобразование, которое мы можем сделать: \str -> print . length . lines $ str
становится просто print . length . lines
.Это оставляет:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>= print . length . lines
На этом этапе мы, вероятно, можем остановиться, так как это выражение намного проще, чем наше первоначальное (мы могли бы продолжать, используя >=>
, если мыхотеть).Поскольку это намного проще, его также легче отлаживать (представьте, если бы мы забыли использовать lines
: в оригинальном main
это было бы не очень понятно, в последнем это очевидно.)
В вашем коде вы можете и должны делать то же самое: вы можете использовать такие вещи, как section (что означает \x -> x !! 1
, то же самое, что (!! 1)
), а также законы eta-преобразования и монады Iиспользуется выше.