Haskell IO передает другую функцию - PullRequest
2 голосов
/ 18 июня 2010

Этот вопрос здесь относится к кортежу ввода ввода Haskell

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

На самом деле я хочу что-то вроде

-- First Example
test = savefile investinput 
-- Second Example
maxinvest :: a
maxinvest = liftM maximuminvest maxinvestinput

maxinvestinput :: IO()
maxinvestinput = do
    str <- readFile "C:\\Invest.txt"
    let cont = words str
    let mytuple = converttuple cont
    let myint = getint mytuple

    putStrLn ""

-- Convert to Tuple
converttuple :: [String] -> [(String, Integer)]
converttuple [] = []
converttuple (x:y:z) = (x, read y):converttuple z

-- Get Integer
getint :: [(String, Integer)] -> [Integer]
getint [] = []
getint (x:xs) = snd (x) : getint xs

-- Search Maximum Invest
maximuminvest :: (Ord a) => [a] -> a
maximuminvest [] = error "Empty Invest Amount List"
maximuminvest [x] = x
maximuminvest (x:xs)   
     | x > maxTail = x  
     | otherwise = maxTail  
     where maxTail = maximuminvest xs 

Во втором примере maxinvestinput считывается из файла и преобразует данные в ожидаемый тип максимума.Пожалуйста помоги.

Спасибо.

Ответы [ 2 ]

8 голосов
/ 18 июня 2010

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


Я не совсем уверен, что вы хотите, чтобы ваша программа делала.Я понимаю, что вам нужна программа, которая читает в качестве входных данных файл, содержащий список людей и их инвестиции.Однако я не уверен, что ты хочешь с этим делать.Кажется, вы (а) хотите иметь разумную структуру данных ([(String,Integer)]), но затем (б) использовать только целые числа, поэтому я предполагаю, что вы хотите что-то сделать и со строками.Давайте пройдем через это.Во-первых, вам нужна функция, которая может, учитывая список целых чисел, вернуть максимум.Вы называете это maximuminvest, но эта функция более общая, чем просто инвестиции, так почему бы не назвать ее maximum?Оказывается, эта функция уже существует.Как ты мог знать это?Я рекомендую Hoogle - это поисковая система Haskell, которая позволяет вам искать как имена, так и типы функций.Вам нужна функция из списков целых чисел в одно целое число, поэтому давайте найдем это .Как оказалось, первый результат - maximum, который является более общей версией того, что вы хотите.Но для целей обучения, давайте предположим, что вы хотите написать это самостоятельно;в этом случае ваша реализация в порядке.

Хорошо, теперь мы можем вычислить максимум.Но сначала нам нужно составить наш список.Нам понадобится функция типа [String] -> [(String,Integer)] для преобразования нашего списка без форматирования в разумный.Ну, чтобы получить целое число из строки, нам нужно использовать read.Короче говоря, ваша текущая реализация этого также хороша, хотя я бы (а) добавил случай error для списка из одного элемента (или, если мне было приятно, просто вернул пустой список, чтобы игнорироватьпоследний пункт списков нечетной длины), и (б) используйте имя с заглавной буквой, чтобы я мог разделить слова (и, вероятно, другое имя):

tupledInvestors :: [String] -> [(String, Integer)]
tupledInvestors []              = []
tupledInvestors [_]             = error "tupledInvestors: Odd-length list"
tupledInvestors (name:amt:rest) = (name, read amt) : tupledInvestors rest

Теперь у нас есть этиМы можем предоставить себе удобную функцию, maxInvestment :: [String] -> Integer.Единственное, чего не хватает, так это возможности перейти из списка с кортежем в список целых чисел.Есть несколько способов решить эту проблему.Одним из них является тот, который у вас есть, хотя это было бы необычно в ХаскелеСекунду будет использовать map :: (a -> b) -> [a] -> [b].Это функция, которая применяет функцию к каждому элементу списка.Таким образом, ваш getint эквивалентен простому map snd.Самый хороший способ, вероятно, будет использовать Data.List.maximumBy :: :: (a -> a -> Ordering) -> [a] -> a.Это похоже на maximum, но позволяет вам использовать собственную функцию сравнения.И используя Data.Ord.comparing :: Ord a => (b -> a) -> b -> b -> Ordering, все становится хорошо.Эта функция позволяет сравнивать два произвольных объекта путем преобразования их во что-то, что можно сравнить.Таким образом, я написал бы

