Это выиграет от ключевого принципа: отделите свой чистый код от вашего IO как можно больше. Это позволит вашим программам масштабироваться и поддерживать main
breif. Много let
в большом main
не очень функциональный подход и имеет тенденцию становиться намного более грязным по мере роста вашего кода.
Использование подписи типа и readLn
, которое по существу fmap read getLine
помогает сократить некоторые трещины. (Если вы не знакомы с fmap
, посетить вопрос How do functors work in haskell?. fmap
является очень гибким инструментом на самом деле.)
getInts :: IO (Int, Int)
getInts = do
putStrLn "Please enter the dividend :"
x <- readLn
putStrLn " Please enter the divisor :"
y <- readLn
return (x,y)
Теперь обработку. Если бы я делал больше с такими данными, или чаще, я бы использовал тип записи для хранения дивидендов, делителей, частного и остального, так что имейте это в виду на будущее, но здесь это избыток.
Я hackishly возвращает список, а не кортеж, так что я могу использовать map
для show
их всех:
sums :: (Int, Int) -> [Int]
sums (x,y) = [x, y, q, r, y * q, y * q + r] where
q = x `div` y
r = x `mod` y
Последняя часть головоломки является выходным. Опять же, я предпочитаю генерировать это за пределами IO, а затем я могу только mapM_ putStrLn
на нем позже печатать каждую строку. Я бы предпочел, чтобы это взяло тип записи, но я терпеть список строк в качестве ввода вместо этого, так как я предполагаю, что у меня уже есть n все.
explain :: [String] -> [String]
explain [x,y,q,r,yq,yq_r] =
[ concat ["Result: ", x, "/", y, " = ", q, " remainder ", r]
, concat ["Proof: (", y, " x ", q, ") + ", r, " = ", yq, " + ", r, " = ", yq_r]
, "Is this what you had? "]
Теперь мы можем написать main
в
main = do (x,y) <- getInts
let ns = map show (sums (x,y))
es = explain ns
mapM_ putStrLn es
или даже более сжато, по конвейеру вместе функций explain . map show . sums
, и применения к выходу getInts
использования fmap
:
main :: IO()
main = fmap (explain . map show . sums) getInts
>>= mapM_ putStrLn
Вы могли заметить, что я добавил +r
в доказательство, чтобы сделать =
всегда означает =
, что является правильным математическим использованием и значением Haskell для зеркала для =.
Я думаю, что все в порядке - вы могли бы сократить некоторые части (например, * merge * 'read' и' getLine' - используя ['<$>'] (http://haddocks.fpcomplete.com/fp/7.4.2/ 20130829-168/base/Data-Functor.html # v: -60--36--62-) и 'div' /' mod', используя ['divMod'] (http://haddocks.fpcomplete.com/fp /7.4.2/20130829-168/base/Prelude.html#v:divMod) - но я думаю, что это нормально для такой простой программы. – Carsten
Стиль в основном предпочтителен, но я бы сделал следующее: 'input <- putStrLn" prompt ">> getLine'; инструкции sequential let не нуждаются в let в каждой строке (т.е. замените все значения let после первого на 3 пробела), вместо того, чтобы иметь' putStrLn' в последовательности, я бы написал строку 'putStrLn $ ' 1 "++" строка 2 "++" строка 3 "' (каждая строка может быть в отдельной строке - если строки выделены, haskell знает свою часть того же оператора). – user2407038
@ user2407038 ваш 'putStrLn' пропустит несколько строк новой строки;) – Carsten