【问题标题】:Does Haskell have implicit pattern matching?Haskell 有隐式模式匹配吗?
【发布时间】:2021-07-05 22:58:02
【问题描述】:

举个例子:

module Main where

main = print (reverseWords "lol")
  
reverseWords :: String -> [String]  
reverseWords = words

reverseWords 函数未与此处的任何参数进行模式匹配,但该函数运行并输出 "[lol]"

我有两个问题:

  1. Haskell 如何知道我是否针对reverseWords 的输入调用words 函数?在这种语法中,看起来我只是在返回函数words

  2. 为什么即使我没有在模式中向reverseWords提供任何输入参数,它也能成功运行?

【问题讨论】:

  • 我发现,不要从(例如“回归”)的角度思考,而是从存在的角度思考是有帮助的。当我看到reverseWords = words 时,我认为“reverseWords words

标签: haskell syntax function-call


【解决方案1】:

你只是说reverseWords 函数words。任何出现reverseWords 的地方都可以用函数words 替换。所以print (reverseWords "lol") 完全等同于print (words "lol")。基本上,你有这个新函数reverseWords,它接受String 参数,就像words 所做的那样,只是将该参数传递给words,返回words 为该参数返回的任何内容。您对reverseWords 的定义相当于:

reverseWords :: String -> [String]
reverseWords s = words s

鉴于所有这些,reverseWords 是一个具有误导性的名称,因为它与words 并没有什么不同。所以,你并没有真正做任何有用的事情,只是重命名了一些东西。一个更好的例子是:

reverseWords :: String -> [String]
reverseWords = reverse . words

其中reverse 是您使用组合运算符(.)words 组合的其他函数,以创建一个有用的新函数。这称为无点样式,您可以通过组合其他函数而不引用任何参数来定义新函数。该定义相当于:

reverseWords :: String -> [String]
reverseWords s = reverse (words s)

【讨论】:

  • 我值得一提的是 eta-reduction。
  • 这些如何等效?据我了解,reverseWords 返回一个函数定义词(因此调用 reverseWords 应该只返回函数词(没有被调用))
  • @AndrewTang 它们不仅相等,而且相等。您可以一个一个地替换。他们是平等的,因为你这么说reverseWords = wordsreverseWords 返回的函数与words 返回的函数相同。这就像说 x = y, y = z, z = 1。即使您决定将其存储为 x -> y, y -> z, z->1,所有的值和存储方式都相同这个事实是无关紧要的。 (更准确地说,reverseWordswords 的类型仍然可能不同,即words 可以有更通用的类型。但您仍然可以一个一个替换。)
【解决方案2】:

您正在声明一个新函数reverseWords。首先你声明它的类型:

reverseWords :: String -> [String]

所以它将是一个接受字符串并返回字符串列表的函数。

现在有两种方法可以解决这个问题。首先是编写一个规则,说明当reverseWords 接收到某个参数时,结果是一个字符串列表的表达式(可能涉及调用其他函数并使用该参数)。像这样:

reverseWords s = words s

这表示“reverseWords s 形式的表达式被定义为等于words s”。那么编译器就知道reverseWords "lol" 等于words "lol"。函数reverseWords 由我们为其编写的规则隐式定义1

但是我们可以通过另一种方式来思考这个问题。我假设您对它的工作原理非常满意:

myFavouriteNumber :: Integer
myFavouriteNumber = 28

我们首先声明myFavouriteNumber 的类型为Integer,然后通过写下一个整数来定义它。

嗯,函数是 Haskell 中的第一类值,这意味着我们不必只使用专用的专用语法来定义它们。如果我们可以通过写下一个整数来定义Integer 类型,那么我们应该能够通过写下具有该类型的东西来定义String -> [String] 类型,而不是写下规则。这就是这种形式发生的事情:

reverseWords = words

