Haskell是否有隐式模式匹配



举这个例子:

module Main where
main = print (reverseWords "lol")

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

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

我这里有两个问题:

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

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

你只是说reverseWords函数words。无论reverseWords出现在何处,都可以将其替换为函数words。所以print (reverseWords "lol")完全等同于print (words "lol").基本上,你有这个新函数reverseWords它像words一样接受String参数,并简单地将该参数传递给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)

您正在声明一个新函数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如何知道我是否正在对输入调用"words"函数来反转单词?在这种语法中,看起来我只是返回函数"words">

是的,您只是将函数words返回。但是你把它"返回"为函数本身reversedWords(在它应用于任何东西之前),而不是作为应用它时reversedWords的结果。这就是Haskell如何知道words函数是接收输入到reverseWords;reverseWords等于words,所以任何时候你传递一些输入给reverseWords你实际上是在传递给words

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

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

事实上,你没有在你的定义中为reverseWords的参数提供一个名称,这正是Haskell知道这就是你正在做的事情。如果要定义类型为A -> B的函数并为参数命名,则右侧必须是类型B。如果你不这样做,那么右手边一定是A -> B型的东西。阿拉伯数字

但是对于您的磁贴问题:

Haskell有隐式模式匹配吗?

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


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

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

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

可以由以下任一定义:

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

模式很清楚;每次向规则添加参数时,RHS 都有一个类型,该类型会再去除一个箭头(从左侧开始)。

所有 3 个选项都会生成一个具有相同类型的函数foo,您可以以相同的方式调用该函数。函数的内部定义对外界来说并不重要。

>reverseWord确实"返回"words而不调用它,所以reverseWordss变得wordss- 由于reverseWords返回words调用reverseWordss变成了呼叫wordss

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

最新更新