简体   繁体   English

Haskell模式匹配(初学者)

[英]Haskell Pattern Matching (beginner)

I have to implement a small programm in Haskell that increments/decrements a result by what in the console line is. 我必须在Haskell中实现一个小型程序,该程序根据控制台行的内容来递增/递减结果。 For example if we have -a in the console the results must be 0, if -b the result must be incremented with 6 and so on. 例如,如果控制台中有-a,则结果必须为0,如果-b,则结果必须以6递增,依此类推。 I have to do this with pattern matching. 我必须通过模式匹配来做到这一点。

I haven't used Haskell until now and I find it pretty hard to understand. 到目前为止,我还没有使用过Haskell,但我很难理解。 I have this to start with: 我首先要开始:

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

I don't understand what in the main is. 我不明白主要是什么。 What does it make and the reverse from end, what does it do? 它是做什么的,从头到尾是相反的,它是做什么的? As I've read on the internet the getArgs function gives me the values from the console line. 正如我在Internet上阅读的,getArgs函数为我提供了控制台行中的值。 But how can I use them? 但是我该如何使用它们呢? Are there are equivalent functions like for/while in Haskell? 在Haskell中是否有for / while等功能?

Also, if you have some examples or maybe could help me, I will be very thankful. 另外,如果您有一些例子或者也许可以帮助我,我将非常感谢。

Thanks! 谢谢!

This is not beginner-friendly code. 这不是适合初学者的代码。 Several shortcuts are taken there to keep the code very compact (and in pointfree form). 那里采取了几种快捷方式来使代码保持紧凑(无点形式)。 The code 编码

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

can be expanded as follows 可以扩展如下

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

The result of this can be seen as follows. 其结果如下所示。 If the command line arguments are, say, args = ["A","B","C"] , then we get reversedArgs = ["C","B","A"] and finally 如果命令行参数例如是args = ["A","B","C"] ,那么我们得到reversedArgs = ["C","B","A"] ,最后

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

since foldr applies the function apply in such way. 因为foldr应用功能apply于这样的方式。

Honestly, I'm unsure about why the code uses reverse and foldr for your task. 老实说,我不确定为什么代码会在您的任务中使用reverse和文件foldr I would have considered foldl (or, to improve performance, foldl' ) instead. 我本来会考虑使用foldl (或者为了提高性能,使用foldl' )。

I expect the exercise is not to touch the given code, but to expand on it to perform your function. 我希望练习不是触摸给定的代码,而是扩展它以执行您的功能。 It defines a complicated-looking main function and declares the type of a more straight forward apply , which is called but not defined. 它定义了一个看起来复杂的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

If we concentrate on apply , we see that it receives a string and an integer, and returns an integer. 如果我们专注于apply ,我们会看到它接收到一个字符串和一个整数,并返回一个整数。 This is the function we have to write, and it can't decide control flow, so we can just get to it while hoping the argument handling works out. 这是我们必须编写的函数,它不能决定控制流,因此我们可以在希望处理参数的同时找到它。

If we do want to figure out what main is up to, we can make a few observations. 如果我们要弄清楚什么main是提出来,我们可以提出一些看法。 The only integer in main is 0 , so the first call must get that as its second argument; main唯一的整数是0 ,因此第一个调用必须将其作为第二个参数; later ones will be chained with whatever is returned, as that's how foldr operates. 以后的将与返回的内容链接在一起,因为这就是文件foldr操作方式。 r stands for from the right, but the arguments are reverse d, so this still processes arguments from the left. r代表从右边开始,但参数是reverse d,因此它仍然从左边开始处理参数。

So I could go ahead and just write a few apply bindings to make the program compile: 所以我可以继续,只写一些apply绑定来使程序编译:

apply "succ"   n = succ n
apply "double" n = n + n
apply "div3"   n = n `div` 3

This added a few usable operations. 这增加了一些可用的操作。 It doesn't handle all possible strings. 它不能处理所有可能的字符串。

$ 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

The exercise should be about how you handle the choice of operation based on the string argument. 练习应该涉及如何处理基于字符串参数的操作选择。 There are several options, including distinct patterns as above, pattern guards, case and if expressions. 有几个选项,包括上述不同的模式 ,模式保护, caseif表达式。

It can be useful to examine the used functions to see how they might fit together. 检查已使用的功能以了解它们如何组合在一起可能很有用。 Here's a look at a few of the used functions in ghci : 这是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]

This shows that all the strings come out of getArgs , it and print operate in the IO monad, which must be the m in >>= , and . 这表明所有字符串都来自getArgs ,它和printIO monad中运行,它必须是>>=和中的m . transfers results from the right function into arguments for the left function. 将结果从右函数转换为左函数的参数。 The type signature alone doesn't tell us what order foldr handles things, though, or what reverse does (though it can't create new values, only reorder including repetition). 但是,仅类型签名并不能告诉我们什么顺序的文件foldr处理事情,或者reverse事情(尽管它不能创建新值,只能重新排序,包括重复)。

As a last exercise, I'll rewrite the main function in a form that doesn't switch directions as many times: 作为最后的练习,我将以不会多次切换方向的形式重写main函数:

main = print . foldl (flip apply) 0 =<< getArgs

This reads from right to left in a data flow sense and handles arguments from left to right because foldl performs left-associative folding. 这在数据流意义上从右到左读取,并从左到右处理参数,因为foldl执行左关联折叠。 flip is just there to match the argument order for apply . flip只是为了匹配apply的参数顺序apply

As suggested in the comment, hoogle is a great tool. 正如评论中所建议的那样,欢呼是一个很棒的工具。 To find out what exactly you get from getArgs you can search for it on hoogle: 要找出从getArgs确切得到什么,可以在getArgs中搜索它:

https://hackage.haskell.org/package/base-4.11.1.0/docs/System-Environment.html#v:getArgs As you can see, it's of type IO [String] . https://hackage.haskell.org/package/base-4.11.1.0/docs/System-Environment.html#v:getArgs如您所见,它的类型为IO [String] Since I don't know how familiar you are with the IO abstractions yet, we'll just say that the right part of >>= gets those as argument. 由于我不知道您对IO抽象有多熟悉,因此我们只能说>>=的正确部分将其作为参数。

The arguments for a call like ./a.out -a -b --asdf Hi will then be a list of strings: ./a.out -a -b --asdf Hi这样的调用的参数将是字符串列表:

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

The fold + reverse in the main will then do some magic, and your apply function will be called with each string in the list and the previous return value ( 0 for the first invocation). 然后,主菜单中的fold + reverse apply起到一些神奇作用,并且您的apply函数将被列表中的每个字符串和上一个返回值(第一次调用为0 )调用。

In Haskell, String is the same as [Char] with a bit of compiler sugar, so you can match on strings like you would on regular lists in your definition of apply . 在Haskell中, String[Char]相同,但带有一些编译器糖,因此您可以像在apply定义中的常规列表上一样在字符串上进行匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM