F#奇妙游(5):计算π的值

news2024/9/20 5:55:51

F#到底有什么用?

奇妙游写到第五篇,前面的几篇都是开场白:

  1. 一个用F#编写WinForm的例子
  2. donet命令行工具,也就是F#的开发环境
  3. 关于函数和函数式编程的碎碎念
  4. 函数式编程的核心概念:值

下面,我们开始正式来搞点事情,看看F#能做些什么。在此之前,我们再复习F#的运行环境。

F# Interactive

那么F#到底有什么用呢?我们前面说了F#有一个命令行,可以用dotnet fsi打开。这个叫做交互式开发环境,比较现代的语言,都会提供一个交互式的环境,比如Java、Python,都有。而函数式的语言,则更加注重这个环境,原因如下:

函数式的编程,注重由下向上来开发,为了实现一个系统的整体功能,先逐步实现更加底层的功能,慢慢把一个系统整合出来。相区别的是面向对象的程序开发,一开始会投入大量的精力来规划类层次结构、设计类的接口。函数虽然也是接口,但是函数的接口很轻。

每一个小函数的正确性比较容易证明,而正确的函数组合在一起,加上的概念,整个软件系统的正确性也很容易保证;

在反复实验和测试函数的过程,就十分有必要有一个可以输个函数得到一个值的计算器。那些支持F#开发的环境,很容易就配置一个F# Interactive,比如Rider中Shift+Shift,Start F# Interactive就是这个样子:

在这里插入图片描述

在下面的提示符里面就能输入命令,帮助命令输入

#help;;

通过帮助可以看到,F# 交互窗口指令:

#r "file.dll";;                               // 引用(动态加载)给定的 DLL
#i "package source uri";;                     // 搜索包时包含包源 uri
#I "path";;                                   // 为被引用的 DLL 添加给定搜索路径
#load "file.fs" ...;;                         // 像已编译和被引用的文件一样加载给定的文件
#time ["on"|"off"];;                          // 启用/停止计时
#help;;                                       // 显示帮助
#r "nuget:FSharp.Data, 3.1.2";;               // 加载 Nuget 包 'FSharp.Data' 版本 '3.1.2'
#r "nuget:FSharp.Data";;                      // 加载 Nuget 包 'FSharp.Data' 具有最高版本
#clear;;                                      // 清除屏幕
#quit;;                                       // 退出

帮助还会告诉你,F# 交互窗口命令行选项:

  请参阅“dotnet fsi --help”以了解各个选项

总之,我们能够通过运行上面的命令运行一个开发环境,我们也能通过dotnet fsi filename.fsx来执行一个脚本。

计算器

第一个作用:当然是高级计算器。

比如,小朋友问:1+2+3+…+100等于多少?

 [1..100] |> List.sum;;

马上就有val it: int = 55。这个问题可以口算,但是更大的数字怎么办?你说你还是能口算……那算我没说。

上面这里有个奇怪的东西|>,这是一个运算符(术语:管道),其实很简单,就是

List.sum [1..100];;

这么写是因为可以连着写,用|>,比如,100以内所有能被3整除的数和是多少?

[1..100] 
|> List.filter (fun i -> i % 3 = 0) 
|> List.sum;;

得到:

val it: int = 1683

还可以更加复杂:

1 − 1 2 + 1 3 − 1 4 + … 1 - \frac{1}{2} + \frac{1}{3} - \frac{1}{4} + \ldots 121+3141+

[1..1000000] 
|> List.map (fun i-> (-1.0) ** (float i+1.0) / float i) 
|> List.sum;;

计算 π \pi π

利用交互式计算器,可以解决所有小学生的奇怪计算。下面就来点严肃的,计算一下 π \pi π

利用的加拿大滑铁卢大学的Bouweins提出的公式。

y 0 = 2 − 1 , α 0 = 6 − 4 2 y n = 1 − ( 1 − y n − 1 4 ) 1 / 4 1 + ( 1 − y n − 1 4 ) 1 / 4 α n = ( 1 + y n ) 4 α n − 1 − 2 2 n + 3 y n ( 1 + y n + y n 2 ) π = lim ⁡ n → ∞ 1 α n \begin{split} &y_0 = \sqrt{2}-1, \alpha_0=6-4\sqrt{2}\\ &y_{n} = \frac{1-(1-y_{n-1}^4)^{1/4}}{1+(1-y_{n-1}^4)^{1/4}}\\ &\alpha_n = (1+y_n)^4\alpha_{n-1}-2^{2n+3}y_n(1+y_n+y_n^2)\\ &\pi = \lim_{n\to\infty}\frac{1}{\alpha_n} \end{split} y0=2 1,α0=642 yn=1+(1yn14)1/41(1yn14)1/4αn=(1+yn)4αn122n+3yn(1+yn+yn2)π=nlimαn1

这个计算方法非常厉害,只需要迭代15次,精度就能达到20亿位。

那么我们编辑一个fsx文件:

let sqrt (x: decimal) n =
    let rec _sqrt (x: decimal) (rn: decimal) n =
        match n with
        | 0 -> rn
        | _ -> _sqrt x ((rn + x / rn) * 0.5m) (n - 1)

    _sqrt x (x / 3m) n

