微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Haskell 是否有隐式模式匹配?

如何解决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,它接受​​一个 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)
,

您正在声明一个新函数 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 和 {{1} })。因为函数是一流的,我们可以做同样的事情:

+

这里我们不说 reversedWords :: String -> [String] reversedWords = reverse . words 等于现有函数,而是计算 reversedWords 是您通过调用组合运算符获得的函数 {{ 1}} 在预先存在的函数 reverseWords. 上。但我们仍在计算函数reverse 类型),而不是函数的结果words 类型)。


所以回答你的问题:

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

是的,您只是返回函数 String -> [String]。但是您将它作为 function [String] 本身(在它应用于任何事物之前)“返回”,不是作为结果 words 应用时。这就是 Haskell 如何知道 reversedWords 函数将接收 reversedWords 的输入; words 等于reverseWords,所以任何时候你把一些输入传递给 reverseWords 你实际上是把它传递给 words

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

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

您没有在定义中为 words 的参数提供名称这一事实正是 Haskell 知道您在做什么的原因。如果您正在定义类型为 reverseWords 的函数并为参数命名,则右侧必须是类型为 reverseWords 的东西。如果不这样做,则右侧必须是 A -> B 类型的内容。2

但是对于您的磁贴问题:

haskell 是否有隐式模式匹配?

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


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

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

B

可以定义为:

  1. 编写一个接受两个参数(一个 B 和一个 foo :: Int -> String -> (Int,String) )的规则,右手边的类型为 Int
  2. 编写一个接受一个参数(String)的规则,右手边的类型为 (Int,String)
  3. 编写不带参数的直接定义,右侧类型为 Int

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

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

,

reverseWord 确实“返回”了 words 而不调用它,因此 reverseWords s 变为 words s -- 因为 reverseWords 返回了 wordscall reverseWords s 变成了 call words s

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。