【PL理论】(12) F#:模块 | 命名空间 | 异常处理 | 内置异常 |:? | 相互递归函数

本文主要是介绍【PL理论】(12) F#:模块 | 命名空间 | 异常处理 | 内置异常 |:? | 相互递归函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

  • 💭 写在前面:本章我们将介绍 F# 的模块,我们前几章讲的列表、集合和映射都是模块。然后我们将介绍 F# 中的异常,以及内置异常,最后再讲解一下相互递归函数。

目录

0x00 F# 模块(Module)

0x01 F# 异常处理(Exception)

0x02 内置异常(Built-in Exceptions)

0x03 相互递归函数

0x04 通过泰勒级数展开来逼近计算 e^x


0x00 F# 模块(Module)

用于代码组织和抽象的特性,模块 (Module) 就是相关类型、值和函数的集合。

类似于面向对象编程中的类,但没有对象的概念。

比如我们说的的列表、集合和映射都是模块。

在这个章节后,我会给出一点练习题,方便大家更好地掌握 F# 基础。

为了方便大家没有负担地有效练习,我会在框架代码中勾勒出模块,你只需要填写就行了:

namespace DataStructuremodule Queue =type t = int list * int listlet empty: t = ([], [])let enqueue (i: int) (queue: t) = ...

另外,这个 namespace 就是命名空间,类似于 C++。

0x01 F# 异常处理(Exception)

F# 中也是可以 raise 捕获异常的,raise ... 会被求值为一个异常并传播。

使用 try-with 来捕获引发的异常,异常会被视为 any type,可以是任何类型 ( `a ) 。 

exception DivByZerolet div (x: int) (y: int) : int =if y = 0 then raise DivByZero else x / ylet printDiv (x: int) (y: int) : unit =try printfn "%d" (div x y) with| DivByZero -> printfn "Divisor is zero" 

0x02 内置异常(Built-in Exceptions)

F# 有不少预定义的异常,要捕获这些错误,你必须使用 |:? 

这是因为 F# 与 C# (.NET) 都是一个爹有着密不可分的关系。

这里提供几种还不错的选择,让你避免记住这些复杂的异常名称:

let doFind1 (k: string) (m: Map<string,int>) : int =try Map.find k m with| :? System.Collections.Generic.KeyNotFoundException -> 0let doFind2 (k: string) (m: Map<string,int>) : int =if Map.containsKey k m then Map.find k m else 0let doFind3 (k: string) (m: Map<string,int>) : int =match Map.tryFind k m with| None -> 0 | Some i -> i

0x03 相互递归函数

相互递归函数 (Mutually Recursive Function),指的是多个函数可以相互递归调用。

简单来说就是你递归调用我,我递归调用你,用 let rec ... and 语法来定义这样的函数。

💬 举个例子:我们来定义三个相互递归的函数

let rec f x =x + g (x - 1)and g y =if y <= 1 then 1 else y * h (y - 1)and h z =if z <= 2 then 0 else f (z - 1) + f (z - 2)

这段代码定义了三个相互递归的函数 f,g,h,它们彼此之间互相调用。

形成了一个循环,每个函数的返回值都依赖于其他函数的返回值,从而实现了相互递归。

0x04 通过泰勒级数展开来逼近计算 e^x

通过泰勒级数展开来逼近计算 e^x

① 首先计算 n 的阶乘:

n!=n\times (n-1)\times(n-2)\times...\times2\times1

我们定义一个递归函数 Fac 计算一个非负整数的阶乘,当输入值 n\leq 1 时,返回1。

否则,返回 n 乘以 (n-1)  的阶乘。

在 Tylor 函数中,Fac 被用来计算泰勒级数展开的分母部分,即 n!  。

② 再通过泰勒级数展开公式 (以 e 为底的指数函数) ,我们展开前十项:

e^x=\sum_{n=0 }^{\infty }\frac{x^n}{n!}\, \, \, \Rightarrow \, \, \, e^x\approx \sum_{n=0 }^{10}\frac{x^n}{n!}

再定义一个递归函数 Taylor 计算 e^x 的泰勒级数展开,当展开的级数项数 n=0 时,返回 1.0

否则计算 x^n/n!  并加上递归调用 Taylor 函数计算更低阶的项。

在 Taylor 函数中,Fac 函数被用来计算每一项的阶乘。

💬 代码演示:通过泰勒级数展开来逼近计算 e^x

let rec Fac n =if n <= 1 then 1else n * Fac (n - 1)let rec Taylor x n =if n = 0 then 1.0else (float x ** float n) / float (Fac n) + Taylor x (n - 1)// 计算 e^x 的值
let calculateExponential x =if System.Double.IsNaN(x) || System.Double.IsInfinity(x) theninvalidArg "x" "x must be a finite number"elseTaylor x 10  // 前10项

这两个函数就相互递归了,因为 Taylor 调用了 Fac 来计算阶乘,而 Fac 也会调用 Taylor。

你可以发现,我们没有使用刚才讲的 "0x03 相互递归",let rec ... and。

因为每次计算阶乘都会重新计算泰勒级数的一部分,导致大量的重复计算:

let rec Fac n =if n <= 1 then 1else n * Taylor (n - 1) 1and Taylor x n =if n = 0 then 1.0else (float x ** float n) / float (Fac n) + Taylor x (n - 1)// 计算 e^x 的值
let calculateExponential x =if System.Double.IsNaN(x) || System.Double.IsInfinity(x) theninvalidArg "x" "x must be a finite number"elseTaylor x 10  // 前10项// 测试计算函数
let result = calculateExponential 1.0
printfn "e^1 的值近似为: %f" result


📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2024.6.16
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

这篇关于【PL理论】(12) F#:模块 | 命名空间 | 异常处理 | 内置异常 |:? | 相互递归函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1046122

相关文章

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Java Response返回值的最佳处理方案

《JavaResponse返回值的最佳处理方案》在开发Web应用程序时,我们经常需要通过HTTP请求从服务器获取响应数据,这些数据可以是JSON、XML、甚至是文件,本篇文章将详细解析Java中处理... 目录摘要概述核心问题:关键技术点:源码解析示例 1:使用HttpURLConnection获取Resp

Python中的getopt模块用法小结

《Python中的getopt模块用法小结》getopt.getopt()函数是Python中用于解析命令行参数的标准库函数,该函数可以从命令行中提取选项和参数,并对它们进行处理,本文详细介绍了Pyt... 目录getopt模块介绍getopt.getopt函数的介绍getopt模块的常用用法getopt模

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Java中Switch Case多个条件处理方法举例

《Java中SwitchCase多个条件处理方法举例》Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,:本文主要介绍Java中SwitchCase多个条件处理的相... 目录前言基本语法处理多个条件示例1:合并相同代码的多个case示例2:通过字符串合并多个case进阶用法使用

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

Python处理函数调用超时的四种方法

《Python处理函数调用超时的四种方法》在实际开发过程中,我们可能会遇到一些场景,需要对函数的执行时间进行限制,例如,当一个函数执行时间过长时,可能会导致程序卡顿、资源占用过高,因此,在某些情况下,... 目录前言func-timeout1. 安装 func-timeout2. 基本用法自定义进程subp