let quad (x: decimal) = sqrt (sqrt x 32) 32
let sqrt2 = sqrt 2m 32

let yp (y: decimal) =
    let y' = 1.0m - y * y * y * y
    let y'' = quad y'
    (1m - y'') / (1m + y'')

let ap (alpha: decimal) (y: decimal) (n: int) =
    let term1 = (1m + y) * (1m + y) * (1m + y) * (1m + y) * alpha
    let term2 = 2.0 ** (2.0 * float n + 1.0)
    let term3 = y * (1m + y + y * y)
    term1 - (decimal term2) * term3


let alpha n =
    let rec y_a n =
        match n with
        | 0 -> sqrt2 - 1m, 6m - 4m * sqrt2
        | _ ->
            let y_1, alpha_1 = y_a (n - 1)
            let y = yp y_1
            let a = ap alpha_1 y n
            y, a

    let _, a = y_a n
    1m / a

seq {0..10}
|> Seq.map (fun i -> i, alpha i)
|> Seq.iter (fun (i, pi) -> printfn $"%4i{i}\t%A{pi}")

为了得到更多的有效精度,我们采用了decimal数据,这个数据类型最少可以确保28位的精确计算。

首先我们定义了一个参数是decimal的开方运算符,因为.NET针对这个数据类型只有加减乘除等运算,没有开方。

采用迭代法:

x 0 = a / 3 x n + 1 = 1 2 ( x n + a x n ) a = lim ⁡ n → ∞ x n x_0 = a/3 \\ x_{n+1} = \frac{1}{2}(x_n + \frac{a}{x_n})\\ \sqrt{a} = \lim_{n\to\infty}x_n x0=a/3xn+1=21(xn+xna)a =nlimxn

这里使用了尾递归,而不采用循环。在F#中,尾递归会被编译器优化为循环,所以不用担心性能问题和栈溢出问题。

从下面的代码可以看到,F#在编制程序时,会把一小段一小段数学描述实现为一个个函数,这样的代码更加清晰,更加容易理解。

总结

  1. F#可以作为一个交互式计算器,可以解决所有小学生的奇怪计算。
  2. 利用F#很直观地实现数学表达式,所采用的代码不采用循环、不使用变量,而是采用递归,这是函数式编程的典型思考方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/714820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库左、右、内、逗号、全连接(mysql不包含全连接)方式

1、准备数据 学生有归属班级 学生表 班级表 2、执行查询语句 2.1执行左关联 select * from student stu left join class cla on (stu.class_idcla.class_id); 结果如下 2.2执行右关联 2.3、执行内连接 2.4执行逗号分隔表的连接方式 和内连接的查询结果是一样的

实训笔记7.3

实训笔记7.3 7.3一、座右铭二、单例模式三、IDEA集成开发环境的安装和基本使用四、Debug断点调试4.1 作用有两个4.2 用法:4.3 IDEA设置step into进入JDK源码4.4 step over4.5 step into 五、Java中的网络编程5.1 网络编程的三个核心要素5.2 通过Java实现网络编程 7.…

第三章 搜索与图论(二)——最短路问题

文章目录 单源最短路朴素Dijkstra堆优化版DijkstraBellman Ford算法SPFASPFA求负环 多源汇最短路Floyd 最短路练习题849. Dijkstra求最短路 I850. Dijkstra求最短路 II853. 有边数限制的最短路851. spfa求最短路852. spfa判断负环854. Floyd求最短路 源点表示起点,汇…

Linux系统远程挂载Mac OS系统目录方法

