【问题标题】:Haskell Pattern Matching (beginner)Haskell 模式匹配(初级)
【发布时间】:2018-07-02 10:37:36
【问题描述】:

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

直到现在我还没有使用过 Haskell,我发现它很难理解。我有这个开始:

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

我不明白主要是什么。它做了什么,从头到尾反过来,它做了什么?正如我在互联网上所读到的,getArgs 函数为我提供了来自控制台行的值。但是我该如何使用它们呢? Haskell 中是否有类似 for/while 的等效函数?

另外,如果你有一些例子或者可能对我有帮助,我将非常感激。

谢谢!

【问题讨论】:

  • 您好,您知道 hoogle 吗? haskell.org/hoogle/?hoogle=reverse
  • 但是,是的,如果你真的开始使用 Haskell,我建议你看看比 main 更简单的东西。 learnyouahaskell.com/chapters 非常适合了解基础知识。
  • 这是课程中的练习吗?他们给你的代码真的不是一个很好的教学例子

标签: haskell matching fold


【解决方案1】:

这不是适合初学者的代码。那里采用了几个快捷方式来保持代码非常紧凑(并且采用无点形式)。代码

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')。

【讨论】:

  • 使用foldl (flip apply) 0 而不进行反转确实可以正常工作。
【解决方案2】:

我希望练习不是接触给定的代码,而是扩展它以执行您的功能。它定义了一个看起来很复杂的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 代表从右边,但参数是reversed,所以这仍然处理来自左边的参数。

所以我可以继续编写一些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 monad中运行,必须是&gt;&gt;=中的m,而.从右边传递结果函数转换为左函数的参数。但是,类型签名本身并不能告诉我们foldr 处理事情的顺序,或者reverse 做了什么(尽管它不能创建新值,只能重新排序,包括重复)。

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

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

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

【讨论】:

    【解决方案3】:

    正如评论中所建议的,hoogle 是一个很棒的工具。 要了解您从 getArgs 得到的确切信息,您可以在 hoogle 上搜索:

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

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

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

    然后,main 中的 fold + reverse 将发挥一些作用,您的 apply 函数将被调用,并使用列表中的每个字符串和前一个返回值(第一次调用为 0)。

    在 Haskell 中,String[Char] 相同,但带有一点编译器糖,因此您可以像在 apply 的定义中的常规列表中一样匹配字符串。

    【讨论】:

    • 另一个选项::t 在 GHCI。
    猜你喜欢
    • 1970-01-01
    • 2017-11-13
    • 2021-05-22
    • 2017-05-08
    • 2019-03-01
    • 2016-10-29
    • 2017-10-12
    • 2011-11-30
    • 2019-05-17
    相关资源
    最近更新 更多