maxInvestment :: [String] -> Integer
maxInvestment = maximumBy (comparing snd) . tupledInvestors

Хотя вы также можете написать maxInvestment = maximum . map snd . tupledInvestors.

Хорошо, теперь перейдем к IO.Таким образом, ваша основная функция хочет читать из определенного файла, вычислять максимальные инвестиции и распечатывать их.Один из способов представить это в виде последовательности из трех отдельных шагов:

main :: IO ()
main = do dataStr <- readFile "C:\\Invest.txt"
          let maxInv = maxInvestment $ words dataStr
          print maxInv

(Оператор $, если вы его не видели, это просто приложение функции, но с более удобным приоритетом;имеет тип (a -> b) -> a -> b, что должно иметь смысл.) Но это let maxInv кажется довольно бессмысленным, поэтому мы можем избавиться от него:

main :: IO ()
main = do dataStr <- readFile "C:\\Invest.txt"
          print . maxInvestment $ words dataStr

., если вы его еще не виделивсе же, это функциональная композиция;f . g совпадает с \x -> f (g x).(Он имеет тип (b -> c) -> (a -> b) -> a -> c, который, если подумать, должен иметь смысл.) Таким образом, f . g $ h x - это то же самое, что и f (g (h x)), только легче читать.

Теперь мы смогли избавиться от let.А как насчет <-?Для этого мы можем использовать оператор =<< :: Monad m => (a -> m b) -> m a -> m b.Обратите внимание, что это почти как $, но с m портит почти все.Это позволяет нам принять монадическое значение (здесь readFile "C:\\Invest.txt" :: IO String), передать его функции, которая превращает простое значение в монадическое значение, и получить это монадическое значение.Таким образом, у нас есть

main :: IO ()
main = print . maxInvestment . words =<< readFile "C:\\Invest.txt"

Это должно быть понятно, я надеюсь, особенно если вы думаете о =<< как о монаде $.

Я не уверен, что происходит сtestfile;если вы отредактируете свой вопрос, чтобы отразить это, я постараюсь обновить мой ответ.


Еще одна вещь.Вы сказали, что

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

Как и во всем в Haskell, этовопрос типов .Итак, давайте разберемся с типами здесь.У вас есть некоторая функция f :: a -> b и некоторое монадическое значение m :: IO a.Вы хотите использовать f, чтобы получить значение типа b.Это невозможно, как я объяснил в моем ответе на другой вопрос ;однако, вы можете получить что-то типа IO b.Таким образом, вам нужна функция, которая берет ваш f и дает вам монадическую версию.Другими словами, что-то типа Monad m => (a -> b) -> (m a -> m b).Если мы подключим это к Hoogle , первый результат будет Control.Monad.liftM, который имеет именно эту сигнатуру типа.Таким образом, вы можете трактовать liftM как несколько другое "монадическое $", чем =<<: f \ liftM` m applies f to the pure result of m (in accordance with whichever monad you're using) and returns the monadic result. The difference is that liftM takes a pure function on the left, and = << `частично-монадический. </p>

Другой способ написать то же самое - использовать do -обозначение:

do x <- m
   return $ f x

Это говорит: "получить x из m, применитьf к нему и поднять результат обратно в монаду. "Это то же самое, что и утверждение return . f =<< m, которое снова точно равно liftM.Сначала f выполняет чистые вычисления;его результат передается в return (через .), который поднимает чистое значение в монаду;и затем эта частично-монадическая функция применяется через =<, к m.

Уже поздно, поэтому я не уверен, какой смысл это имело.Позвольте мне попытаться подвести итог.Короче говоря, нет общего способа покинуть монаду .Когда вы хотите выполнить вычисления на монадических значениях, вы поднимаете чистые значения (включая функции) в монаду, а не наоборот;это может нарушить чистоту, что было бы Very Bad ™.


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

3 голосов
/ 18 июня 2010

Я не уверен, что понимаю ваш вопрос, но я отвечу, как смогу. Я немного упростил вопрос, чтобы понять «суть» вопроса, если я правильно понимаю.

maxInvestInput :: IO [Integer]
maxInvestInput = liftM convertToIntegers (readFile "foo")

maximumInvest :: Ord a => [a] -> a
maximumInvest = blah blah blah

main = do
   values <- maxInvestInput
   print $ maximumInvest values

OR

main = liftM maximumInvest maxInvestInput >>= print
...