函数式语言中的“模式匹配”是什么?

2023-12-12

我正在阅读有关函数式编程的内容,我注意到模式匹配许多文章都提到它是函数式语言的核心特性之一。

有人可以为 Java/C++/JavaScript 开发人员解释一下这是什么意思吗?


理解模式匹配需要解释三个部分:

  1. 代数数据类型。
  2. 什么是模式匹配
  3. 为什么它太棒了。

简而言之,代数数据类型

类似 ML 的函数语言允许您定义称为“不相交联合”或“代数数据类型”的简单数据类型。这些数据结构是简单的容器,并且可以递归地定义。例如:

type 'a list =
    | Nil
    | Cons of 'a * 'a list

定义了一个类似栈的数据结构。将其视为与此 C# 等效:

public abstract class List<T>
{
    public class Nil : List<T> { }
    public class Cons : List<T>
    {
        public readonly T Item1;
        public readonly List<T> Item2;
        public Cons(T item1, List<T> item2)
        {
            this.Item1 = item1;
            this.Item2 = item2;
        }
    }
}

So, the Cons and Nil标识符定义了一个简单的类,其中of x * y * z * ...定义了一个构造函数和一些数据类型。构造函数的参数是未命名的,它们由位置和数据类型标识。

您创建您的实例a list类如下:

let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))

这与以下内容相同:

Stack<int> x = new Cons(1, new Cons(2, new Cons(3, new Cons(4, new Nil()))));

简而言之,模式匹配

模式匹配是类型测试的一种。假设我们创建了一个像上面这样的堆栈对象,我们可以实现查看和弹出堆栈的方法,如下所示:

let peek s =
    match s with
    | Cons(hd, tl) -> hd
    | Nil -> failwith "Empty stack"

let pop s =
    match s with
    | Cons(hd, tl) -> tl
    | Nil -> failwith "Empty stack"

上面的方法与以下 C# 等效(尽管不是这样实现):

public static T Peek<T>(Stack<T> s)
{
    if (s is Stack<T>.Cons)
    {
        T hd = ((Stack<T>.Cons)s).Item1;
        Stack<T> tl = ((Stack<T>.Cons)s).Item2;
        return hd;
    }
    else if (s is Stack<T>.Nil)
        throw new Exception("Empty stack");
    else
        throw new MatchFailureException();
}

public static Stack<T> Pop<T>(Stack<T> s)
{
    if (s is Stack<T>.Cons)
    {
        T hd = ((Stack<T>.Cons)s).Item1;
        Stack<T> tl = ((Stack<T>.Cons)s).Item2;
        return tl;
    }
    else if (s is Stack<T>.Nil)
        throw new Exception("Empty stack");
    else
        throw new MatchFailureException();
}

(机器学习语言几乎总是实现模式匹配without运行时类型测试或强制转换,因此 C# 代码有些欺骗性。让我们挥挥手,把实现细节放在一边:))

简而言之,数据结构分解

好吧,让我们回到 peek 方法:

let peek s =
    match s with
    | Cons(hd, tl) -> hd
    | Nil -> failwith "Empty stack"

诀窍是理解hd and tl标识符是变量(呃……因为它们是不可变的,所以它们不是真正的“变量”,而是“值”;))。如果s有类型Cons,然后我们将从构造函数中取出它的值并将它们绑定到名为的变量hd and tl.

模式匹配很有用,因为它让我们可以根据数据结构的不同来分解数据结构shape而不是它的contents。想象一下,如果我们按如下方式定义二叉树:

type 'a tree =
    | Node of 'a tree * 'a * 'a tree
    | Nil

我们可以定义一些树轮换如下:

let rotateLeft = function
    | Node(a, p, Node(b, q, c)) -> Node(Node(a, p, b), q, c)
    | x -> x

let rotateRight = function
    | Node(Node(a, p, b), q, c) -> Node(a, p, Node(b, q, c))
    | x -> x

(The let rotateRight = function构造函数是语法糖let rotateRight s = match s with ....)

所以除了将数据结构绑定到变量之外,我们还可以深入研究它。假设我们有一个节点let x = Node(Nil, 1, Nil)。如果我们打电话rotateLeft x,我们测试x针对第一个模式,该模式无法匹配,因为右孩子具有类型Nil代替Node。它将移动到下一个模式,x -> x,它将匹配任何输入并返回未经修改的输入。

为了进行比较,我们将上面的方法用 C# 编写为:

