Я ожидаю, что упражнение состоит не в том, чтобы коснуться данного кода, а в том, чтобы расширить его для выполнения вашей функции. Он определяет сложную на вид функцию main
и объявляет тип более простой apply
, которая вызывается, но не определяется.
import System.Environment -- contains the function getArgs
-- main gets arguments, does something to them using apply, and prints
main = getArgs >>= print . (foldr apply 0) . reverse
-- apply must have this type, but what it does must be elsewhere
apply :: String -> Integer -> Integer
Если мы сконцентрируемся на apply
, мы увидим, что он получает строку и целое число и возвращает целое число. Это функция, которую мы должны написать, и она не может определять поток управления, поэтому мы можем просто добраться до нее, надеясь, что обработка аргументов сработает.
Если мы хотим выяснить, чем занимается main
, мы можем сделать несколько замечаний. Единственное целое число в main
- это 0
, поэтому первый вызов должен получить его в качестве второго аргумента; последующие будут прикованы тем, что возвращено, как работает foldr
. r
обозначает справа, но аргументы reverse
d, поэтому он по-прежнему обрабатывает аргументы слева.
Итак, я мог бы написать несколько привязок apply
, чтобы программа скомпилировалась:
apply "succ" n = succ n
apply "double" n = n + n
apply "div3" n = n `div` 3
Это добавило несколько полезных операций. Он не обрабатывает все возможные строки.
$ runhaskell pmb.hs succ succ double double succ div3
3
$ runhaskell pmb.hs hello?
pmb.hs: pmb.hs:(5,1)-(7,26): Non-exhaustive patterns in function apply
Упражнение должно быть о том, как вы обрабатываете выбор операции на основе строкового аргумента. Существует несколько опций, включая различные шаблоны , как указано выше, охранники шаблонов, case
и if
выражения.
Может быть полезно изучить используемые функции, чтобы увидеть, как они могут сочетаться друг с другом. Вот посмотрите на некоторые из используемых функций в ghci
:
Prelude> import System.Environment
Prelude System.Environment> :t getArgs
getArgs :: IO [String]
Prelude System.Environment> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Prelude System.Environment> :t print
print :: Show a => a -> IO ()
Prelude System.Environment> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
Prelude System.Environment> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude System.Environment> :t reverse
reverse :: [a] -> [a]
Это показывает, что все строки получены из getArgs
, it и print
работают в монаде IO
, которая должна быть m
in >>=
и .
переносят результаты из правой функции в аргументы для левой функции. Сигнатура типа сама по себе не говорит нам, какой порядок foldr
обрабатывает вещи, хотя, или что делает reverse
(хотя он не может создавать новые значения, только переупорядочивать, включая репетиция).
В качестве последнего упражнения я перепишу функцию main
в форме, которая не меняет направления столько раз:
main = print . foldl (flip apply) 0 =<< getArgs
Это читает справа налево в смысле потока данных и обрабатывает аргументы слева направо, потому что foldl
выполняет левоассоциативное свертывание. flip
просто соответствует порядку аргументов apply
.