Haskell模式匹配(初学者)

时间:2018-07-02 10:37:36

标签: haskell matching fold

我必须在Haskell中实现一个小型程序,该程序根据控制台行的内容来递增/递减结果。例如,如果控制台中有-a,则结果必须为0,如果-b,则结果必须以6递增,依此类推。我必须通过模式匹配来做到这一点。

直到现在我还没有使用过Haskell,我很难理解。我首先要这样做:

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

我不明白主要是什么。它是做什么的,从头到尾是相反的,它是做什么的?正如我在Internet上阅读的,getArgs函数为我提供了控制台行中的值。但是我该如何使用它们呢?在Haskell中是否有for / while等功能?

此外,如果您有一些例子或者可能对我有帮助,我将非常感谢。

谢谢!

3 个答案:

答案 0 :(得分:5)

这不是适合初学者的代码。那里采取了几种快捷方式来使代码保持紧凑(无点形式)。代码

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

老实说,我不确定代码为什么将reversefoldr用于您的任务。我本来会考虑使用foldl(或者为了提高性能,而考虑使用foldl')。

答案 1 :(得分:1)

我希望练习不是触摸给定的代码,而是扩展它以执行您的功能。它定义了外观复杂的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

本练习应该涉及如何处理基于字符串参数的操作选择。有几个选项,包括上述不同的patterns,模式保护符,caseif表达式。

检查已使用的功能以了解它们如何组合在一起可能很有用。以下是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,它和printIO单子中运行,该单子必须是m中的>>=.将结果从右函数转换为左函数的参数。但是,仅类型签名并不能告诉我们foldr处理什么顺序,还是reverse处理什么(尽管它不能创建新值,只能重新排序,包括重复)。

作为最后一个练习,我将重写main函数的形式不会多次切换方向:

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

这在数据流意义上从右到左读取,并从左到右处理自变量,因为foldl执行左关联折叠。 flip就是为了匹配apply的参数顺序。

答案 2 :(得分:0)

正如评论中所建议的,hoogle是一个很好的工具。 要找出您从getArgs中得到的确切信息,可以在大叫中搜索它:

https://hackage.haskell.org/package/base-4.11.1.0/docs/System-Environment.html#v:getArgs 如您所见,它的类型为IO [String]。 既然我还不知道您对IO抽象有多熟悉,我们只能说>>=的正确部分将这些抽象作为参数。

./a.out -a -b --asdf Hi这样的调用的参数将是一个字符串列表:

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

然后,主菜单中的fold + reverse将起到一些神奇作用,您的apply函数将被列表中的每个字符串和先前的返回值(第一次调用为0)所调用。

在Haskell中,String[Char]相同,但有少许编译器糖,因此您可以像在apply定义中的常规列表上一样在字符串上进行匹配。 / p>