Haskell Pattern Matching (новичок) - PullRequest
0 голосов
/ 02 июля 2018

Мне нужно реализовать небольшую программу на Haskell, которая увеличивает / уменьшает результат в зависимости от того, что находится в строке консоли. Например, если у нас есть -a в консоли, результаты должны быть 0, если -b результат должен быть увеличен на 6 и так далее. Я должен сделать это с сопоставлением с образцом.

Я не использовал Haskell до сих пор, и мне довольно трудно это понять. У меня есть это, чтобы начать с:

import System.Environment
main = getArgs >>= print . (foldr apply 0) . reverse
apply :: String -> Integer -> Integer

Я не понимаю, что в основном. Что он делает и наоборот от конца, что он делает? Как я читал в интернете, функция getArgs дает мне значения из строки консоли. Но как я могу их использовать? Существуют ли эквивалентные функции, такие как for / while в Haskell?

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

Спасибо!

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

Это не удобный для начинающих код. Несколько ярлыков используются для того, чтобы сделать код очень компактным (и в беспочвенной форме). Код

main = getArgs >>= print . (foldr apply 0) . reverse

может быть расширено следующим образом

main = do
  args <- getArgs
  let reversedArgs = reverse args
      result = foldr apply 0 reversedArgs
  print result

Результат этого можно увидеть следующим образом. Если аргументы командной строки, скажем, args = ["A","B","C"], то мы получаем reversedArgs = ["C","B","A"] и, наконец,

result = apply "C" (apply "B" (apply "A" 0))

, поскольку foldr применяет функцию apply таким образом.

Честно говоря, я не уверен, почему код использует reverse и foldr для вашей задачи. Я бы рассмотрел foldl (или, чтобы улучшить производительность, foldl') вместо этого.

0 голосов
/ 03 июля 2018

Я ожидаю, что упражнение состоит не в том, чтобы коснуться данного кода, а в том, чтобы расширить его для выполнения вашей функции. Он определяет сложную на вид функцию 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.

0 голосов
/ 02 июля 2018

Как указано в комментарии, Google - отличный инструмент. Чтобы узнать, что именно вы получаете от getArgs, вы можете найти его в Google:

https://hackage.haskell.org/package/base-4.11.1.0/docs/System-Environment.html#v:getArgs Как видите, он имеет тип IO [String]. Поскольку я пока не знаю, насколько вы знакомы с абстракциями ввода-вывода, мы просто скажем, что правая часть >>= получает их в качестве аргумента.

Аргументы для вызова типа ./a.out -a -b --asdf Hi будут тогда списком строк:

["-a", "-b", "--asdf", "Hi"].

Свернуть + перевернуть в главном будет затем волшебство, и ваша функция apply будет вызываться с каждой строкой в ​​списке и предыдущим возвращаемым значением (0 для первого вызова).

В Haskell, String - это то же самое, что и [Char] с небольшим количеством сахара компилятора, так что вы можете сопоставлять строки как в обычных списках в вашем определении apply.

...