public abstract class Tree<T>
{
    public abstract U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc);

    public class Nil : Tree<T>
    {
        public override U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc)
        {
            return nilFunc();
        }
    }

    public class Node : Tree<T>
    {
        readonly Tree<T> Left;
        readonly T Value;
        readonly Tree<T> Right;

        public Node(Tree<T> left, T value, Tree<T> right)
        {
            this.Left = left;
            this.Value = value;
            this.Right = right;
        }

        public override U Match<U>(Func<U> nilFunc, Func<Tree<T>, T, Tree<T>, U> nodeFunc)
        {
            return nodeFunc(Left, Value, Right);
        }
    }

    public static Tree<T> RotateLeft(Tree<T> t)
    {
        return t.Match(
            () => t,
            (l, x, r) => r.Match(
                () => t,
                (rl, rx, rr) => new Node(new Node(l, x, rl), rx, rr))));
    }

    public static Tree<T> RotateRight(Tree<T> t)
    {
        return t.Match(
            () => t,
            (l, x, r) => l.Match(
                () => t,
                (ll, lx, lr) => new Node(ll, lx, new Node(lr, x, r))));
    }
}

对于认真的。

模式匹配太棒了

你可以实施一些东西similar使用 C# 中的模式匹配访客模式,但它不太灵活,因为您无法有效地分解复杂的数据结构。此外,如果您使用模式匹配,编译器会告诉你是否遗漏了一个案例。那有多棒?

考虑如何在 C# 或没有模式匹配的语言中实现类似的功能。考虑一下在运行时没有测试和强制转换的情况下如何做到这一点。肯定不是hard,只是笨重且笨重。并且您没有让编译器进行检查以确保您已经涵盖了每种情况。

因此,模式匹配可以帮助您以非常方便、紧凑的语法分解和导航数据结构,它使编译器能够检查logic你的代码,至少一点点。它真的is杀手级功能。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

