Написание циклов для интерактивного ввода-вывода: проблемы с do-notation и layout
Я только что начал Haskell, и это полностью смущает меня. Я делал Java и Python, перед которым было намного больше смысла для меня.
В настоящее время я пытаюсь получить пользовательский ввод, проверьте, действительно ли он, если нет: напечатайте ошибку и снова введите этот ввод; если он действителен: выведите из него булевское значение.
Чтобы быть более точным, мне нужен вход yes/no, где "y" будет выдавать True, "n" будет выдавать False, а любой другой вход будет печатать сообщение в командной строке и запрашивать ввод y/n еще раз.
например:.
Continue? y/n:
> assd
Invalid input.
Continue? y/n:
> y
(something happens)
Continue? y/n:
> n
(Close program)
Записывая его в семейном формате, выполните следующие действия:
boolean inputBool() {
while(True) {
str = input("Continue? y/n: ");
if (str == "y") {
return True;
} else if (str == "n") {
return False;
} else {
print("Invalid input");
}
}
--main program--
while(inputBool()) {
doSomething();
}
Поскольку я только начал haskell (сегодня на самом деле), у меня нет большой идеи, что я могу или не могу сделать.
Я думал о чем-то подобном:
yesno :: Bool
yesno = do
putStr "Continue? y/n: "
str <- readLn
if (str == "y") then True else (
if (str == "n") then False else (
do
putStrLn "Invalid input."
yesno
)
)
Это не работает по многим причинам. Моя основная проблема заключается в том, что я не знаю, что это делает. Я просто прочитал его, когда он нуждается в операциях io, и каким-то образом выполняет следующие выражения. Что не имеет смысла для меня после прочтения, что в haskell все оценивает значение. Что делает "делать"?
Кроме того, какой отступ ожидается? Кажется, это случайный случай.
Я знаю, что функция должна оценивать мое логическое значение, которое не представляется возможным при использовании этой операции "делать". Но тогда как я что-то печатаю на консоли и все еще делаю это частью выражения, которое оценивается как True или False?
Спасибо за любую помощь.
(Кстати, есть ли в Интернете активные форумы haskell? Я не нашел ни одного =/)
Ответы
Ответ 1
Поскольку ваша функция должна выполнять IO, она должна возвращать значение в монаде IO
, поэтому ее тип должен быть yesno :: IO Bool
, а значения True
и False
должны быть подняты в монаду на return
. Вам также нужно getLine
вместо readLn
, так как вам нужна необработанная строка, а не разобранное значение (которое имеет потенциал для ошибки синтаксического анализа). Наконец, вам нужно hFlush stdout
перед вызовом getLine
, в противном случае приглашение все равно будет сидеть в буфере, ожидающем новой строки (и для этого требуется, чтобы вы импортировали System.IO).
Я ввел строку приглашения в аргумент, поэтому вы можете использовать эту функцию, чтобы задать все вопросы о да/нет:
import System.IO
yesno :: String -> IO Bool
yesno prompt = do
putStr $ prompt ++ " y/n: "
hFlush stdout
str <- getLine
case str of
"y" -> return True
"n" -> return False
_ -> do
putStrLn "Invalid input."
yesno prompt
Затем вы можете вызвать свою функцию из цикла взаимодействия:
main :: IO ()
main = do
fun <- yesno "Is Haskell fun?"
if fun
then putStrLn "rock on"
else putStrLn "read more tutorials"
continue <- yesno "Continue?"
if continue then main else return ()