我们没有为reverseWords 应用于某事物时的结果编写规则,而是写下reverseWords 是什么。在这种情况下,我们告诉编译器reverseWords 被定义为等于words。这仍然让编译器知道reverseWords "lol" 等于words "lol",但它只是通过查看reverseWords 部分来实现;即使不看"lol",它也可以解决这个问题。

此外,我们还可以这样写定义:

two :: Integer
two = 1 + 1

这里不是将two 定义为等于某个预先存在的事物,而是计算它的值(根据其他预先存在的事物:1+)。因为函数是一流的,我们可以做同样的事情:

reversedWords :: String -> [String]
reversedWords = reverse . words

这里我们不是说reversedWords 等于现有函数,而是我们计算 reverseWords 是你通过调用组合运算符. 得到的函数- 现有函数reversewords。但我们仍在计算函数(类型为String -> [String]),而不是函数的结果(类型为[String])。


所以回答你的问题:

haskell 如何知道我是否针对 reverseWords 的输入调用“words”函数?在这种语法中,看起来我只是在返回函数“words”

是的,您正在只是返回函数words。但是你将它作为 function reversedWords 本身(在它应用于任何东西之前)“返回”它,不是作为 @ 的 result 987654358@ 应用时。这就是 Haskell 知道words 函数是接收reverseWords 的输入的方式; reverseWords等于words,所以任何时候你将一些输入传递给reverseWords,你实际上是把它传递给words

为什么即使我没有在模式中向 reverseWords 提供任何输入参数,它也能成功运行?

因为你定义了函数reverseWords。您通过声明它等于某个其他现有函数来定义它,因此它执行该函数所做的任何事情。为函数结果编写规则(基于参数)并不是定义函数的唯一方法。

您没有在定义中为 reverseWords 的参数提供名称这一事实正是 Haskell 知道您在做什么的方式。如果你定义了一个A -> B 类型的函数并且你给参数一个名字,那么右边必须是B 类型的东西。如果你不这样做,那么右手边必须是 A -> B 类型的东西。2

但是对于你的瓷砖问题:

haskell 有隐式模式匹配吗?

我不知道如何回答这个问题,因为这个讨论根本没有涉及到模式匹配。您可以使用模式匹配在 Haskell 中定义函数,但这不是这里发生的事情。


1 好的,在这种情况下,reverseWords 非常明确由规则定义,但通常可以使用多个规则使用模式匹配和守卫来定义函数,并带有辅助where 定义;该函数的实际值是所有规则(以及如何按自上而下的顺序尝试它们的知识)和where 子句的一种涌现属性。

2 无论AB 是什么,这个逻辑都有效。特别是,B 可能是带有更多箭头的东西!这正是 Haskell 中具有多个参数的函数的工作方式。像这样的函数:

foo :: Int -> String -> (Int, String)

可以定义为:

  1. 编写带有两个参数(IntString)的规则,右侧类型为 (Int, String)
  2. 编写带有一个参数(Int)的规则,右侧类型为String -> (Int, String)
  3. 编写不带参数的直接定义,右侧类型为Int -> String -> (Int, String)

图案清晰;每次向规则添加参数时,RHS 都会有一个类型会再去掉一个箭头(从左侧开始)。

所有 3 个选项都会生成一个函数 foo,其类型相同,您可以以相同的方式调用。函数的内部定义与外界无关。

【讨论】:

    【解决方案3】:

    reverseWord确实“返回”words而不调用它,所以reverseWords <b><i>s</i></b>变成words <b><i>s</i></b>——因为reverseWords已经返回words调用reverseWords <b><i>s</i></b>已成为电话words <b><i>s</i></b>

    这就像在更传统的语法中定义foo() { bar },然后是foo()(x) === bar(x)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-12
      • 1970-01-01
      • 2017-11-13
      • 2021-05-22
      • 2017-05-08
      • 2019-03-01
      • 2016-10-29
      • 2011-11-30
      相关资源
      最近更新 更多