函数式语言中的“模式匹配”是什么? 的相关文章

  • 什么样的函数被认为是“可组合的”?

    维基百科文章函数组合 计算机科学 https en wikipedia org wiki Function composition computer science says 就像数学中通常的函数组合一样 每个函数的结果作为下一个函数的参数
  • 免费商店的“堆”一词的由来是什么?

    我试图找到免费存储通常被称为堆的官方 或足够好的 原因 除了它从数据段末尾增长这一事实之外 我实在想不出一个很好的理由 特别是因为它与堆数据结构关系不大 注意 很多人提到这只是一大堆没有组织的东西 但对我来说 堆 一词在物理上意味着一堆物理
  • 减少/折叠幺半群列表,但减少器返回任一

    我发现自己遇到过几次这样的情况 我有一个减速器 组合 fn 如下所示 def combiner a String b String Either String String a b asRight String 它是一个虚拟实现 但 fn
  • 当函数中的模式匹配采用 &self 或 &mut self 时,如何避免使用 ref 关键字?

    铁锈书称为ref关键词 遗产 https doc rust lang org book ch18 03 pattern syntax html legacy patterns ref and ref mut 因为我想遵循隐含的建议来避免re
  • Haskell 入门

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 几天来 我一直试图理解 Haskell 中的函数式编程范例 我通过阅读教程和观看截屏视频
  • Haskell / GHC - 是否有“警告不完整模式”的中缀标签/编译指示

    我正在寻找一个可以对特定的不完整模式发出警告的编译指示 它会使编译器失败并显示以下 假设的 代码 FAILIF incomplete patterns f Int gt Int f 0 0 我正在尝试使用 Arrows 编写一个 编译器 并
  • @tailrec为什么这个方法不编译为“包含不在尾部位置的递归调用”?

    tailrec private def loop V key String V key match case gt loop key 此方法无法编译并抱怨它 包含不在尾部位置的递归调用 有人可以向我解释一下发生了什么事吗 这个错误消息对我来
  • 构造微积分中的“Refl”东西?

    在语言中 例如Agda Idris or Haskell对于类型扩展 有一个 键入类似于以下内容的内容 data a b where Refl a a a b意思是a and b是相同的 这样的类型可以定义在结构演算 https en wi
  • 迭代和遍历有什么区别?

    过去几周我一直在学习迭代器 我仍然不明白迭代链接列表和遍历链接列表之间的主要区别 我知道遍历意味着遍历 访问每个元素 链接列表 并且在迭代时基本上做同样的事情 但是有什么不同 为什么不能遍历所有内容 标准库数据结构 遍历 只是意味着遍历数据
  • 匹配没有周围字符列表的单词列表

    我有这个正则表达式 one common word or another 除非这两个单词相邻 否则它匹配得很好 One one s more word word common word or another word more anothe
  • Scala 模式匹配与 Option[Any] 的混淆

    我有以下 Scala 代码 import scala actors Actor object Alice extends Actor this start def act loop react case Hello gt sender Hi
  • duckmap 到底有什么作用?

    From 文档 https docs perl6 org routine duckmap duckmap将会应用 block每个元素上并返回一个新列表 其中包含块的已定义返回值 对于未定义的返回值 duckmap如果该元素实现了 将尝试下降
  • 在 C# 中实现模式匹配

    在 Scala 中 您可以使用模式匹配根据输入的类型生成结果 例如 val title content match case blogPost BlogPost gt blogPost blog title blogPost title c
  • 有没有好的 Clojure 基准测试?

    Edit Clojure 基准测试已达到基准游戏 http benchmarksgame alioth debian org u64q clojure html 我已经制作了这个问题社区维基并邀请其他人保持更新 有人知道 Clojure 性
  • 对列表中的相邻元素进行分组

    假设我想编写一个函数来执行此操作 输入 1 1 3 3 4 2 2 5 6 6 输出 1 1 3 3 4 2 2 5 6 6 它将相同的相邻元素分组 这个方法的名称应该是什么 此操作有标准名称吗 In 1 1 3 3 4 2 2 5 6 6
  • Scala 模式匹配打印漂亮

    是否有可能以某种方式编组部分函数 假设它总是只包含一种情况 进入某物人类可读的 假设我们有 Any 类型的集合 消息 List Any 以及使用模式匹配块定义的 PartialFuntion Any T 的数量 case object R1
  • 从函数返回随机值是副作用吗?

    我当时正在编写一些 F 代码 并且正在编写一个从一组字符串中返回随机字符串的函数 假设我有这样的事情 open System let a a b c d let rstring arr string let r new Random arr
  • 如何使用 FS2 中的分类器函数对对象进行分组?

    我有一个无序的流measurements 我想将其分组为固定大小的批次 以便以后可以有效地保留它们 val measurements for id lt Seq foo bar baz value lt 1 to 5 yield id va
  • 如何使用 rxpy/rxjs 延迟事件发射?

    我有两个事件流 一个来自电感环路 另一个来自网络摄像机 汽车将驶过环路 然后撞上相机 如果事件彼此相差在 N 毫秒内 汽车总是会首先进入循环 我想将它们组合起来 但我也希望每个流中不匹配的事件 硬件可能会失败 全部合并到单个流中 像这样的事
  • 需要澄清令人困惑的 Http4s 消息类型 `Response[F]` / `Request[F]`

    我很难理解为什么Request and Response参数化为F 类似的东西是猫效应数据类型资源 从文档中 https typelevel org cats effect docs std resource https typelevel