打开mac文件共享功能 开启共享服务 进入系统偏好设置中的共享选项。勾中文件共享(如下图),之后右边的文件共享的绿灯会点亮,并显示“文件共享:打开”。 添加共享目录 点击在文件共享界面(如下图&#x…

【狂神】MySQL - 数据库级别的外键

1. 外键 FOREIGN KEY (了解) 测试数据 : 学生表 CREATE TABLE IF NOT EXISTS student (id INT(4) NOT NULL AUTO_INCREMENT COMMENT 学号,name VARCHAR(30) NOT NULL DEFAULT 匿名 COMMENT 姓名,pwd VARCHAR(20) NOT NULL DEFAULT 123456 COMMENT 密码,sex VARC…

【数据结构与算法】 完成用十字链表存储的稀疏矩阵的加法运算

题目: Qestion: 完成用十字链表存储的稀疏矩阵的加法运算。 主要思路: 获取两个稀疏矩阵总有多少个非零元素,记作cnt。当cnt 不为零时一直循环,每循环一次i,也就是行循环,每循环一次就转移至下一行。先从…

Git常用指令总结

1、git init&#xff1a;初始化一个Git仓库&#xff1b; 2、git clone&#xff1a;从远程仓库克隆代码到本地&#xff1b; 直接使用网址 git clone <url>or 用a代替网址 git remote add a <url>git clone a3、git add&#xff1a;添加文件到暂存区&#xff1b; 文件…

K8S数据管理

K8S数据管理 1 数据管理1.1 数据持久化1.1.1 存储方案1.1.2 EmptyDir实践1.1.3 hostPath实践1.1.4 NFS实践 1.2 持久化进阶1.2.1 数据对象1.2.2 PV&PVC实践1.2.3 SC解析1.2.4 SC实践 1.3 配置管理1.3.1 配置基础1.3.2 CM1.3.3 CM案例1.3.4 Secret1.3.5 Secret案例 1.4 状态…

36. QT中使用QFtp实现文件传输1 -- 本地文件或文件夹上传到远程服务器

1. 说明 在使用QT进行嵌入式开发或者是使用到TCP控制传输时,有时程序的正常运行会用到某一个文件或者整个文件夹,此时就需要软件方面将需要的文件或者文件夹传输到远程服务器上。在QT中主要有两种方式可以实现这个功能,一个是QT4中使用QFtp这个类来实现,这个类提供了很丰富…

每天一点Python——day48

#第四十八天 #什么是元组为什么元组没有增删改操作和生成式&#xff1f; 元组&#xff1a;Python内置的数据结构之一&#xff0c;是一个不可变序列 不可变序列&#xff1a;没有增删改操作【例如字符串&#xff0c;元组】 可变序列&#xff1a;可以执行增删改操作&#xff0c;操…

记录好项目D21

记录好项目 你好呀&#xff0c;这里是我专门记录一下从某些地方收集起来的项目&#xff0c;对项目修改&#xff0c;进行添砖加瓦&#xff0c;变成自己的闪亮项目。修修补补也可以成为毕设哦 本次的项目是个基于Springbootvue的景区旅游系统 一、系统介绍 本项目分为管理员与…

534 · 打劫房屋 II

链接&#xff1a;LintCode 炼码 - ChatGPT&#xff01;更高效的学习体验&#xff01; 题解&#xff1a;九章算法 - 帮助更多程序员找到好工作&#xff0c;硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧 处理循环数组问题&#xff1a;分类&#xff0c;重复&#xff0c;取反…

信息安全概述笔记

保密性、完整性、可用性是传统的信息安全的原则和目标&#xff0c;目前随着信息安全问题的日益严峻&#xff0c;信息安全的原则和目标衍生为诸如可控性、不可否认性等其他的原则和目标。 保密性&#xff08;Confidentiality&#xff09;:确保信息只能由那些被授权使用的人获取…

Web服务器群集:四层代理与七层代理

目录 一、理论 1.OSI七层模型 2.四层代理 3.七层代理 4.四层代理与七层代理区别 5.负载均衡器 6.常见的代理组件 7.应用场景 二、总结 一、理论 1.OSI七层模型 &#xff08;1&#xff09;概念 标准的七层网络分层是OSI七层模型&#xff0c;TCP/IP五层模型和TCP/IP四…

Android Matrix的理解

文章目录 前言一.基础1.1 Matrix1.2 使用Matrix的准备知识 二.preXXX和postXXX2.1 右乘和左乘2.2 验证规律 三.坐标原点结束 前言 Android绘制中最重要的要算Matrix类了&#xff0c;同时也是不太好理解的。以前也用过&#xff0c;但是掌握的也不是太好&#xff0c;刚好有时间好…

GO语言包相关总结 -引用(本地和远程),自定义,安装,使用

本篇文章总结以下go语言包相关的知识。 目录 一.导入包 &#xff08;1&#xff09;常规导入 &#xff08;2&#xff09;别名导入 &#xff08;3&#xff09;特殊导入 二.自定义包 三.安装自定义包 四.调用自定义包调用 五.获取远程包 六.go中的保留函数 七.实战 - G…

Python自动化测试实战篇(12),一文学完,Pytest 常用11种第三方插件

这些是之前的文章&#xff0c;里面有一些基础的知识点在前面由于前面已经有写过&#xff0c;所以这一篇就不再详细对之前的内容进行描述 Python自动化测试实战篇&#xff08;1&#xff09; Python自动化测试实战篇&#xff08;2&#xff09; Python自动化测试实战篇&#xff…

解决python-opencv:(-215:Assertion failed) _img.empty() in function ‘cv::imwrite‘在将视频分成帧图片,写入时出现的问题

最近在搞视频检测问题&#xff0c;在用到将视频分帧保存为图片时&#xff0c;图片可以保存&#xff0c;但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行&#xff0c;在检查代码、检查路径等措施均无果后&#xff0c;了解了视频分帧…

rk3399 调试ap6354

电路如下: wifi&#xff1a; 按照rk3399 sdk默认配置&#xff0c;修改相应的引脚 sdio_pwrseq: sdio-pwrseq { compatible "mmc-pwrseq-simple"; clocks <&rk808 1>; clock-names "ext_clock"; pinctrl-nam…

filter功能演示-鉴权、声明缓存

文章目录 Filter定义工作原理Filter所处环节 Demo示例总结 Filter定义 在Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;中&#xff0c;过滤器&#xff08;Filter&#xff09;是一种强大的组件&#xff0c;用于在Web应用程序中拦截和处理传入的请求和响应…