Haskell - Ветвь внутри рекурсивного l oop выполняет все ранее посещенные ветки в дополнение к выбранной - PullRequest
3 голосов
/ 20 февраля 2020

Я пытаюсь научиться чему-то Haskell, но столкнулся с проблемой, я понятия не имею, как вообще объяснить. Проблемным моментом является некоторый код для обработки, позволяющий пользователю выбирать порядок посещений пунктов меню и изменять их так часто, как они хотят, до подтверждения. Первый выбор проходит гладко, и последующие выборки той же опции тоже делают, но как только будет сделан другой выбор, каждый l oop с этой точки, независимо от того, введен ли 1 или 2, выполнение будет go вниз обе опции в функции modeSelectParse (см. код ниже). Эти ветви являются единственными вызовами либо collectSettings , либо mart в программе, и такое поведение сохраняется, даже когда они оба были заглушены просто оператором print.

Когда я добавил больше веток, все ранее посещенные ветви запускались один раз на каждой итерации, в фиксированном порядке все время, независимо от того, как я перетасовывал порядок ветвей в modeSelectParse , или какое число соответствует какому варианту.

Я подозревал, что буферизация стандартного ввода-вывода была ошибочной, поэтому я попытался отключить это, и различные другие тесты на входе, в который я вошел. Но ничего не показалось в данных, когда я пытался распечатывать его в разных точках программы.

Первоначальный вызов modeSelectInit также находится не там, где он может когда-либо вызываться более одного раза.

Я могу дать больше объяснений и код при необходимости.

modeSelectInit :: DatabaseStruct -> IO (IO Settings, IO Order)
modeSelectInit db = do
  putStrLn "A lot of text"
  hFlush stdout

  let select = (return emptySettings, return [])
  input <- awaitChar ['1', '2']
  modeSelect db (modeSelectParse db input select)

modeSelect :: DatabaseStruct -> (IO Settings, IO Order) -> IO (IO Settings, IO Order)
modeSelect db select = do
  settings <- fst select
  order <- snd select

  putStrLn "A lot of text"
  hFlush stdout

  input <- awaitChar ['1', '2', '3']

  if input == '3'
  then return select
  else modeSelect (modeSelectParse db input select)

modeSelectParse :: DatabaseStruct -> Char -> (IO Settings, IO Order) -> (IO Settings, IO Order)
modeSelectParse db '1' (_, o) = (gatherSettings, o)
modeSelectParse db '2' (s, _) = (s, mart db)
awaitChar :: [Char] -> IO Char
awaitChar chars = do
  input <- getLine
  awaitChar2 input chars

awaitChar2 :: String -> [Char] -> IO Char
awaitChar2 [] chars = awaitChar chars
awaitChar2 (x: _) chars = ifThenElse (x `elem` chars) (return x) (awaitChar chars)

1 Ответ

3 голосов
/ 20 февраля 2020

И передача IO действий в качестве аргументов, и возврат вложенных IO действий очень необычны. (Есть, конечно, ситуации, когда это именно то, что нужно, но это не в 99% от стандартного, обычного, повседневного Haskell.) Я подозреваю, что просто устранение этой странности позаботится о твоя проблема. Поэтому попробуйте вместо этого использовать следующие сигнатуры типов:

modeSelectInit :: DatabaseStruct -> IO (Settings, Order)
modeSelect :: DatabaseStruct -> (Settings, Order) -> IO (Settings, Order)
modeSelectParse :: DatabaseStruct -> Char -> (Settings, Order) -> IO (Settings, Order)

Их реализации придется немного подправить, чтобы компенсировать это, но из того, что я вижу здесь, я подозреваю, что вы примерно знаете, как это сделать. (Но если вам тяжело, тогда определенно скажите - многие здесь с удовольствием скажут, как именно, если вы чувствуете себя застрявшим!)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...