Задача haskell: строка ввода -> [int] - PullRequest
7 голосов
/ 23 февраля 2011

Здравствуйте, отличные программисты,

Я делаю свои первые шаги в haskell и имею функцию, которая меня смущает:

import Data.List.Split
getncheck_guesslist = do
    line <- getLine
    let tmp = splitOneOf ",;" line
    map read tmp::[Int]

splitOneOf в Data.List.Split (я установил его с помощью cabal install split) splitOneOf :: (Eq a)=> [a]->[a]->[[a]]

Из ошибки я понял, что есть некоторая некорректность типа - но не знаю, как решить этот конфликт, так как IO по-прежнемудля меня загадка

Я хочу прочитать ввод целых чисел, разделенных запятыми или точками с запятой, и получить список целых чисел так:

  • как я могу проверить, имеет ли пользовательский ввод типInt
  • как я могу "перевести" ввод, который имеет тип "IO String", в [Int]

Заранее спасибо за мысли и подсказки - ваши ε / 2

Ответы [ 4 ]

12 голосов
/ 23 февраля 2011

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

Это означает, что вместо возврата значения с типом[Int], вы должны что-то вернуть с типом IO [Int].Для этого вы используете функцию return, которая «оборачивает» значение в IO (на самом деле это работает для любой монады).

Просто измените последнюю строку наоберните ваше значение с помощью return, например:

getncheck_guesslist = do
    line <- getLine
    let tmp = splitOneOf ",;" line
    return (map read tmp :: [Int])
7 голосов
/ 23 февраля 2011

«Правильный способ» сделать это в Haskell - это отделить IO от, ну, от всего остального.Прямой перевод вашего кода будет выглядеть так:

getncheck_guesslist :: IO [Int]
getncheck_guesslist = do line <- getLine               -- get
                         return (check_guesslist line) -- check

check_guesslist :: String -> [Int]
check_guesslist line = let tmp = splitOneOf ",;" line
                       in map read tmp

Обратите внимание, что getncheck_guesslist - это просто IO-действие.Функция не имеет входных параметров , хотя для этого требуется (IO) ввод с getLine.

Также обратите внимание, что getncheck_guesslist - это простоемодификация действия getLine IO.Разве нет комбинатора, который позволил бы мне выдвинуть функцию, которая воздействует на значение внутри монады?Стоп.Hoogle time!

У меня есть функция (a -> b).У меня есть значение типа ввода, но оно застряло в монаде m a.Я хочу выполнить функцию внутри монады, поэтому результат неизбежно застрянет и в монаде m b.Собирая все это вместе, мы hoogle (a -> b) -> m a -> m b.И вот, fmap - это как раз то, что мы искали.

get_guesslist = check_guesslist `fmap` getLine
-- or, taking it a step further
get_guesslist = (map read . splitOneOf ",;") `fmap` getLine :: IO [Int]

В заключение: всякий раз, когда вы кодируете метод с именем, например somethingAndSomethingElse, обычно лучше писать стиль кодирования ивызовите something и somethingElse как два отдельных метода.Для окончательных версий я просто переименовал его get_guesslist, так как концептуально это то, что он делает.Он получает предположения в виде списка Ints.

В качестве заключительной заключительной ноты, я остановился в точке, где началось barsoap.;) fmap совпадает с <$>.

2 голосов
/ 23 февраля 2011
import Data.List.Split
import Control.Applicative

getncheck_guesslist :: IO [Int]
getncheck_guesslist = map read . splitOneOf ",;" <$> getLine

Каждый раз, когда код сводится к foo >>= return . bar, что вы делаете, отлаживая do-блок (и исправляя ошибку типа), вы не используете монадические функции своей монады, а только ее функторную часть, то есть прямо скажем, вы не возитесь с IO частью шрифта, а с a из IO a:

(<$>) :: (Functor f) => (a -> b) -> f a -> f b

(fmap будет неинфиксным именем для <$>. Оба тесно связаны с map.)

Этот код выше идиоматичен для специального кода, но чистый код будет выглядеть как

import Data.Maybe

maybeRead :: Read a => String -> Maybe a
maybeRead = fmap fst . listToMaybe . reads 
                               -- That fmap is using "instance Functor Maybe"

parseGuessList :: String -> [Int]
parseGuessList =  catMaybes . map maybeRead . splitOneOf ",;"

getncheck_guesslist = parseGuessList <$> getLine

или, альтернативно, если вы не хотите игнорировать ввод не-int, но выводить ошибку,

parseGuessList xs = if success then Just . catMaybes $ ys else Nothing 
  where ys :: String -> [Mabye Int]
        ys = map maybeRead . splitOneOf ",;" $ xs
        success = all isJust ys

(Осторожно, я только доказал, что код правильный, но на самом деле не пробовал.)

Если это станет более сложным, чем то, что вы захотите использовать правильную библиотеку синтаксического анализа, я думаю.

2 голосов
/ 23 февраля 2011

Если что-то находится в монаде IO, вы не можете перенести это в чистый внешний мир для дальнейшей обработки. Вместо этого вы передаете свои чистые функции для работы внутри функций в IO.

В конце ваша программа читает некоторые входные данные и записывает некоторые выходные данные, так что ваша основная функция будет работать с IO, иначе вы просто не сможете ничего выводить. Вместо чтения IO String и создания [Int], передайте функцию, которая потребляет это [Int], в вашу основную функцию и используйте ее внутри do.

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