随机推荐

  • JAXB:第三方或外部超类上的 @XmlTransient

    我需要有关 JAXB 2 1 的以下问题的一些帮助 示例 我创建了一个扩展抽象类 Person 的 SpecialPerson 类 现在我想使用 JAXB 将对象结构转换为 XML 模式 因此 我不希望 Person XML 类型出现在我的
  • onload() 和 $.ready 之间的区别?

    你能列出之间的区别吗onload and document ready function 使用 jQuery 中的函数 the load窗口和 或主体元素上的事件 又名 onload 将触发一次all页面内容已加载 这包括所有图像 脚本等
  • 解析 Excel 文件并读取单元格

    我有一个excel文件 我已经上传了截图 我需要编写一个 NET应用程序 控制台应用程序 来解析excel文件 您可以看到一个标题为 函数名称 的单元格 我的 NET 应用程序应该找到该特定单元格并读取该列中的内容 例如模板 Instanc
  • 如何在javascript中获取服务器时区

    我想在 Javascript 中设置不同的时区 当前它显示本地计算机或客户端 PC 日期 时区的日期和时区 Regards Javascript 是一种客户端语言 不会以这种方式与服务器交互 您需要从服务器端平台获取该数据 下面是一些 PH
  • 在空 JTextField 中按下退格键时禁用蜂鸣声

    初学者在这里 有谁知道一种快速简便的方法 可以让 JTextField 在按下退格键且字段为空时不发出蜂鸣声 我在网上看到了一些关于更改 DefaultEditorKit 的内容 但我无法理解 任何帮助将不胜感激 这段代码对我有用 Acti
  • AdSense IAB TCF 错误 3.3:如何删除旧字符串并重新获得同意

    我的网站已经上线几年了 使用 AdSense 及其集成的 GDPR 内容功能 即在 IAB TCF 术语中 Google 充当 CMP 在过去的几周里 我收到了以下消息 我们检测到您的一个或多个网站或应用程序上的 IAB TC 字符串存在问
  • 为什么java无法从死锁中恢复?

    我正在读 Java Concurrency in Practice 一书 里面是关于死锁的内容 JVM无法从死锁中恢复 只有摆脱死锁的方法 lock就是重启服务器 还提到了JVM使用graph 搜索其中线程充当两个线程 A 之间的图节点和边
  • Oracle SQL - 识别顺序值范围

    这是我的桌子 ID Name Department 1 Michael Marketing 2 Alex Marketing 3 Tom Marketing 4 John Sales 5 Brad Marketing 6 Leo Marke
  • 调用窗口加载事件 - javascript

    我将尽力在这里不使用 jsfiddle 清楚地解释我的问题是什么 因为 window on load 不会在他们的 IDE 中触发 我有一个 html 包装器 它动态加载 ajax html 到div content div class h
  • 在 ASP.NET MVC 3 中添加您自己的 HtmlHelper

    我是 MVC 新手 我正在尝试创建自己的扩展方法 以便我可以添加到我的 razor 视图中可用的 html 帮助器中 Html DropDownListFor 允许您为模型上的任何属性创建下拉列表 我想创建一个名为的助手Html State
  • 替换 jQuery 中选定的 HTML 文本

    我有这个代码用于替换选定的文本 它在选定的文本之前和之后放置 1 和 2 var content text html if window getSelection not IE case var selObj window getSelec
  • 如何获取Meteor包中文件的路径?

    我知道怎么做从 Meteor 包中获取当前目录 但是如何获取项目中特定文件的路径呢 node s dirname and filename在流星中不起作用 这很复杂 meteor run将您的项目文件复制到内部的目录树中
  • --oaa 2 和 --loss_function=logistic 在 Vowpal Wabbit 中的效果

    我应该在 VW 中使用哪些参数来执行二元分类任务 例如 让我们使用rcv1 small dat I thought最好使用逻辑损失函数 或铰链 但使用没有意义 oaa 2 然而 经验结果 所有 4 个实验中报告的渐进验证 0 1 损失 表明
  • 如何防止为未实现方法的对象生成模板

    因此 出于示例的目的 假设我有 3 个简单的struct是 其中第二个不包含bar method struct one void foo const int void bar struct two void foo const int st
  • 每个物种使用多个条目的系统发育模型

    我对系统发育回归模型比较陌生 过去 当我的树中每个物种只有 1 个条目时 我使用 PGLS 现在我有一个包含 9 个物种的数千条记录的数据集 我想运行一个系统发育模型 我阅读了最常见的软件包 例如 caper 的教程 但我不确定如何构建模型
  • jQuery 在第一个 11 后停止“单击”操作

    有两个嵌套元素 两者都有不同的click行动 单击内部元素时 我需要停止外部元素操作 HTML div div div div jQuery out click function alert OUT div is pressed in cl
  • 读取 Magic Mouse 和 Apple 无线键盘的电池百分比

    我想问您是否有人知道在 Mac 操作系统中访问鼠标和键盘电池状态的简单方法 有一些API可以访问这些信息吗 谢谢 对于键盘来说是 ioreg n IOAppleBluetoothHIDDriver grep i batterypercent
  • Ruby:仅在某些情况下重载运算符行为

    我的问题是 如何在内置类 例如 Integer new 上重载运算符 但仅限于某些情况 具体取决于第二个操作数的类 这是我正在寻找的行为 myObject myClass new 1 myObject gt special behaviou
  • 移动向量

    我有一个数据框 我想 对齐 每一列 以便每列的最大值位于同一行 我试图使用基本功能来做到这一点 但得到了错误的结果 即 只是覆盖而不转移 我刚刚在 Hmisc 中找到了 Lag 函数 但是 我确信有一种方法可以在基础上做到这一点 我只是想错
  • 函数式语言中的“模式匹配”是什么?

    我正在阅读有关函数式编程的内容 我注意到模式匹配许多文章都提到它是函数式语言的核心特性之一 有人可以为 Java C JavaScript 开发人员解释一下这是什么意思吗 理解模式匹配需要解释三个部分 代数数据类型 什么是模式匹配 为什么它