目录

F# - 快速指南

F# - Overview

F#是一种函数式编程语言。 要理解F#构造,您需要阅读有关名为Functional Programming的编程范例的几行内容。

功能编程将计算机程序视为数学函数。 在函数式编程中,重点是常量和函数,而不是变量和状态。 因为函数和常量是不会改变的。

在函数式编程中,您将编写模块化程序,即程序将包含将其他函数作为输入的函数。

用函数式编程语言编写的程序往往简洁。

关于F#

以下是关于F#的基本信息 -

  • 它于2005年在Microsoft Research开发。
  • 它是Microsoft的.Net语言家族的一部分。
  • 它是一种函数式编程语言。
  • 它基于函数式编程语言OCaml。

F#的特点

  • 它是OCaml的.Net实现。

  • 它编译.Net CLI(公共语言接口)字节代码或在CLR(公共语言运行时)上运行的MSIL(Microsoft中间语言)。

  • 它提供类型推断。

  • 它提供了丰富的模式匹配结构。

  • 它具有交互式脚本和调试功能。

  • 它允许编写更高阶的函数。

  • 它提供了完善的对象模型。

使用F#

F#通常用于以下领域 -

  • 制作科学模型
  • 数学问题解决
  • 人工智能研究工作
  • Financial modelling
  • 平面设计
  • CPU设计
  • 编译器编程
  • Telecommunications

它还用于CRUD应用程序,网页,GUI游戏和其他通用程序。

F# - Environment Setup

本章将讨论F#编程所需的工具。

F#的集成开发环境(IDE)

Microsoft为F#编程提供Visual Studio 2013。

免费的Visual Studio 2013社区版可从Microsoft官方网站获得。 Visual Studio 2013社区及以上版本附带Visual F#Tools。 安装详细信息可从Asp.net Tutorial获得.Visual F#Tools包括命令行编译器(fsc.exe)和F#Interactive(fsi.exe)。

Visual Studio安装程序

使用这些工具,您可以将各种F#程序从简单的命令行应用程序编写到更复杂的应用程序。 您还可以使用基本文本编辑器(如记事本)编写F#源代码文件,并使用命令行编译器将代码编译为程序集。

您可以从Microsoft Visual Studio下载它。 它会自动安装在您的机器中。

在链接上编写F#程序

请访问F#官方网站,获取有关将工具作为Debian软件包获取或直接从源代码编译的最新说明 - https://fsharp.org/use/linux/.

F# - Program Structure

F#是一种函数式编程语言。

在F#中,函数的工作方式与数据类型相同。 您可以像任何其他变量一样以相同的方式声明和使用函数。

通常,F#应用程序没有任何特定的入口点。 编译器从上到下执行文件中的所有顶级语句。

但是,为了遵循过程编程风格,许多应用程序保留一个调用主循环的顶级语句。

以下代码显示了一个简单的F#程序 -

open System
(* This is a multi-line comment *)
// This is a single-line comment
let sign num =
   if num > 0 then "positive"
   elif num < 0 then "negative"
   else "zero"
let main() =
   Console.WriteLine("sign 5: {0}", (sign 5))
main()

编译并执行程序时,它会产生以下输出 -

sign 5: positive

请注意 -

  • F#代码文件可能以许多用于导入名称空间的open语句开头。

  • 文件正文包括实现应用程序业务逻辑的其他函数。

  • 主循环包含顶部可执行语句。

F# - Basic Syntax

您已经了解了F#程序的基本结构,因此很容易理解F#编程语言的其他基本构建块。

F#中的标记

F#程序由各种令牌组成。 令牌可以是关键字,标识符,常量,字符串文字或符号。 我们可以将F#标记分为两种类型 -

  • Keywords
  • 符号和运算符

F#关键词

下表显示关键字的关键字和简要说明。 我们将在后续章节中讨论这些关键字的使用。

关键词 描述
abstract 表示一种方法,该方法在声明它的类型中没有实现,或者是虚拟的并且具有默认实现。
and 用于相互递归绑定,属性声明以及对泛型参数的多个约束。
as 用于为当前类对象提供对象名称。 还用于在模式匹配中为整个模式命名。
assert 用于在调试期间验证代码。
base 用作基类对象的名称。
begin 在详细语法中,表示代码块的开始。
class 在详细语法中,表示类定义的开始。
default 表示抽象方法的实现; 与抽象方法声明一起使用以创建虚拟方法。
delegate 用于声明委托。
do 用于循环结构或执行命令式代码。
done 在详细语法中,表示循环表达式中的代码块的结尾。
downcast 用于转换为继承链中较低的类型。
downto for表达式中,在反向计数时使用。
elif 用于条件分支。 如果是其他的简短形式。
else Used in conditional branching.
end

在类型定义和类型扩展中,表示成员定义部分的结尾。

在详细语法中,用于指定以begin关键字开头的代码块的结尾。

exception 用于声明异常类型。
extern 指示已声明的程序元素在另一个二进制或程序集中定义。
false 用作布尔文字。
finally 与try一起使用,引入一个代码块,无论是否发生异常都会执行。
for Used in looping constructs.
fun 用于lambda表达式,也称为匿名函数。
function 用作fun关键字的简短替代,以及在单个参数上具有模式匹配的lambda表达式中的匹配表达式。
global 用于引用顶级.NET命名空间。
if 用于条件分支构造。
in 用于序列表达式,在详细语法中,用于将表达式与绑定分开。
inherit 用于指定基类或基接口。
inline 用于指示应直接集成到调用者代码中的函数。
interface 用于声明和实现接口。
internal 用于指定成员在程序集内部可见但不在其外部。
lazy 用于指定仅在需要结果时执行的计算。
let 用于将名称与值或函数关联或绑定。
let! 在异步工作流中用于将名称绑定到异步计算的结果,或者在其他计算表达式中用于将名称绑定到结果(计算类型)。
match 用于通过将值与模式进行比较来进行分支。
member 用于在对象类型中声明属性或方法。
module 用于将名称与一组相关类型,值和函数相关联,以将其与其他代码逻辑分离。
mutable 用于声明变量,即可以更改的值。
namespace 用于将名称与一组相关类型和模块相关联,从逻辑上将其与其他代码分开。
new

用于声明,定义或调用创建或可以创建对象的构造函数。

也用于泛型参数约束,以指示类型必须具有某个构造函数。

not 实际上不是关键字。 但是,组合结构不是用作通用参数约束。
null

表示没有对象。

也用于通用参数约束。

of 用于区分联合以指示值类别的类型,以及委托和异常声明。
open 用于使命名空间或模块的内容可用而无需限定。
or

与布尔条件一起用作布尔值或运算符。 相当于||。

也用于成员约束。

override 用于实现与基本版本不同的抽象或虚拟方法的版本。
private 限制对成员的访问以在相同类型或模块中进行编码。
public 允许从类型外部访问成员。
rec 用于表示函数是递归的。
return 用于指示作为计算表达式的结果提供的值。
return! 用于指示计算表达式,在计算表达式时,它提供包含计算表达式的结果。
select 在查询表达式中用于指定要提取的字段或列。 请注意,这是一个上下文关键字,这意味着它实际上不是一个保留字,它只在适当的上下文中表现为关键字。
static 用于指示可以在没有类型实例的情况下调用的方法或属性,或者在类型的所有实例之间共享的值成员。
struct

用于声明结构类型。

也用于通用参数约束。

用于模块定义中的OCaml兼容性。

then

用于条件表达式。

也用于在对象构造之后执行副作用。

to 用于for循环以指示范围。
true 用作布尔文字。
try 用于引入可能生成异常的代码块。 with或一起使用。
type 用于声明类,记录,结构,区分联合,枚举类型,度量单位或类型缩写。
upcast 用于转换为继承链中较高的类型。
use 对于需要调用Dispose以释放资源的值,使用而不是let。
use! 用而不是让! 在异步工作流和其他计算表达式中,需要调用Dispose来释放资源。
val 在有限的情况下,在签名中用于指示值,或用于声明成员的类型。
void 表示.NET void类型。 与其他.NET语言互操作时使用。
when 用于模式匹配的布尔条件(when guards) ,并为泛型类型参数引入约束子句。
while Introduces a looping construct.
with 与模式匹配表达式中的match关键字一起使用。 还用于对象表达式,记录复制表达式和类型扩展以引入成员定义,并引入异常处理程序。
yield 用于序列表达式以生成序列的值。
yield! 在计算表达式中使用,以将给定计算表达式的结果附加到包含计算表达式的结果集合。

一些保留的关键字来自OCaml语言 -

asrlandlorlsllsrlxormodsig

保留一些其他保留关键字以供将来扩展F#。

atomicbreakcheckedcomponentconstconstraintconstructor
continueeagereventexternalfixedfunctorinclude
methodmixinobjectparallelprocessprotectedpure
sealedtailcalltraitvirtualvolatile

F#中的评论

F#提供两种类型的评论 -

  • 一行注释以//符号开头。
  • 多行注释以(*并以*结尾)开头。

F#中的基本程序和应用程序入口点

通常,您没有任何明确的F#程序入口点。 编译F#应用程序时,提供给编译器的最后一个文件将成为入口点,该文件中的所有顶级语句将从上到下执行。

一个编写良好的程序应该有一个顶级语句,可以调用该程序的主循环。

一个极简主义的F#程序,它将在屏幕上显示“Hello World” -

(* This is a comment *)
(* Sample Hello World program using F# *)
printfn "Hello World!"

编译并执行程序时,它会产生以下输出 -

Hello World!

F# - Data Types

F#中的数据类型可分为以下几类 -

  • 积分类型
  • 浮点类型
  • 文字类型
  • 其他类型

积分数据类型

下表提供了F#的整数数据类型。 这些基本上是整数数据类型。

F#类型 尺寸 范围 备注
sbyte 1个字节 -128 to 127

42y

-11y

8-bit signed integer
byte 1个字节 0到255

42uy

200uy

8-bit unsigned integer
int16 2个字节 -32768 to 32767

42s

-11s

16-bit signed integer
uint16 2个字节 0 to 65,535

42us

200us

16-bit unsigned integer
int/int32 4字节 -2,147,483,648 to 2,147,483,647

42

-11

32-bit signed integer
uint32 4字节 0 to 4,294,967,295

42u

200u

32-bit unsigned integer
int64 8个字节 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

42L

-11L

64-bit signed integer
uint64 8个字节 0 to 18,446,744,073,709,551,615

42UL

200UL

64-bit unsigned integer
bigint 至少4个字节 任何整数

42I

1499999

9999999

9999999

9999999

9999I

arbitrary precision integer

例子 (Example)

(* single byte integer *)
let x = 268.97f
let y = 312.58f
let z = x + y
printfn "x: %f" x
printfn "y: %f" y
printfn "z: %f" z
(* unsigned 8-bit natural number *)
let p = 2uy
let q = 4uy
let r = p + q
printfn "p: %i" p
printfn "q: %i" q
printfn "r: %i" r
(* signed 16-bit integer *)
let a = 12s
let b = 24s
let c = a + b
printfn "a: %i" a
printfn "b: %i" b
printfn "c: %i" c
(* signed 32-bit integer *)
let d = 212l
let e = 504l
let f = d + e
printfn "d: %i" d
printfn "e: %i" e
printfn "f: %i" f

编译并执行程序时,它会产生以下输出 -

x: 1
y: 2
z: 3
p: 2
q: 4
r: 6
a: 12
b: 24
c: 36
d: 212
e: 504
f: 716

浮点数据类型

下表提供了F#的浮点数据类型。

F#类型 尺寸 范围 备注
float32 4字节 ±1.5e-45 to ±3.4e38

42.0F

-11.0F

32位带符号浮点数(7位有效数字)
float 8个字节 ±5.0e-324 to ±1.7e308

42.0

-11.0

64位带符号浮点数(15-16位有效数字)
decimal 16个字节 ±1.0e-28 to ±7.9e28

42.0M

-11.0M

128位带符号浮点数(28-29位有效数字)
BigRational 至少4个字节 Any rational number.

42N

-11N

任意精度有理数。 使用此类型需要引用FSharp.PowerPack.dll。

例子 (Example)

(* 32-bit signed floating point number *)
(* 7 significant digits *)
let d = 212.098f
let e = 504.768f
let f = d + e
printfn "d: %f" d
printfn "e: %f" e
printfn "f: %f" f
(* 64-bit signed floating point number *)
(* 15-16 significant digits *)
let x = 21290.098
let y = 50446.768
let z = x + y
printfn "x: %g" x
printfn "y: %g" y
printfn "z: %g" z

编译并执行程序时,它会产生以下输出 -

d: 212.098000
e: 504.768000
f: 716.866000
x: 21290.1
y: 50446.8
z: 71736.9

文本数据类型

下表提供了F#的文本数据类型。

F#类型 尺寸 范围 备注
char 2个字节 U + 0000到U + ffff

'x'

'\t'

Single unicode characters
string 20 +(2 *字符串的长度)字节 0到大约20亿个字符

"Hello"

"World"

Unicode text

例子 (Example)

let choice = 'y'
let name = "Zara Ali"
let org = "IOWIKI"
printfn "Choice: %c" choice
printfn "Name: %s" name
printfn "Organisation: %s" org

编译并执行程序时,它会产生以下输出 -

Choice: y
Name: Zara Ali
Organisation: IOWIKI

其他数据类型

下表提供了F#的其他一些数据类型。

F#类型 尺寸 范围 备注
bool 1个字节 只有两个可能的值,true或false

true

false

Stores boolean values

例子 (Example)

let trueVal = true
let falseVal = false
printfn "True Value: %b" (trueVal)
printfn "False Value: %b" (falseVal)

编译并执行程序时,它会产生以下输出 -

True Value: true
False Value: false

F# - Variables

变量是指给我们的程序可以操作的存储区域的名称。 每个变量都有一个特定的类型,它决定了变量内存的大小和布局; 可存储在该内存中的值范围; 以及可以应用于变量的操作集。

F#中的变量声明

let关键字用于变量声明 -

例如,

let x = 10

它声明了一个变量x并为其赋值10。

您还可以为变量指定表达式 -

let x = 10
let y = 20
let z = x + y

以下示例说明了这一概念 -

例子 (Example)

let x = 10
let y = 20
let z = x + y
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z

编译并执行程序时,它会产生以下输出 -

x: 10
y: 20
z: 30

F#中的变量是immutable,这意味着一旦变量绑定到某个值,就无法更改。 它们实际上被编译为静态只读属性。

以下示例演示了这一点。

例子 (Example)

let x = 10
let y = 20
let z = x + y
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z
let x = 15
let y = 20
let z = x + y
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z

编译并执行该程序时,它显示以下错误消息 -

Duplicate definition of value 'x'
Duplicate definition of value 'Y'
Duplicate definition of value 'Z'

变量定义与类型声明

变量定义告诉编译器应该创建变量的存储位置和数量。 变量定义可以指定数据类型,并包含该类型的一个或多个变量的列表,如以下示例所示。

例子 (Example)

let x:int32 = 10
let y:int32 = 20
let z:int32 = x + y
printfn "x: %d" x
printfn "y: %d" y
printfn "z: %d" z
let p:float = 15.99
let q:float = 20.78
let r:float = p + q
printfn "p: %g" p
printfn "q: %g" q
printfn "r: %g" r

编译并执行该程序时,它显示以下错误消息 -

x: 10
y: 20
z: 30
p: 15.99
q: 20.78
r: 36.77

可变变量

有时您需要更改存储在变量中的值。 要指定声明和赋值变量的值可能发生更改,在程​​序的后续部分中,F#提供mutable关键字。 您可以使用此关键字声明和分配可变变量,您将更改其值。

mutable关键字允许您在可变变量中声明和赋值。

您可以使用let关键字为可变变量分配一些初始值。 但是,要为其分配新的后续值,您需要使用运算符。

例如,

let mutable x = 10
x ← 15

以下示例将清除概念 -

例子 (Example)

let mutable x = 10
let y = 20
let mutable z = x + y
printfn "Original Values:"
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z
printfn "Let us change the value of x"
printfn "Value of z will change too."
x <- 15
z <- x + y
printfn "New Values:"
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z

编译并执行程序时,它会产生以下输出 -

Original Values:
x: 10
y: 20
z: 30
Let us change the value of x
Value of z will change too.
New Values:
x: 15
y: 20
z: 35

F# - Operators

运算符是一个符号,告诉编译器执行特定的数学或逻辑操作。 F#内置运算符丰富,提供以下类型的运算符 -

  • 算术运算符
  • 比较运算符
  • 布尔运算符
  • 按位运算符

算术运算符 (Arithmetic Operators)

下表显示了F#语言支持的所有算术运算符。 假设变量A保持10,变量B保持20然后 -

显示示例

操作者 描述
+ 添加两个操作数 A + B将给出30
- 从第一个减去第二个操作数 A - B将给-10
* 将两个操作数相乘 A * B将给出200
/Divides numerator by de-numerator B/A会给2
% 模数运算符和整数除法后的余数 B%A将给出0
** 指数运算符,为另一个运算符提供操作数 B ** A将给出20 10

比较运算符 (Comparison Operators)

下表显示了F#语言支持的所有比较运算符。 这些二进制比较运算符可用于整数和浮点类型。 这些运算符返回bool类型的值。

假设变量A保持10,变量B保持20,则 -

显示示例

操作者 描述
= 检查两个操作数的值是否相等,如果是,则条件变为真。 (A == B)不是真的。
<> 检查两个操作数的值是否相等,如果值不相等则条件变为真。 (A <> B)是真的。
> 检查左操作数的值是否大于右操作数的值,如果是,则条件变为真。 (A> B)不是真的。
< 检查左操作数的值是否小于右操作数的值,如果是,则条件变为真。 (A < B) 为真
>= 检查左操作数的值是否大于或等于右操作数的值,如果是,则条件变为真。 (A> = B)不是真的。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件变为真。 (A <= B)是真的。

布尔运算符

下表显示了F#语言支持的所有布尔运算符。 假设变量A保持为true ,变量B保持为false,则 -

显示示例

操作者 描述
&& 称为布尔AND运算符。 如果两个操作数都不为零,则条件成立。 (A && B)是假的。
|| 称为布尔OR运算符。 如果两个操作数中的任何一个非零,则条件变为真。 (A || B)是真的。
not 称为布尔NOT运算符。 用于反转其操作数的逻辑状态。 如果条件为真,则Logical NOT运算符将为false。 不(A && B)是真的。

按位运算符 (Bitwise Operators)

按位运算符处理位并执行逐位运算。 &&&(按位AND),|||的真值表 (按位OR),^^^(按位异或)如下 -

显示示例

pq p &&& q p ||| q p ^^^ q
00000
01011
11110
10011

假设A = 60; 和B = 13; 现在以二进制格式,他们将如下 -

A = 0011 1100

B = 0000 1101

-----------------

A &&& B = 0000 1100

A ||| B = 0011 1101

A ^^^ B = 0011 0001

~~~ A = 1100 0011

F#语言支持的Bitwise运算符如下表所示。 假设变量A保持60,变量B保持13,则 -

操作者 描述
&&& 如果二进制AND运算符存在于两个操作数中,则它会将结果复制到结果中。 (A &&& B)将给出12,即0000 1100
||| 二进制OR运算符如果存在于任一操作数中,则复制一位。 (A ||| B)将给出61,即0011 1101
^^^ 二进制异或运算符如果在一个操作数中设置但不在两个操作数中设置,则复制该位。 (A ^^^ B)将给出49,即0011 0001
~~~ 二元一元补语运算符是一元的,具有“翻转”位的效果。 (~~~ A)将给出-61,即2的补码形式的1100 0011。
<<< 二进制左移运算符。 左操作数值向左移动右操作数指定的位数。 <<< 2将给出240,即1111 0000
>>> 二进制右移运算符。 左操作数值向右移动右操作数指定的位数。 A >>> 2将给出15,即0000 1111

运算符优先

下表显示了F#语言中运算符和其他表达式关键字的优先顺序,从最低优先级到最高优先级。

显示示例

操作者 关联性
asRight
whenRight
| (pipe)Left
;Right
letNon associative
function, fun, match, tryNon associative
ifNon associative
Right
:=Right
,Non associative
or, ||Left
&, &&Left
op,=,| op,&op Left
&&&,|||,^^^,~~~,<<>> Left
^ op Right
::Right
:?>, :?Non associative
- op, +op, (binary)Left
* op,/ op,%op Left
** op Right
f x (function application)Left
| (pattern match)Right
prefix operators (+op, -op, %, %%, &, &&, !op, ~op)Left
.Left
f(x)Left
f<types>Left

F# - Decision Making

决策结构要求程序员指定一个或多个要由程序评估或测试的条件。 如果条件被确定为真,则它应与要执行的一个或多个语句一起,并且如果确定条件为假,则应该可选地执行其他语句。

以下是大多数编程语言中常见决策结构的一般形式 -

做决定

F#编程语言提供以下类型的决策制定语句。

声明 描述
if /then statement if/then statement由一个布尔表达式后跟一个或多个语句组成。
if/then/ else statement if/then statement后面可以跟一个可选的else statement,else statement,在布尔表达式为false时执行。
if/then/elif/else statement if/then/elif/else语句允许您拥有多个else分支。
嵌套if语句 您可以在另一个ifelse if语句中使用ifelse if语句。

F# - Loops

编程语言提供各种控制结构,允许更复杂的执行路径。

循环语句允许我们多次执行语句或语句组,以下是大多数编程语言中循环语句的一般形式 -

循环声明

F#提供以下类型的循环来处理循环要求。

循环类型 描述
for ... to ... for downto expressions for ... to表达式用于循环遍历循环变量的一系列值。 for ... downto表达式减少了循环变量的值。
for ... in expression 这种形式的for循环用于迭代项集合,即循环集合和序列
While…do 循环 在给定条件为真时重复语句或语句组。 它在执行循环体之前测试条件。
嵌套循环 您可以在任何其他for或while循环中使用一个或多个循环。

F# - 函数

在F#中,函数的工作方式与数据类型相同。 您可以像任何其他变量一样以相同的方式声明和使用函数。

由于函数可以像任何其他变量一样使用,您可以 -

  • 创建一个具有名称的函数,并将该名称与类型相关联。
  • Assign it a value.
  • 对该值执行一些计算。
  • 将其作为参数传递给另一个函数或子例程。
  • 作为另一个函数的结果返回一个函数。

定义一个函数 (Defining a Function)

使用let关键字定义函数。 函数定义具有以下语法 -

let [inline] function-name parameter-list [ : return-type ]
= function-body

Where,

  • function-name是表示函数的标识符。

  • parameter-list给出由空格分隔的参数列表。 您还可以为每个参数指定显式类型,如果未指定,则编译器倾向于从函数体中推导出它(如变量)。

  • function-body由表达式或由多个表达式组成的复合表达式组成。 函数体中的最终表达式是返回值。

  • return-type是冒号,后跟一个类型,是可选的。 如果未指定返回类型,则编译器将从函数体中的最终表达式确定它。

函数的参数

您可以在函数名称后面列出参数的名称。 您可以指定参数的类型。 参数的类型应遵循以冒号分隔的参数名称。

如果未指定参数类型,则由编译器推断。

例如 -

let doubleIt (x : int) = 2 * x

调用函数

通过指定函数名称后跟空格然后用空格分隔的任何参数来调用函数。

例如 -

let vol = cylinderVolume 3.0 5.0

以下程序说明了这些概念。

例子1 (Example 1)

当半径和长度作为参数给出时,以下程序计算圆柱体的体积

// the function calculates the volume of
// a cylinder with radius and length as parameters
let cylinderVolume radius length : float =
   // function body
   let pi = 3.14159
   length * pi * radius * radius
let vol = cylinderVolume 3.0 5.0
printfn " Volume: %g " vol

编译并执行程序时,它会产生以下输出 -

Volume: 141.372

例子2 (Example 2)

以下程序返回两个给定参数的较大值 -

// the function returns the larger value between two
// arguments
let max num1 num2 : int32 =
   // function body
   if(num1>num2)then
      num1
   else
      num2
let res = max 39 52
printfn " Max Value: %d " res

编译并执行程序时,它会产生以下输出 -

Max Value: 52

例子3 (Example 3)

let doubleIt (x : int) = 2 * x
printfn "Double 19: %d" ( doubleIt(19))

编译并执行程序时,它会产生以下输出 -

Double 19: 38

递归函数 (Recursive Functions)

递归函数是自称的函数。

您可以使用let rec关键字组合定义递归。

定义递归函数的语法是 -

//Recursive function definition
let rec function-name parameter-list = recursive-function-body

例如 -

let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)

例子1 (Example 1)

以下程序返回Fibonacci 1到10 -

let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)
for i = 1 to 10 do
   printfn "Fibonacci %d: %d" i (fib i)

编译并执行程序时,它会产生以下输出 -

Fibonacci 1: 1
Fibonacci 2: 2
Fibonacci 3: 3
Fibonacci 4: 5
Fibonacci 5: 8
Fibonacci 6: 13
Fibonacci 7: 21
Fibonacci 8: 34
Fibonacci 9: 55
Fibonacci 10: 89

例子2 (Example 2)

以下程序返回factorial 8 -

open System
let rec fact x =
   if x < 1 then 1
   else x * fact (x - 1)
Console.WriteLine(fact 8)

编译并执行程序时,它会产生以下输出 -

40320

F#中的箭头符号

F#使用链式箭头表示法报告函数和值中的数据类型。 让我们举一个带有一个int输入的函数的例子,并返回一个字符串。 在箭头符号中,它被写为 -

int -> string

数据类型从左到右读取。

让我们采用另一个假设函数,它接受两个int数据输入并返回一个字符串。

let mydivfunction x y = (x/y).ToString();;

F#使用链式箭头表示法报告数据类型 -

val mydivfunction : x:int -> y:int -> string

返回类型由链式箭头表示法中最右边的数据类型表示。

更多例子 -

符号 含义
float→float→float 该函数接受两个float输入,返回另一个float
int→string→float 该函数接受一个int和一个string输入,返回一个float

Lambda表达式

lambda expression是一个未命名的函数。

让我们举两个函数的例子 -

let applyFunction ( f: int -> int -> int) x y = f x y
let mul x y = x * y
let res = applyFunction mul 5 7
printfn "%d" res

编译并执行程序时,它会产生以下输出 -

35

现在在上面的例子中,如果不是定义函数mul,我们可以使用lambda表达式 -

let applyFunction ( f: int -> int -> int) x y = f x y
let res = applyFunction (fun x y -> x * y ) 5 7
printfn "%d" res

编译并执行程序时,它会产生以下输出 -

35

功能组合和流水线

在F#中,一个函数可以由其他函数组成。

以下示例显示了一个名为f的函数的组成,它来自两个函数function1和function2 -

let function1 x = x + 1
let function2 x = x * 5
let f = function1 >> function2
let res = f 10
printfn "%d" res

编译并执行程序时,它会产生以下输出 -

55

F#还提供了一个称为功能pipelining of functions. 流水线操作允许函数调用作为连续操作链接在一起。

以下示例显示 -

let function1 x = x + 1
let function2 x = x * 5
let res = 10 |> function1 |> function2
printfn "%d" res

编译并执行程序时,它会产生以下输出 -

55

F# - Strings

在F#中,字符串类型将不可变文本表示为Unicode字符序列。

字符串常量 (String Literals)

字符串文字由引号(“)字符分隔。

一些特殊字符用于特殊用途,如换行符,制表符等。它们使用反斜杠(\)字符进行编码。 反斜杠字符和相关字符构成转义序列。 下表显示了F#支持的转义序列。

字符 逃脱序列
Backspace\b
Newline\n
Carriage return\r
Tab\t
Backslash\\
Quotation mark\"
Apostrophe\'
Unicode character \uXXXX或\ UXXXXXXXX(其中X表示十六进制数字)

审视逃逸序列的方法

以下两种方法使编译器忽略转义序列 -

  • 使用@符号。
  • 用三重引号括起字符串。

当字符串文字前面有@符号时,它被称为verbatim string. 这样,除了两个引号字符被解释为一个引号字符外,字符串中的所有转义序列都被忽略。

当字符串被三引号括起来时,也会忽略所有转义序列,包括双引号字符。

例子 (Example)

以下示例演示了此技术,说明如何使用包含嵌入式引号的XML或其他结构 -

// Using a verbatim string
let xmldata = @"<book author=""Lewis, C.S"" title=""Narnia"">"
printfn "%s" xmldata

编译并执行程序时,它会产生以下输出 -

<book author="Lewis, C.S" title="Narnia">

字符串的基本运算符

下表显示了字符串的基本操作 -

描述
collect:(char→string)→string→string 创建一个新字符串,其字符是将指定函数应用于输入字符串的每个字符并连接结果字符串的结果。
concat:string→seq →string 返回通过将给定字符串与分隔符连接而生成的新字符串。
存在:( char→bool)→string→bool 测试字符串的任何字符是否满足给定的谓词。
forall :( char→bool)→string→bool 测试字符串中的所有字符是否满足给定的谓词。
init:int→(int→string)→string 创建一个新字符串,其字符是将指定函数应用于每个索引并连接结果字符串的结果。
iter :( char→unit)→string→unit 将指定的函数应用于字符串中的每个字符。
iteri:(int→char→unit)→string→unit 将指定的函数应用于字符串中每个字符的索引和字符本身。
length:string→int 返回字符串的长度。
map:(char→char)→string→string 创建一个新字符串,其字符是将指定函数应用于输入字符串的每个字符的结果。
mapi:(int→char→char)→string→string 创建一个新字符串,其字符是将指定函数应用于输入字符串的每个字符和索引的结果。
replicate:int→string→string 通过连接指定数量的字符串实例来返回字符串。

以下示例演示了上述某些功能的用途 -

例子1 (Example 1)

String.collect函数构建一个新字符串,其字符是将指定函数应用于输入字符串的每个字符并连接结果字符串的结果。

let collectTesting inputS =
   String.collect (fun c -> sprintf "%c " c) inputS
printfn "%s" (collectTesting "Happy New Year!")

编译并执行程序时,它会产生以下输出 -

H a p p y N e w Y e a r !

例子2 (Example 2)

String.concat函数将给定的字符串序列与分隔符连接起来并返回一个新字符串。

let strings = [ "IOWIKI"; "Coding Ground"; "Absolute Classes" ]
let ourProducts = String.concat "\n" strings
printfn "%s" ourProducts

编译并执行程序时,它会产生以下输出 -

IOWIKI
Coding Ground
Absolute Classes

例子3 (Example 3)

String.replicate方法通过连接指定数量的字符串实例来返回字符串。

printfn "%s" <| String.replicate 10 "*! "

编译并执行程序时,它会产生以下输出 -

*! *! *! *! *! *! *! *! *! *!

F# - Options

当可能存在或不存在变量或函数的值时,F#中的option类型用于计算。 选项类型用于表示计算中的可选值。 它们可以有两个可能的值 - Some(x)None

例如,执行除法的函数将在正常情况下返回值,但在零分母的情况下将抛出异常。 在此处使用选项将有助于指示功能是成功还是失败。

选项具有基础类型,可以保存该类型的值,也可以没有值。

使用选项

我们举一个除法函数的例子。 以下程序解释了这一点 -

让我们编写一个函数div,并向它发送两个参数20和5 -

let div x y = x/y
let res = div 20 5
printfn "Result: %d" res

编译并执行程序时,它会产生以下输出 -

Result: 4

如果第二个参数为零,则程序抛出异常 -

let div x y = x/y
let res = div 20 0
printfn "Result: %d" res

编译并执行程序时,它会产生以下输出 -

Unhandled Exception:
System.DivideByZeroException: Division by zero

在这种情况下,我们可以使用选项类型在操作成功时返回Some(值),如果操作失败则返回None。

以下示例演示了选项的使用 -

例子 (Example)

let div x y =
   match y with
   | 0 -> None
   | _ -> Some(x/y)
let res : int option = div 20 4
printfn "Result: %A " res

编译并执行程序时,它会产生以下输出 -

Result: Some 5

选项属性和方法

选项类型支持以下属性和方法 -

财产或方法 类型 描述
None'T option 一个静态属性,使您可以创建具有None value的选项None value
IsNonebool 如果该选项具有None值,则返回true
IsSomebool 如果选项的值不是None ,则返回true
Some'T option 一个静态成员,用于创建一个值为None的选项。
Value'T 返回基础值,如果值为None ,则抛出NullReferenceException。

例子1 (Example 1)

let checkPositive (a : int) =
   if a > 0 then
      Some(a)
   else
      None
let res : int option = checkPositive(-31)
printfn "Result: %A " res

编译并执行程序时,它会产生以下输出 -

Result: <null>

例子2 (Example 2)

let div x y =
   match y with
   | 0 -> None
   | _ -> Some(x/y)
let res : int option = div 20 4
printfn "Result: %A " res
printfn "Result: %A " res.Value

编译并执行程序时,它会产生以下输出 -

Result: Some 5
Result: 5

例子3 (Example 3)

let isHundred = function
   | Some(100) -> true
   | Some(_) | None -> false
printfn "%A" (isHundred (Some(45)))
printfn "%A" (isHundred (Some(100)))
printfn "%A" (isHundred None)

编译并执行程序时,它会产生以下输出 -

false
true
false

F# - Tuples

tuple是以逗号分隔的值集合。 这些用于创建临时数据结构,将相关值组合在一起。

例如,(“Zara Ali”,“Hyderabad”,10)是一个包含两个字符串值和一个int值的3元组,它具有类型(string * string * int)。

元组可以是相同或不同类型的对,三元组等。

这里提供了一些例子 -

// Tuple of two integers.
( 4, 5 )
// Triple of strings.
( "one", "two", "three" )
// Tuple of unknown types.
( a, b )
// Tuple that has mixed types.
( "Absolute Classes", 1, 2.0 )
// Tuple of integer expressions.
( a * 4, b + 7)

例子 (Example)

这个程序有一个函数,它接受四个浮点值的元组并返回平均值 -

let averageFour (a, b, c, d) =
   let sum = a + b + c + d
   sum/4.0
let avg:float = averageFour (4.0, 5.1, 8.0, 12.0)
printfn "Avg of four numbers: %f" avg

编译并执行程序时,它会产生以下输出 -

Avg of four numbers: 7.275000

访问单个元组成员

可以使用模式匹配来评估和打印元组的各个成员。

以下示例说明了这一概念 -

例子 (Example)

let display tuple1 =
   match tuple1 with
   | (a, b, c) -> printfn "Detail Info: %A %A %A" a b c
display ("Zara Ali", "Hyderabad", 10 )

编译并执行程序时,它会产生以下输出 -

Detail Info: "Zara Ali" "Hyderabad" 10

F#有两个内置函数fstsnd,它们返回2元组中的第一个和第二个项目。

以下示例说明了这一概念 -

例子 (Example)

printfn "First member: %A" (fst(23, 30))
printfn "Second member: %A" (snd(23, 30))
printfn "First member: %A" (fst("Hello", "World!"))
printfn "Second member: %A" (snd("Hello", "World!"))
let nameTuple = ("Zara", "Ali")
printfn "First Name: %A" (fst nameTuple)
printfn "Second Name: %A" (snd nameTuple)

编译并执行程序时,它会产生以下输出 -

First member: 23
Second member: 30
First member: "Hello"
Second member: "World!"
First Name: "Zara"
Second Name: "Ali"

F# - Records

record类似于元组,但它包含命名字段。 例如,

type website =
   { title : string;
      url : string }

定义记录

记录定义为使用type关键字的type ,记录的字段定义为以分号分隔的列表。

定义记录的语法是 -

type recordName =
   { [ fieldName : dataType ] + }

创建记录

您可以通过指定记录的字段来创建记录。 例如,让我们创建一个名为homepagewebsite记录 -

let homepage = { Title = "IoWiki"; Url = "www.iowiki.com" }

以下示例将解释这些概念 -

例子1 (Example 1)

该程序定义了一个名为网站的记录类型。 然后它创建一些类型网站的记录并打印记录。

(* defining a record type named website *)
type website =
   { Title : string;
      Url : string }
(* creating some records *)
let homepage = { Title = "IoWiki"; Url = "www.iowiki.com" }
let cpage = { Title = "Learn C"; Url = "www.iowiki.com/cprogramming/index.htm" }
let fsharppage = { Title = "Learn F#"; Url = "www.iowiki.com/fsharp/index.htm" }
let csharppage = { Title = "Learn C#"; Url = "www.iowiki.com/csharp/index.htm" }
(*printing records *)
(printfn "Home Page: Title: %A \n \t URL: %A") homepage.Title homepage.Url
(printfn "C Page: Title: %A \n \t URL: %A") cpage.Title cpage.Url
(printfn "F# Page: Title: %A \n \t URL: %A") fsharppage.Title fsharppage.Url
(printfn "C# Page: Title: %A \n \t URL: %A") csharppage.Title csharppage.Url

编译并执行程序时,它会产生以下输出 -

Home Page: Title: "IoWiki"
       URL: "www.iowiki.com"
C Page: Title: "Learn C"
      URL: "www.iowiki.com/cprogramming/index.htm"
F# Page: Title: "Learn F#"
      URL: "www.iowiki.com/fsharp/index.htm"
C# Page: Title: "Learn C#"
      URL: "www.iowiki.com/csharp/index.htm"

例子2 (Example 2)

type student =
   { Name : string;
      ID : int;
      RegistrationText : string;
      IsRegistered : bool }
let getStudent name id =
   { Name = name; ID = id; RegistrationText = null; IsRegistered = false }
let registerStudent st =
   { st with
      RegistrationText = "Registered";
      IsRegistered = true }
let printStudent msg st =
   printfn "%s: %A" msg st
let main() =
   let preRegisteredStudent = getStudent "Zara" 10
   let postRegisteredStudent = registerStudent preRegisteredStudent
   printStudent "Before Registration: " preRegisteredStudent
   printStudent "After Registration: " postRegisteredStudent
main()

编译并执行程序时,它会产生以下输出 -

Before Registration: : {Name = "Zara";
   ID = 10;
   RegistrationText = null;
   IsRegistered = false;}
After Registration: : {Name = "Zara";
   ID = 10;
   RegistrationText = "Registered";
   IsRegistered = true;}

F# - Lists

在F#中,列表是有序的,不可变的相同类型的元素系列。 它在某种程度上等同于链表数据结构。

F#模块Microsoft.FSharp.Collections.List,对列表具有常见操作。 但是,F#会自动导入此模块,并使每个F#应用程序都可以访问它。

创建和初始化列表

以下是创建列表的各种方法 -

  • 使用列表literals

  • 使用cons (::)运算符。

  • 使用List模块的List.init方法。

  • 使用一些名为List Comprehensions syntactic constructs

列出文字

在此方法中,您只需在方括号中指定以分号分隔的值序列。 例如 -

let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

The cons (::) Operator

使用此方法,您可以使用::运算符通过在现有列表中添加或使用它来添加一些值。 例如 -

let list2 = 1::2::3::4::5::6::7::8::9::10::[];;

[]表示一个空列表。

列出init方法

List模块的List.init方法通常用于创建列表。 这种方法有类型 -

val init : int -> (int -> 'T) -> 'T list

第一个参数是新列表的所需长度,第二个参数是初始化函数,它在列表中生成项。

例如,

let list5 = List.init 5 (fun index -> (index, index * index, index * index * index))

这里,索引函数生成列表。

列表理解

列表推导是用于生成列表的特殊语法结构。

F#list comprehension语法有两种形式 - 范围和生成器。

范围有结构 - [start .. end]和[start .. step .. end]

例如,

let list3 = [1 .. 10]

生成器有构造 - [for x in collection do ... yield expr]

例如,

let list6 = [ for a in 1 .. 10 do yield (a * a) ]

yield关键字将单个值推送到列表中时,关键字yield!,将一组值推送到列表中。

以下函数演示了上述方法 -

例子 (Example)

(* using list literals *)
let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
(*using cons operator *)
let list2 = 1 :: 2 :: 3 :: []
printfn "The list: %A" list2
(* using range constructs*)
let list3 = [1 .. 10]
printfn "The list: %A" list3
(* using range constructs *)
let list4 = ['a' .. 'm']
printfn "The list: %A" list4
(* using init method *)
let list5 = List.init 5 (fun index -> (index, index * index, index * index * index))
printfn "The list: %A" list5
(* using yield operator *)
let list6 = [ for a in 1 .. 10 do yield (a * a) ]
printfn "The list: %A" list6
(* using yield operator *)
let list7 = [ for a in 1 .. 100 do if a % 3 = 0 && a % 5 = 0 then yield a]
printfn "The list: %A" list7
(* using yield! operator *)
let list8 = [for a in 1 .. 3 do yield! [ a .. a + 3 ] ]
printfn "The list: %A" list8

编译并执行程序时,它会产生以下输出 -

The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The list: [1; 2; 3]
The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The list: ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm']
The list: [(0, 0, 0); (1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64)]
The list: [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
The list: [15; 30; 45; 60; 75; 90]
The list: [1; 2; 3; 4; 2; 3; 4; 5; 3; 4; 5; 6]

列表数据类型的属性

下表显示了列表数据类型的各种属性 -

属性 类型 描述
Head'T 第一个元素。
Empty'T list 一个静态属性,返回相应类型的空列表。
IsEmptybool 如果列表没有元素,则为true
Item'T 指定索引处的元素(从零开始)。
Lengthint 元素的数量。
Tail'T list 没有第一个元素的列表。

以下示例显示了这些属性的用法 -

例子 (Example)

let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
// Use of Properties
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))

编译并执行程序时,它会产生以下输出 -

list1.IsEmpty is false
list1.Length is 8
list1.Head is 2
list1.Tail.Head is 4
list1.Tail.Tail.Head is 6
list1.Item(1) is 4

列表中的基本运算符

下表显示了列表数据类型的基本操作 -

描述
追加:'T列表→'T列表→'T列表 返回一个新列表,其中包含第一个列表的元素,后跟第二个列表的元素。
平均值:'T list→^ T. 返回列表中元素的平均值。
averageBy:('T→^ U)→'T list→^ U. 返回通过将函数应用于列表的每个元素而生成的元素的平均值。
选择:('T→'U选项)→'T列表→'U列表 将给定函数应用于列表的每个元素。 返回由函数返回Some每个元素的结果组成的列表。
收集:('T→'U列表)→'T列表→'U列表 对于列表的每个元素,应用给定的函数。 连接所有结果并返回组合列表。
concat:seq →'T列表 返回一个新列表,按顺序包含每个列表的元素。
empty : 'T list 返回给定类型的空列表。
存在:('T→bool)→'T list→bool 测试列表中的任何元素是否满足给定谓词。
exists2:('T1→'T2→bool)→'T1 list→'T2 list→bool 测试列表的任何一对相应元素是否满足给定谓词。
filter:('T→bool)→'T list→'T列表 返回一个新集合,该集合仅包含给定谓词返回true的集合元素。
找到:('T→bool)→'T list→'T 返回给定函数返回true的第一个元素。
findIndex:('T→bool)→'T list→int 返回列表中满足给定谓词的第一个元素的索引。
fold:('State→'T→'State)→'State→'T list→'State 将函数应用于集合的每个元素,通过计算线程化累加器参数。 此函数接受第二个参数,并将函数应用于它和列表的第一个元素。 然后,它将此结果与第二个元素一起传递给函数,依此类推。 最后,它返回最终结果。 如果输入函数是f并且元素是i0 ... iN,则此函数计算f(...(fs i0)i1 ...)iN。
fold2 :('状态→'T1→'T2→'状态)→'状态→'T1列表→'T2列表→'状态 将函数应用于两个集合的相应元素,通过计算线程化累加器参数。 集合必须具有相同的大小。 如果输入函数是f并且元素是i0 ... iN和j0 ... jN,则此函数计算f(...(fs i0 j0)...)iN jN。
foldBack :('T→'州→'州)→'T列表→'州→'州 将函数应用于集合的每个元素,通过计算线程化累加器参数。 如果输入函数是f并且元素是i0 ... iN,则计算f i0(...(f iN s))。
foldBack2 :('T1→'T2→'状态→'状态)→'T1列表→'T2列表→'状态→'状态 将函数应用于两个集合的相应元素,通过计算线程化累加器参数。 集合必须具有相同的大小。 如果输入函数是f并且元素是i0 ... iN和j0 ... jN,则该函数计算f i0 j0(...(f iN jN s))。
forall:('T→bool)→'T list→bool 测试集合的所有元素是否满足给定的谓词。
forall2:('T1→'T2→bool)→'T1列表→'T2列表→布尔 测试集合的所有相应元素是否成对满足给定谓词。
head:'T list→'T 返回列表的第一个元素。
init:int→(int→'T)→'T列表 通过在每个索引上调用给定的生成器来创建列表。
isEmpty:'T list→bool 如果列表不包含元素,则返回true ,否则返回false
iter:('T→unit)→'T list→unit 将给定函数应用于集合的每个元素。
iter2:('T1→'T2→单位)→'T1列表→'T2列表→单位 将给定函数同时应用于两个集合。 集合必须具有相同的大小。
iteri:(int→'T→unit)→'T list→unit 将给定函数应用于集合的每个元素。 传递给函数的整数表示元素的索引。
iteri2:(int→'T1→'T2→单位)→'T1列表→'T2列表→单位 将给定函数同时应用于两个集合。 集合必须具有相同的大小。 传递给函数的整数表示元素的索引。
length:'T list→int 返回列表的长度。
map:('T→'U)→'T list→'U列表 创建一个新集合,其元素是将给定函数应用于集合的每个元素的结果。
map2:('T1→'T2→'U)→'T1列表→'T2列表→'U列表 创建一个新集合,其元素是将给定函数成对应用于两个集合的相应元素的结果。
map3:('T1→'T2→'T3→'U)→'T1列表→'T2列表→'T3列表→'U列表 创建一个新集合,其元素是将给定函数同时应用于三个集合的相应元素的结果。
mapi:(int→'T→'U)→'T list→'U列表 创建一个新集合,其元素是将给定函数应用于集合的每个元素的结果。 传递给函数的整数索引表示正在转换的元素的索引(从0开始)。
mapi2:(int→'T1→'T2→'U)→'T1列表→'T2列表→'U列表 与List.mapi类似,但是从两个相等长度的列表中映射相应的元素。
max:'T list→'T 返回列表中所有元素中最大的元素,使用Operators.max进行比较。
maxBy:('T→'U)→'T list→'T 返回列表中所有元素中最大的元素,通过在函数结果上使用Operators.max进行比较。
min:'T list→'T 返回列表中所有元素的最低元素,使用Operators.min进行比较。
minBy :('T→'U)→'T list→'T 返回列表中所有元素的最低值,通过在函数结果上使用Operators.min进行比较
nth:'T list→int→'T 索引到列表中。 第一个元素的索引为0。
ofArray:'T []→'T列表 从给定数组创建列表。
ofSeq:seq →'T列表 从给定的可枚举对象创建新列表。
分区:('T→bool)→'T list *'T列表 将集合拆分为两个集合,其中包含给定谓词分别返回truefalse的元素。
permute:(int→int)→'T list→'T列表 返回一个列表,其中所有元素都根据指定的排列进行置换。
选择:('T→'U选项)→'T列表→'U 将给定函数应用于连续元素,返回第一个结果,其中函数返回Some值。
reduce:('T→'T→'T)→'T list→'T 将函数应用于集合的每个元素,通过计算线程化累加器参数。 此函数将指定的函数应用于列表的前两个元素。 然后它将此结果与第三个元素一起传递给函数,依此类推。 最后,它返回最终结果。 如果输入函数是f并且元素是i0 ... iN,则该函数计算f(...(f i0 i1)i2 ...)iN。
reduceBack :('T→'T→'T)→'T list→'T 将函数应用于集合的每个元素,通过计算线程化累加器参数。 如果输入函数是f并且元素是i0 ... iN,则该函数计算f i0(...(f iN-1 iN))。
复制:(int→'T→'T列表) 通过在每个索引上调用给定的生成器来创建列表。
rev:'T list→'T列表 返回包含相反顺序元素的新列表。
扫描:('状态→'T→'状态)→'状态→'T列表→'状态列表 将函数应用于集合的每个元素,通过计算线程化累加器参数。 此函数接受第二个参数,并将指定的函数应用于它和列表的第一个元素。 然后,它将此结果与第二个元素一起传递给函数,依此类推。 最后,它返回中间结果列表和最终结果。
scanBack :('T→'State→'State)→'T list→'State→'状态列表 像foldBack一样,但返回中间和最终结果
sort:'T list→'T列表 使用Operators.compare对给定列表进行排序。
sortBy:('T→'Key)→'T list→'T列表 使用给定投影给出的键对给定列表进行排序。 使用Operators.compare比较密钥。
sortWith:('T→'T→int)→'T list→'T列表 使用给定的比较函数对给定列表进行排序。
sum:^ T list→^ T. 返回列表中元素的总和。
sumBy:('T→^ U)→'T list→^ U. 返回通过将函数应用于列表的每个元素而生成的结果的总和。
tail:'T list→'T列表 返回没有第一个元素的输入列表。
toArray:'T list→'T [] 从给定列表创建一个数组。
toSeq:'T list→seq 将给定列表视为序列。
tryFind:('T→bool)→'T list→'T选项 返回给定函数返回true的第一个元素。 如果不存在此类元素,则返回None
tryFindIndex :('T→bool)→'T list→int选项 返回列表中满足给定谓词的第一个元素的索引。 如果不存在此类元素,则返回None
tryPick :('T→'U选项)→'T列表→'U选项 将给定函数应用于连续元素,返回第一个结果,其中函数返回Some值。 如果不存在此类元素,则返回None
解压:('T1 *'T2)列表→'T1列表*'T2列表 将对列表拆分为两个列表。
unzip3:('T1 *'T2 *'T3)列表→'T1列表*'T2列表*'T3列表 将三元组列表拆分为三个列表。
zip:'T1 list→'T2 list→('T1 *'T2)列表 将两个列表组合成一对列表。 这两个列表必须具有相同的长度。
zip3:'T1列表→'T2列表→'T3列表→('T1 *'T2 *'T3)列表 将三个列表组合成三元组列表。 列表必须具有相同的长度。

以下示例演示了上述功能的用途 -

例子1 (Example 1)

该程序显示递归反转列表 -

let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
printfn "The original list: %A" list1
let reverse lt =
   let rec loop acc = function
      | [] -> acc
      | hd :: tl -> loop (hd :: acc) tl
   loop [] lt
printfn "The reversed list: %A" (reverse list1)

编译并执行程序时,它会产生以下输出 -

The original list: [2; 4; 6; 8; 10; 12; 14; 16]
The reversed list: [16; 14; 12; 10; 8; 6; 4; 2]

但是,您可以将模块的rev功能用于相同的目的 -

let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
printfn "The original list: %A" list1
printfn "The reversed list: %A" (List.rev list1)

编译并执行程序时,它会产生以下输出 -

The original list: [2; 4; 6; 8; 10; 12; 14; 16]
The reversed list: [16; 14; 12; 10; 8; 6; 4; 2]

例子2 (Example 2)

该程序显示使用List.filter方法过滤列表 -

let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
let list2 = list1 |> List.filter (fun x -> x % 2 = 0);;
printfn "The Filtered list: %A" list2

编译并执行程序时,它会产生以下输出 -

The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The Filtered list: [2; 4; 6; 8; 10]

例子3 (Example 3)

List.map方法将列表从一种类型映射到另一种类型 -

let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
let list2 = list1 |> List.map (fun x -> (x * x).ToString());;
printfn "The Mapped list: %A" list2

编译并执行程序时,它会产生以下输出 -

The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The Mapped list: ["1"; "4"; "9"; "16"; "25"; "36"; "49"; "64"; "81"; "100"]

例子4 (Example 4)

List.append方法和@运算符将一个列表附加到另一个列表 -

let list1 = [1; 2; 3; 4; 5 ]
let list2 = [6; 7; 8; 9; 10]
let list3 = List.append list1 list2
printfn "The first list: %A" list1
printfn "The second list: %A" list2
printfn "The appened list: %A" list3
let lt1 = ['a'; 'b';'c' ]
let lt2 = ['e'; 'f';'g' ]
let lt3 = lt1 @ lt2
printfn "The first list: %A" lt1
printfn "The second list: %A" lt2
printfn "The appened list: %A" lt3

编译并执行程序时,它会产生以下输出 -

The first list: [1; 2; 3; 4; 5]
The second list: [6; 7; 8; 9; 10]
The appened list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The first list: ['a'; 'b'; 'c']
The second list: ['e'; 'f'; 'g']
The appened list: ['a'; 'b'; 'c'; 'e'; 'f'; 'g']

例子5 (Example 5)

List.sort方法对列表进行排序。 List.sum方法给出列表中元素的总和, List.average方法给出列表中元素的平均值 -

let list1 = [9.0; 0.0; 2.0; -4.5; 11.2; 8.0; -10.0]
printfn "The list: %A" list1
let list2 = List.sort list1
printfn "The sorted list: %A" list2
let s = List.sum list1
let avg = List.average list1
printfn "The sum: %f" s
printfn "The average: %f" avg

编译并执行程序时,它会产生以下输出 -

The list: [9.0; 0.0; 2.0; -4.5; 11.2; 8.0; -10.0]
The sorted list: [-10.0; -4.5; 0.0; 2.0; 8.0; 9.0; 11.2]
The sum: 15.700000
The average: 2.242857

“折叠”操作将函数应用于列表中的每个元素,将函数的结果聚合在累加器变量中,并作为折叠操作的结果返回累加器。

例6

List.fold方法从左到右为每个元素应用一个函数,而List.foldBack从右到左对每个元素应用一个函数。

let sumList list = List.fold (fun acc elem -> acc + elem) 0 list
printfn "Sum of the elements of list %A is %d." [ 1 .. 10 ] (sumList [ 1 .. 10 ])

编译并执行程序时,它会产生以下输出 -

Sum of the elements of list [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] is 55.

F# - Sequences

序列,如列表也表示有序的值集合。 但是,在需要时计算序列或序列表达式中的元素。 它们不是一次计算的,因此它们用于表示无限的数据结构。

定义序列

序列使用以下语法定义 -

seq { expr }

例如,

let seq1 = seq { 1 .. 10 }

创建序列和序列表达式

与列表类似,您可以使用范围和理解来创建序列。

序列表达式是您可以编写用于创建序列的表达式。 这些可以做到 -

  • By specifying the range.
  • 通过增加或减少指定范围。
  • 通过使用yield关键字生成成为序列一部分的值。
  • 使用→运算符。

以下示例演示了这一概念 -

例子1 (Example 1)

(* Sequences *)
let seq1 = seq { 1 .. 10 }
(* ascending order and increment*)
printfn "The Sequence: %A" seq1
let seq2 = seq { 1 .. 5 .. 50 }
(* descending order and decrement*)
printfn "The Sequence: %A" seq2
let seq3 = seq {50 .. -5 .. 0}
printfn "The Sequence: %A" seq3
(* using yield *)
let seq4 = seq { for a in 1 .. 10 do yield a, a*a, a*a*a }
printfn "The Sequence: %A" seq4

编译并执行程序时,它会产生以下输出 -

The Sequence: seq [1; 2; 3; 4; ...]
The Sequence: seq [1; 6; 11; 16; ...]
The Sequence: seq [50; 45; 40; 35; ...]
The Sequence: seq [(1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64); ...]

例子2 (Example 2)

以下程序打印从1到50的素数 -

(* Recursive isprime function. *)
let isprime n =
   let rec check i =
      i > n/2 || (n % i <> 0 && check (i + 1))
   check 2
let primeIn50 = seq { for n in 1..50 do if isprime n then yield n }
for x in primeIn50 do
   printfn "%d" x

编译并执行程序时,它会产生以下输出 -

1
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47

序列的基本操作

下表显示了序列数据类型的基本操作 -

描述
append:seq →seq →seq 将两个给定的枚举包装为单个连接枚举。
平均值:seq →^ T. 返回序列中元素的平均值。
averageBy:('T→^ U)→seq →^ U. 返回通过将函数应用于序列的每个元素而生成的结果的平均值。
cache:seq →seq 返回与输入序列的缓存版本对应的序列。
cast:IEnumerable→seq 包装松散类型的系统。 集合序列为类型序列。
选择:('T→'U选项)→seq →seq 将给定函数应用于列表的每个元素。 返回由函数返回Some每个元素的结果组成的列表。
收集:('T→'收藏)→seq →seq 将给定函数应用于序列的每个元素并连接所有结果。
compareWith:('T→'T→int)→seq →seq →int 使用给定的比较函数逐个元素地比较两个序列。
concat:seq →seq 将给定的枚举枚举组合为单个连接枚举。
countBy:('T→'Key)→seq →seq 将键生成函数应用于序列的每个元素,并返回一个序列,生成唯一键及其在原始序列中出现的次数。
延迟:(单位→seq )→seq 返回根据给定的序列延迟规范构建的序列。
distinct:seq →seq 根据条目上的泛型哈希和相等比较,返回不包含重复条目的序列。 如果元素在序列中多次出现,则丢弃后面出现的事件。
distinctBy :('T→'Key)→seq →seq 根据给定密钥生成函数返回的键上的泛型哈希和相等比较,返回不包含重复条目的序列。 如果元素在序列中多次出现,则丢弃后面出现的事件。
empty : seq<'T>Creates an empty sequence.
exactOne:seq →'T 返回序列的唯一元素。
存在:('T→bool)→seq →布尔 测试序列中的任何元素是否满足给定谓词。
exists2:('T1→'T2→bool)→seq →seq →bool 测试输入序列的任何一对相应元素是否满足给定谓词。
filter:('T→bool)→seq →seq 返回一个新集合,该集合仅包含给定谓词返回true的集合元素。
找到:('T→bool)→seq →'T 返回给定函数返回true的第一个元素。
findIndex:('T→bool)→seq →int 返回给定函数返回true的第一个元素的索引。
fold:('State→'T→'State)→'State→seq →'State 将函数应用于集合的每个元素,通过计算线程化累加器参数。 如果输入函数是f并且元素是i0 ... iN,则此函数计算f(...(fs i0)...)iN。
forall:('T→bool)→seq →bool 测试序列的所有元素是否满足给定谓词。
forall2:('T1→'T2→bool)→seq →seq →bool 测试从两个序列中提取的所有元素对满足给定的谓词。 如果一个序列比另一个序列短,则忽略较长序列的其余元素。
groupBy:('T→'Key)→seq →seq > 将键生成函数应用于序列的每个元素,并生成一系列唯一键。 每个唯一键还包含与此键匹配的所有元素的序列。
head:seq →'T 返回序列的第一个元素。
init:int→(int→'T)→seq 生成一个新序列,当迭代时,通过调用给定函数返回连续元素,直到给定计数。 调用函数的结果不会被保存,也就是说,根据需要重新应用函数来重新生成元素。 该函数传递正在生成的项的索引。
initInfinite:(int→'T)→seq 生成一个新序列,当迭代时,将通过调用给定函数返回连续元素。 调用该函数的结果不会被保存,也就是说,将根据需要重新应用该函数以重新生成元素。 该函数传递正在生成的项的索引。
isEmpty:seq →布尔 测试序列是否包含任何元素。
iter :('T→单位)→seq →单位 将给定函数应用于集合的每个元素。
iter2:('T1→'T2→单位)→seq →seq →单位 将给定函数同时应用于两个集合。 如果一个序列比另一个序列短,则忽略较长序列的其余元素。
iteri:(int→'T→unit)→seq →unit 将给定函数应用于集合的每个元素。 传递给函数的整数表示元素的索引。
最后:seq →'T 返回序列的最后一个元素。
length:seq →int 返回序列的长度。
map:('T→'U)→seq →seq 创建一个新集合,其元素是将给定函数应用于集合的每个元素的结果。 在从对象检索的枚举数中使用MoveNext方法需要元素时,将应用给定的函数。
map2:('T1→'T2→'U)→seq →seq →seq 创建一个新集合,其元素是将给定函数应用于两个序列中相应元素对的结果。 如果一个输入序列比另一个输入序列短,则忽略较长序列的其余元素。
mapi:(int→'T→'U)→seq →seq 创建一个新集合,其元素是将给定函数应用于集合的每个元素的结果。 传递给函数的整数索引表示正在转换的元素的索引(从0开始)。
max:seq →'T 返回序列中所有元素中最大的元素,使用Operators.max进行比较。
maxBy :('T→'U)→seq →'T 返回序列中所有元素中最大的元素,通过在函数结果上使用Operators.max进行比较。
min:seq →'T 返回序列中所有元素的最低元素,使用Operators.min进行比较。
minBy :('T→'U)→seq →'T 返回序列中所有元素的最低元素,通过在函数结果上使用Operators.min进行比较。
nth:int→seq →'T 计算集合中的nth元素。
ofArray:'T array→seq 将给定数组视为序列。
ofList:'T list→seq 将给定列表视为序列。
成对:seq →seq 返回输入序列及其前一个元素中每个元素的序列,但第一个元素除外,它仅作为第二个元素的前任返回。
选择:('T→'U选项)→seq →'U 将给定函数应用于连续元素,返回函数返回Some值的第一个值。
readonly:seq →seq 创建一个委托给定序列对象的新序列对象。 这确保了原型序列不能被类型转换重新发现和改变。 例如,如果给定一个数组,则返回的序列将返回数组的元素,但是您无法将返回的序列对象强制转换为数组。
reduce:('T→'T→'T)→seq →'T 将函数应用于序列的每个元素,通过计算线程化累加器参数。 首先将函数应用于前两个元素。 然后将此结果与第三个元素一起提供给函数,依此类推。 返回最终结果。
scan:('State→'T→'State)→'State→seq →seq 与Seq.fold一样,但是按需计算并返回中间和最终结果的序列。
单身人士:'T→seq 返回仅生成一个项目的序列。
skip:int→seq →seq 返回跳过基础序列的指定数量元素的序列,然后生成序列的其余元素。
skipWhile :('T→bool)→seq →seq 返回一个序列,当迭代时,在给定谓词返回true,时跳过基础序列的元素true,然后生成序列的其余元素。
sort:seq →seq 产生按键排序的序列。
sortBy:('T→'Key)→seq →seq 将键生成函数应用于序列的每个元素,并生成按键排序的序列。 使用Operators.compare实现的通用比较来比较密钥。
sum:seq →^ T. 返回序列中元素的总和。
sumBy 返回通过将函数应用于序列的每个元素而生成的结果的总和。
take:int→seq →seq 返回序列的第一个元素,直到指定的计数。
takeWhile :('T→bool)→seq →seq 返回一个序列,当迭代时,在给定谓词返回true,时生成基础序列的元素true,然后不再返回其他元素。
toArray:seq →'T [] 从给定集合创建数组。
toList:seq →'T列表 从给定集合创建列表。
truncate:int→seq →seq 返回枚举时返回的序列不超过指定数量的元素的序列。
tryFind:('T→bool)→seq →'T选项 返回给定函数返回true,的第一个元素,如果不存在此元素,则返回None
tryFindIndex :('T→bool)→seq →int选项 返回满足给定谓词的序列中第一个元素的索引,如果不存在此类元素,则返回None
tryPick :('T→'U选项)→seq →'U选项 将给定函数应用于连续元素,返回函数返回Some值的第一个值。
展开:('州→'T *'州选项)→'州→seq 返回包含给定计算生成的元素的序列。
其中:('T→bool)→seq →seq 返回一个新集合,该集合仅包含给定谓词返回true的集合元素。 Seq.filter的同义词。
windowed:int→seq →seq 返回一个序列,该序列生成包含从输入序列中提取的元素的滑动窗口。 每个窗口都以新数组的形式返回。
zip:seq →seq →seq 将两个序列组合成一对列表。 这两个序列不需要具有相同的长度 - 当一个序列耗尽时,忽略另一个序列中的任何剩余元素。
zip3:seq →seq →seq →seq 将三个序列组合成三元组列表。 序列不需要具有相等的长度 - 当一个序列耗尽时,忽略其他序列中的任何剩余元素。

以下示例演示了上述某些功能的用途 -

例子1 (Example 1)

这个程序创建一个空序列并在以后填充它 -

(* Creating sequences *)
let emptySeq = Seq.empty
let seq1 = Seq.singleton 20
printfn"The singleton sequence:"
printfn "%A " seq1
printfn"The init sequence:"
let seq2 = Seq.init 5 (fun n -> n * 3)
Seq.iter (fun i -> printf "%d " i) seq2
printfn""
(* converting an array to sequence by using cast *)
printfn"The array sequence 1:"
let seq3 = [| 1 .. 10 |] :> seq<int>
Seq.iter (fun i -> printf "%d " i) seq3
printfn""
(* converting an array to sequence by using Seq.ofArray *)
printfn"The array sequence 2:"
let seq4 = [| 2..2.. 20 |] |> Seq.ofArray
Seq.iter (fun i -> printf "%d " i) seq4
printfn""

编译并执行程序时,它会产生以下输出 -

The singleton sequence:
seq [20]
The init sequence:
0 3 6 9 12
The array sequence 1:
1 2 3 4 5 6 7 8 9 10
The array sequence 2:
2 4 6 8 10 12 14 16 18 20

请注意 -

  • Seq.empty方法创建一个空序列。

  • Seq.singleton方法只创建一个指定元素的序列。

  • Seq.init方法创建一个序列,通过使用给定的函数为其创建元素。

  • Seq.ofArray和Seq.ofList 方法从数组和列表创建序列。

  • Seq.iter方法允许迭代序列。

例子2 (Example 2)

Seq.unfold方法从计算函数生成一个序列,该函数接受一个状态并对其进行转换以生成序列中的每个后续元素。

以下函数生成前20个自然数 -

let seq1 = Seq.unfold (fun state -> if (state > 20) then None else Some(state, state + 1)) 0
printfn "The sequence seq1 contains numbers from 0 to 20."
for x in seq1 do printf "%d " x
printfn" "

编译并执行程序时,它会产生以下输出 -

The sequence seq1 contains numbers from 0 to 20.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

例子3 (Example 3)

Seq.truncate方法从另一个序列创建序列,但将序列限制为指定数量的元素。

Seq.take方法创建一个新序列,该序列包含序列开头的指定数量的元素。

let mySeq = seq { for i in 1 .. 10 -> 3*i }
let truncatedSeq = Seq.truncate 5 mySeq
let takeSeq = Seq.take 5 mySeq
printfn"The original sequence"
Seq.iter (fun i -> printf "%d " i) mySeq
printfn""
printfn"The truncated sequence"
Seq.iter (fun i -> printf "%d " i) truncatedSeq
printfn""
printfn"The take sequence"
Seq.iter (fun i -> printf "%d " i) takeSeq
printfn""

编译并执行程序时,它会产生以下输出 -

The original sequence
3 6 9 12 15 18 21 24 27 30
The truncated sequence
3 6 9 12 15
The take sequence
3 6 9 12 15

F# - Sets

F#中的集合是一种数据结构,它充当项目集合,而不保留项目的插入顺序。 集不允许将重复条目插入集合中。

创建集

可以通过以下方式创建集合 -

  • 通过使用Set.empty创建空集并使用add函数添加项。
  • 将序列和列表转换为集合。

以下程序演示了这些技术 -

(* creating sets *)
let set1 = Set.empty.Add(3).Add(5).Add(7). Add(9)
printfn"The new set: %A" set1
let weekdays = Set.ofList ["mon"; "tues"; "wed"; "thurs"; "fri"]
printfn "The list set: %A" weekdays
let set2 = Set.ofSeq [ 1 .. 2.. 10 ]
printfn "The sequence set: %A" set2

编译并执行程序时,它会产生以下输出 -

The new set: set [3; 5; 7; 9]
The list set: set ["fri"; "mon"; "thurs"; "tues"; "wed"]
The sequence set: set [1; 3; 5; 7; 9]

集合的基本操作

下表显示了集合的基本操作 -

描述
添加:'T→设置→设置 返回一个新元素集,其中添加了一个元素。 如果集合已包含给定元素,则不会引发异常。
包含:'T→设置→布尔 如果给定元素在给定集合中,则求值为true
count:设置→int 返回集合中的元素数。
差异:设置→设置→设置 返回一个新集合,其中第二个集合的元素从第一个集合中删除。
空:设置 指定类型的空集。
存在:('T→bool)→设置→布尔 测试集合中的任何元素是否满足给定的谓词。 如果输入函数是谓词且元素是i0 ... iN,则此函数计算谓词i0或...或谓词iN。
filter:('T→bool)→Set →Set 返回一个新集合,该集合仅包含给定谓词返回true的集合元素。
fold:('State→'T→'State)→'State→Set →'State 将给定的累积函数应用于集合的所有元素。
foldBack :('T→'州→'州)→设置→'州→'州 将给定的累积函数应用于集合的所有元素。
forall:('T→bool)→设置→布尔 测试集合的所有元素是否满足给定的谓词。 如果输入函数是p并且元素是i0 ... iN,则此函数计算p i0 && ... && p iN。
相交:设置→设置→设置 计算两组的交集。
intersectMany:seq >→Set 计算一系列集合的交集。 序列必须是非空的。
isEmpty:设置→布尔 如果集合为空,则返回true
isProperSubset:设置→设置→布尔 如果第一组的所有元素都在第二组中,并且第二组中的至少一个元素不在第一组中,则求值为true
isProperSuperset:设置→设置→布尔 如果第二组的所有元素都在第一组中,并且第一组中的至少一个元素不在第二组中,则求值为true
isSubset:设置→设置→布尔 如果第一组的所有元素都在第二组中,则求值为true
isSuperset:设置→设置→布尔 如果第二组的所有元素都在第一组中,则求值为true
iter:('T→unit)→设置→单位 根据比较函数按顺序将给定函数应用于集合的每个元素。
map:('T→'U)→设置→设置 返回一个新集合,其中包含将给定函数应用于输入集的每个元素的结果。
maxElement:设置→'T 根据用于集合的排序返回集合中的最高元素。
minElement:设置→'T 根据用于集合的排序返回集合中的最低元素。
ofArray:'T array→Set 创建一个包含与给定数组相同元素的集合。
ofList:'T list→Set 创建一个包含与给定列表相同元素的集合。
ofSeq:seq →设置 从给定的可枚举对象创建新集合。
分区:('T→bool)→设置→设置 *设置 将集合拆分为两个集合,其中包含给定谓词分别返回true和false的元素。
删除:'T→设置→设置 返回删除了给定元素的新集合。 如果集合不包含给定元素,则不会引发异常。
单身:'T→设置 包含给定元素的集合。
toArray:设置→'T数组 创建一个按顺序包含集合元素的数组。
toList:设置→'T列表 按顺序创建包含集合元素的列表。
toSeq:设置→seq 以可枚举对象的形式返回集合的有序视图。
union:设置→设置→设置 计算两个集合的并集。
unionMany:seq >→Set 计算一系列集合的并集。

以下示例演示了上述某些功能的用法 -

例子 (Example)

let a = Set.ofSeq [ 1 ..2.. 20 ]
let b = Set.ofSeq [ 1 ..3 .. 20 ]
let c = Set.intersect a b
let d = Set.union a b
let e = Set.difference a b
printfn "Set a: "
Set.iter (fun x -> printf "%O " x) a
printfn""
printfn "Set b: "
Set.iter (fun x -> printf "%O " x) b
printfn""
printfn "Set c = set intersect of a and b : "
Set.iter (fun x -> printf "%O " x) c
printfn""
printfn "Set d = set union of a and b : "
Set.iter (fun x -> printf "%O " x) d
printfn""
printfn "Set e = set difference of a and b : "
Set.iter (fun x -> printf "%O " x) e
printfn""

编译并执行程序时,它会产生以下输出 -

Set a:
1 3 5 7 9 11 13 15 17 19
Set b:
1 4 7 10 13 16 19
Set c = set intersect of a and b :
1 7 13 19
Set d = set union of a and b :
1 3 4 5 7 9 10 11 13 15 16 17 19
Set e = set difference of a and b :
3 5 9 11 15 17

F# - Maps

在F#中,map是一种特殊的集合,它将值与key相关联。 创建地图的方式与创建集合的方式类似。

创建地图

通过使用Map.empty创建空映射并使用Add函数添加项来创建映射。 以下示例演示了这一点 -

例子 (Example)

(* Create an empty Map *)
let students =
   Map.empty. (* Creating an empty Map *)
      Add("Zara Ali", "1501").
      Add("Rishita Gupta", "1502").
      Add("Robin Sahoo", "1503").
      Add("Gillian Megan", "1504");;
printfn "Map - students: %A" students
(* Convert a list to Map *)
let capitals =
   [ "Argentina", "Buenos Aires";
      "France ", "Paris";
      "Chili", "Santiago";
      "Malaysia", " Kuala Lumpur";
      "Switzerland", "Bern" ]
   |> Map.ofList;;
printfn "Map capitals : %A" capitals

编译并执行程序时,它会产生以下输出 -

Map - students: map
[("Gillian Megan", "1504"); ("Rishita Gupta", "1502"); ("Robin Sahoo", "1503
");
("Zara Ali", "1501")]
Map capitals : map
[("Argentina", "Buenos Aires"); ("Chili", "Santiago"); ("France ", "Paris");
("Malaysia", " Kuala Lumpur"); ("Switzerland", "Bern")]

您可以使用键访问地图中的各个元素。

例子 (Example)

(* Create an empty Map *)
let students =
   Map.empty. (* Creating an empty Map *)
      Add("Zara Ali", "1501").
      Add("Rishita Gupta", "1502").
      Add("Robin Sahoo", "1503").
      Add("Gillian Megan", "1504");;
printfn "Map - students: %A" students
(*Accessing an element using key *)
printfn "%A" students.["Zara Ali"]

编译并执行程序时,它会产生以下输出 -

Map - students: map
[("Gillian Megan", "1504"); ("Rishita Gupta", "1502"); ("Robin Sahoo", "1503
");
("Zara Ali", "1501")]
"1501"

地图的基本操作

添加模块名称

下表显示了地图上的基本操作 -

会员 描述
Add 返回添加到给定地图的绑定的新地图。
ContainsKey 测试元素是否在地图的域中。
Count 地图中的绑定数量。
IsEmpty 如果地图中没有绑定,则返回true。
Item 在地图中查找元素。 如果地图中不存在绑定,则引发KeyNotFoundException。
Remove 从地图的域中删除元素。 如果元素不存在,则不会引发异常。
TryFind 查找地图中的元素,如果元素位于地图的域中则返回Some值,否则返回None

以下示例演示了上述某些功能的用法 -

例子 (Example)

(* Create an empty Map *)
let students =
   Map.empty. (* Creating an empty Map *)
      Add("Zara Ali", "1501").
      Add("Rishita Gupta", "1502").
      Add("Robin Sahoo", "1503").
      Add("Gillian Megan", "1504").
      Add("Shraddha Dubey", "1505").
      Add("Novonil Sarker", "1506").
      Add("Joan Paul", "1507");;
printfn "Map - students: %A" students
printfn "Map - number of students: %d" students.Count
(* finding the registration number of a student*)
let found = students.TryFind "Rishita Gupta"
match found with
| Some x -> printfn "Found %s." x
| None -> printfn "Did not find the specified value."

编译并执行程序时,它会产生以下输出 -

Map - students: map
[("Gillian Megan", "1504"); ("Joan Paul", "1507"); ("Novonil Sarker", "1506"
);
("Rishita Gupta", "1502"); ("Robin Sahoo", "1503");
("Shraddha Dubey", "1505"); ("Zara Ali", "1501")]
Map - number of students: 7
Found 1502.

F# - Discriminated Unions

联合会或有区别的联合允许您构建代表明确定义的选择的复杂数据结构。 例如,您需要构建一个choice变量的实现,它有两个值yes和no。 使用联合工具,您可以设计它。

语法 (Syntax)

使用以下语法定义受歧视的联合 -

type type-name =
   | case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] 
type2 ...]
   | case-identifier2 [of [fieldname3 : ]type3 [ * [ fieldname4 : ]type4 ...]
...

我们的简单实现, choice,将如下所示 -

type choice =
   | Yes
   | No

以下示例使用类型选项 -

type choice =
   | Yes
   | No
let x = Yes (* creates an instance of choice *)
let y = No (* creates another instance of choice *)
let main() =
   printfn "x: %A" x
   printfn "y: %A" y
main()

编译并执行程序时,它会产生以下输出 -

x: Yes
y: No

例子1 (Example 1)

以下示例显示了将电压设置为高电平或低电平的电压状态的实现 -

type VoltageState =
   | High
   | Low
let toggleSwitch = function (* pattern matching input *)
   | High -> Low
   | Low -> High
let main() =
   let on = High
   let off = Low
   let change = toggleSwitch off
   printfn "Switch on state: %A" on
   printfn "Switch off state: %A" off
   printfn "Toggle off: %A" change
   printfn "Toggle the Changed state: %A" (toggleSwitch change)
main()

编译并执行程序时,它会产生以下输出 -

Switch on state: High
Switch off state: Low
Toggle off: High
Toggle the Changed state: Low

例子2 (Example 2)

type Shape =
   // here we store the radius of a circle
   | Circle of float
   // here we store the side length.
   | Square of float
   // here we store the height and width.
   | Rectangle of float * float
let pi = 3.141592654
let area myShape =
   match myShape with
   | Circle radius -> pi * radius * radius
   | Square s -> s * s
   | Rectangle (h, w) -> h * w
let radius = 12.0
let myCircle = Circle(radius)
printfn "Area of circle with radius %g: %g" radius (area myCircle)
let side = 15.0
let mySquare = Square(side)
printfn "Area of square that has side %g: %g" side (area mySquare)
let height, width = 5.0, 8.0
let myRectangle = Rectangle(height, width)
printfn "Area of rectangle with height %g and width %g is %g" height width (area myRectangle)

编译并执行程序时,它会产生以下输出 -

Area of circle with radius 12: 452.389
Area of square that has side 15: 225
Area of rectangle with height 5 and width 8 is 40

F# - Mutable Data

F#中的变量是immutable,这意味着一旦变量绑定到某个值,就无法更改。 它们实际上被编译为静态只读属性。

以下示例演示了这一点。

例子 (Example)

let x = 10
let y = 20
let z = x + y
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z
let x = 15
let y = 20
let z = x + y
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z

编译并执行该程序时,它显示以下错误消息 -

Duplicate definition of value 'x'
Duplicate definition of value 'Y'
Duplicate definition of value 'Z'

可变变量

有时您需要更改存储在变量中的值。 为了指定在程序的后续部分中声明和赋值变量的值可能发生变化,F#提供了mutable关键字。 您可以使用此关键字声明和分配可变变量,您将更改其值。

mutable关键字允许您在可变变量中声明和赋值。

您可以使用let关键字为可变变量分配一些初始值。 但是,要为其分配新的后续值,您需要使用《-运算符”。

例如,

let mutable x = 10
x <- 15

以下示例将清除概念 -

例子 (Example)

let mutable x = 10
let y = 20
let mutable z = x + y
printfn "Original Values:"
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z
printfn "Let us change the value of x"
printfn "Value of z will change too."
x <- 15
z <- x + y
printfn "New Values:"
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z

编译并执行程序时,它会产生以下输出 -

Original Values:
x: 10
y: 20
z: 30
Let us change the value of x
Value of z will change too.
New Values:
x: 15
y: 20
z: 35

可变数据的使用

通常需要可变数据并用于数据处理,特别是对于记录数据结构。 以下示例演示了这一点 -

open System
type studentData =
   { ID : int;
      mutable IsRegistered : bool;
      mutable RegisteredText : string; }
let getStudent id =
   { ID = id;
      IsRegistered = false;
      RegisteredText = null; }
let registerStudents (students : studentData list) =
   students |> List.iter(fun st ->
      st.IsRegistered <- true
      st.RegisteredText <- sprintf "Registered %s" (DateTime.Now.ToString("hh:mm:ss"))
      Threading.Thread.Sleep(1000) (* Putting thread to sleep for 1 second to simulate processing overhead. *))
let printData (students : studentData list) =
   students |> List.iter (fun x -> printfn "%A" x)
let main() =
   let students = List.init 3 getStudent
   printfn "Before Process:"
   printData students
   printfn "After process:"
   registerStudents students
   printData students
   Console.ReadKey(true) |> ignore
main()

编译并执行程序时,它会产生以下输出 -

Before Process:
{ID = 0;
IsRegistered = false;
RegisteredText = null;}
{ID = 1;
IsRegistered = false;
RegisteredText = null;}
{ID = 2;
IsRegistered = false;
RegisteredText = null;}
After process:
{ID = 0;
IsRegistered = true;
RegisteredText = "Registered 05:39:15";}
{ID = 1;
IsRegistered = true;
RegisteredText = "Registered 05:39:16";}
{ID = 2;
IsRegistered = true;
RegisteredText = "Registered 05:39:17";}

F# - Arrays

数组是固定大小,从零开始,可变的连续数据元素集合,它们都是相同类型的。

创建数组 (Creating Arrays)

您可以使用各种语法和方法或使用Array模块中的函数创建数组。 在本节中,我们将讨论在不使用模块函数的情况下创建数组。

创建没有函数的数组有三种语法方法 -

  • 通过列出[|。之间的连续值 和|,并用分号分隔。
  • 通过将每个元素放在单独的行上,在这种情况下,分号分隔符是可选的。
  • 通过使用序列表达式。

您可以使用点运算符(。)和括号([和])来访问数组元素。

以下示例演示如何创建数组 -

//using semicolon separator
let array1 = [| 1; 2; 3; 4; 5; 6 |]
for i in 0 .. array1.Length - 1 do
   printf "%d " array1.[i]
printfn" "
// without semicolon separator
let array2 =
   [|
      1
      2
      3
      4
      5
   |]
for i in 0 .. array2.Length - 1 do
   printf "%d " array2.[i]
printfn" "
//using sequence
let array3 = [| for i in 1 .. 10 -> i * i |]
for i in 0 .. array3.Length - 1 do
   printf "%d " array3.[i]
printfn" "

编译并执行程序时,它会产生以下输出 -

1 2 3 4 5 6
1 2 3 4 5
1 4 9 16 25 36 49 64 81 100

数组的基本操作

库模块Microsoft.FSharp.Collections.Array支持对一维数组的操作。

下表显示了数组的基本操作 -

描述
追加:'T []→'T []→'T [] 创建一个数组,其中包含一个数组的元素,后跟另一个数组的元素。
平均值:^ T []→^ T. 返回数组中元素的平均值。
averageBy:('T→^ U)→'T []→^ U. 返回通过将函数应用于数组的每个元素而生成的元素的平均值。
blit:'T []→int→'T []→int→int→unit 从一个数组中读取一系列元素并将它们写入另一个数组。
选择:('T→U选项)→'T []→'U [] 将提供的函数应用于数组的每个元素。 返回一个数组,其中包含函数返回Some(x)的每个元素的结果x。
收集:('T→'U [])→T []→'U [] 将提供的函数应用于数组的每个元素,连接结果,并返回组合数组。
concat:seq →'T [] 创建一个数组,其中包含每个提供的数组序列的元素。
复制:'T→'T [] 创建一个包含所提供数组元素的数组。
create:int→'T→'T [] 创建一个数组,其元素最初都是提供的值。
空:'T [] 返回给定类型的空数组。
存在:('T→bool)→'T []→布尔 测试数组的任何元素是否满足提供的谓词。
exists2:('T1→'T2→bool)→'T1 []→'T2 []→布尔 测试两个数组中的任何一对相应元素是否满足所提供的条件。
填充:'T []→int→int→'T→单位 使用提供的值填充数组的一系列元素。
过滤器:('T→bool)→'T []→'T [] 返回一个集合,该集合仅包含所提供条件返回true的所提供数组的元素。
找:('T→bool)→'T []→'T 返回提供的函数返回true的第一个元素。 如果不存在此类元素,则引发KeyNotFoundException。
findIndex:('T→bool)→'T []→int 返回满足所提供条件的数组中第一个元素的索引。 如果没有元素满足条件,则引发KeyNotFoundException。
fold:('State→'T→'State)→'State→'T []→'State 将函数应用于数组的每个元素,通过计算线程化累加器参数。 如果输入函数是f并且数组元素是i0 ... iN,则此函数计算f(...(fs i0)...)iN。
fold2 :('状态→'T1→'T2→'状态)→'状态→'T1 []→'T2 []→'状态 将函数应用于来自两个提供的数组的元素对,从左到右,通过计算线程化累加器参数。 两个输入数组必须具有相同的长度; 否则,引发ArgumentException。
foldBack :('T→'州→'州)→'T []→'州→'州 将函数应用于数组的每个元素,通过计算线程化累加器参数。 如果输入函数是f并且数组元素是i0 ... iN,则此函数计算f i0(...(f iN s))。
foldBack2 :('T1→'T2→'状态→'状态)→'T1 []→'T2 []→'状态→'状态 将函数应用于来自两个提供的数组的元素对,从右到左,通过计算线程化累加器参数。 两个输入数组必须具有相同的长度; 否则,引发ArgumentException。
forall:('T→bool)→'T []→布尔 测试数组的所有元素是否满足提供的条件。
forall2:('T1→'T2→bool)→'T1 []→'T2 []→布尔 测试两个提供的数组的所有相应元素是否满足提供的条件。
get:'T []→int→'T 从数组中获取元素。
init:int→(int→'T)→'T [] 使用提供的函数创建提供的维度的数组。
isEmpty:'T []→布尔 测试数组是否包含任何元素。
iter :('T→单位)→'T []→单位 将提供的函数应用于数组的每个元素。
iter2:('T1→'T2→单位)→'T1 []→'T2 []→单位) 将提供的函数应用于两个数组中匹配索引的一对元素。 两个数组必须具有相同的长度; 否则,引发ArgumentException。
iteri:(int→'T→unit)→'T []→单位 将提供的函数应用于数组的每个元素。 传递给函数的整数表示元素的索引。
iteri2:(int→'T1→'T2→单位)→'T1 []→'T2 []→单位 将提供的函数应用于两个数组中匹配索引的一对元素,同时传递元素的索引。 两个数组必须具有相同的长度; 否则,引发ArgumentException。
长度:'T []→int 返回数组的长度。 Length属性做同样的事情。
地图:('T→'U)→'T []→'U [] 创建一个数组,其元素是将提供的函数应用于提供的数组的每个元素的结果。
map2:('T1→'T2→'U)→'T1 []→'T2 []→'U [] 创建一个数组,其元素是将提供的函数应用于两个提供的数组的相应元素的结果。 两个输入数组必须具有相同的长度; 否则,引发ArgumentException。
mapi:(int→'T→'U)→'T []→'U [] 创建一个数组,其元素是将提供的函数应用于提供的数组的每个元素的结果。 传递给函数的整数索引表示要转换的元素的索引。
mapi2:(int→'T1→'T2→'U)→'T1 []→'T2 []→'U [] 创建一个数组,其元素是将提供的函数成对应用于两个集合的相应元素的结果,同时传递元素的索引。 两个输入数组必须具有相同的长度; 否则,引发ArgumentException。
max:'T []→'T 返回数组中所有元素中最大的元素。 Operators.max用于比较元素。
maxBy :('T→'U)→'T []→'T 返回数组中所有元素中最大的元素,通过函数结果上的Operators.max进行比较。
min:('T []→'T 返回数组中所有元素中的最小元素。 Operators.min用于比较元素。
minBy :('T→'U)→'T []→'T 返回数组中所有元素中的最小元素。 Operators.min用于比较元素。
ofList:'T list→'T [] 从提供的列表创建一个数组。
ofSeq:seq →'T [] 从提供的可枚举对象创建数组。
分区:('T→bool)→'T []→'T [] *'T [] 将数组拆分为两个数组,一个包含提供的条件返回true,的元素,另一个包含返回false的数组。
permute:(int→int)→'T []→'T [] 根据指定的排列置换数组的元素。
选择:('T→'U选项)→'T []→'U 将提供的函数应用于提供的数组的连续元素,返回第一个结果,其中函数返回某些x的Some(x)。 如果函数永远不会返回Some(x),则引发KeyNotFoundException。
reduce:('T→'T→'T)→'T []→'T 将函数应用于数组的每个元素,通过计算线程化累加器参数。 如果输入函数是f并且数组元素是i0 ... iN,则此函数计算f(...(f i0 i1)...)iN。 如果数组的大小为零,则引发ArgumentException。
reduceBack :('T→'T→'T)→'T []→'T 将函数应用于数组的每个元素,通过计算线程化累加器参数。 如果输入函数为f且元素为i0 ... iN,则此函数计算f i0(...(f iN-1 iN))。 如果数组的大小为零,则引发ArgumentException。
rev:'T []→'T [] 反转提供的数组中元素的顺序。
scan:('State→'T→'State)→'State→'T []→'State []) 表现得像折叠,但返回中间结果和最终结果。
scanBack:('T→'State→'State)→'T []→'State→'State [] 表现得像foldBack,但返回中间结果和最终结果。
设置:'T []→int→'T→单位 设置数组的元素。
排序:'T []→'T [] 对数组的元素进行排序并返回一个新数组。 Operators.compare用于比较元素。
sortBy:('T→'Key)→'T []→'T [] 使用提供的函数对元素的元素进行排序,以将元素转换为排序操作所基于的类型,并返回一个新数组。 Operators.compare用于比较元素。
sortInPlace:'T []→单位 使用提供的比较函数,通过更改数组来对数组元素进行排序。 Operators.compare用于比较元素。
sortInPlaceBy :('T→'Key)→'T []→单位 使用提供的键投影,通过更改数组来对数组元素进行排序。 Operators.compare用于比较元素。
sortInPlaceWith:('T→'T→int)→'T []→单位 使用提供的比较函数对数组元素进行排序,以更改数组。
sortWith:('T→'T→int)→'T []→'T [] 使用提供的比较函数对数组的元素进行排序,并返回一个新数组。
sub:'T []→int→int→'T [] 创建一个包含所提供的子范围的数组,该子范围由索引和长度开始指定。
sum:'T []→^ T. 返回数组中元素的总和。
sumBy:('T→^ U)→'T []→^ U. 返回通过将函数应用于数组的每个元素而生成的结果的总和。
toList:'T []→'T列表 将提供的数组转换为列表。
toSeq:'T []→seq 将提供的数组视为序列。
tryFind:('T→bool)→'T []→'T选项 返回提供的函数返回true的提供数组中的第一个元素。 如果不存在此类元素,则返回None
tryFindIndex :('T→bool)→'T []→int选项 返回满足所提供条件的数组中第一个元素的索引。
tryPick :('T→'U选项)→'T []→'U选项 将提供的函数应用于提供的数组的连续元素,并返回第一个结果,其中函数返回某些x的Some(x)。 如果函数永远不会返回Some(x),则返回None
unzip:('T1 *'T2)[]→'T1 [] *'T2 [] 将元组对的数组拆分为两个数组的元组。
unzip3:('T1 *'T2 *'T3)[]→'T1 [] *'T2 [] *'T3 [] 将三个元素的元组数组拆分为三个数组的元组。
zeroCreate:int→'T [] 创建一个数组,其元素最初设置为默认值Unchecked.defaultof 。
zip:'T1 []→'T2 []→('T1 *'T2)[] 将两个数组合并为一个包含两个元素的元组数组。 两个数组必须具有相同的长度; 否则,引发ArgumentException。
zip3:'T1 []→'T2 []→'T3 []→('T1 *'T2 * 113'T3)[] 将三个数组合并为一个具有三个元素的元组数组。 三个阵列必须具有相同的长度; 否则,引发ArgumentException。

在下一节中,我们将看到其中一些功能的用途。

使用函数创建数组 (Creating Arrays Using Functions)

Array模块提供了几个从头开始创建数组的函数。

  • Array.empty函数创建一个新的空数组。

  • Array.create函数创建一个指定大小的数组,并将所有元素设置为给定值。

  • Array.init函数创建一个数组,给定一个维度和一个生成元素的函数。

  • Array.zeroCreate函数创建一个数组,其中所有元素都初始化为零值。

  • Array.copy函数创建一个新数组,其中包含从现有数组复制的元素。

  • Array.sub函数从数组的子范围生成一个新数组。

  • Array.append函数通过组合两个现有数组来创建一个新数组。

  • Array.choose函数选择要包含在新数组中的数组元素。

  • Array.collect函数在现有数组的每个数组元素上运行指定的函数,然后收集函数生成的元素并将它们组合成一个新数组。

  • Array.concat函数接受一系列数组并将它们组合成一个数组。

  • Array.filter函数采用布尔条件函数并生成一个新数组,该数组仅包含条件为true的输入数组中的那些元素。

  • Array.rev函数通过反转现有数组的顺序生成新数组。

以下示例演示了这些功能 -

例子1 (Example 1)

(* using create and set *)
let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
   Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
   printf "%s " (Array.get array1 i)
printfn " "
(* empty array *)
let array2 = Array.empty
printfn "Length of empty array: %d" array2.Length
let array3 = Array.create 10 7.0
printfn "Float Array: %A" array3
(* using the init and zeroCreate *)
let array4 = Array.init 10 (fun index -> index * index)
printfn "Array of squares: %A" array4
let array5 : float array = Array.zeroCreate 10
let (myZeroArray : float array) = Array.zeroCreate 10
printfn "Float Array: %A" array5

编译并执行程序时,它会产生以下输出 -

0 1 2 3 4 5 6 7 8 9
Length of empty array: 0
Float Array: [|7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0; 7.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Float Array: [|0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0|]

例子2 (Example 2)

(* creating subarray from element 5 *)
(* containing 15 elements thereon *)
let array1 = [| 0 .. 50 |]
let array2 = Array.sub array1 5 15
printfn "Sub Array:"
printfn "%A" array2
(* appending two arrays *)
let array3 = [| 1; 2; 3; 4|]
let array4 = [| 5 .. 9 |]
printfn "Appended Array:"
let array5 = Array.append array3 array4
printfn "%A" array5
(* using the Choose function *)
let array6 = [| 1 .. 20 |]
let array7 = Array.choose (fun elem -> if elem % 3 = 0 then
                                             Some(float (elem))
                                          else
                                             None) array6
printfn "Array with Chosen elements:"
printfn "%A" array7
(*using the Collect function *)
let array8 = [| 2 .. 5 |]
let array9 = Array.collect (fun elem -> [| 0 .. elem - 1 |]) array8
printfn "Array with collected elements:"
printfn "%A" array9

编译并执行程序时,它会产生以下输出 -

Sub Array:
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19|]
Appended Array:
[|1; 2; 3; 4; 5; 6; 7; 8; 9|]
Array with Chosen elements:
[|3.0; 6.0; 9.0; 12.0; 15.0; 18.0|]
Array with collected elements:
[|0; 1; 0; 1; 2; 0; 1; 2; 3; 0; 1; 2; 3; 4|]

搜索数组

Array.find函数接受一个Boolean函数并返回函数返回true的第一个元素,否则引发KeyNotFoundException。

Array.findIndex函数的工作方式类似,只是它返回元素的索引而不是元素本身。

以下示例演示了这一点。

Microsoft提供了这个有趣的程序示例,它找到了给定数字范围内的第一个元素,它既是完美的正方形,也是完美的立方体 -

let array1 = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
   let y = sqrt (float x)
   abs(y - round y) < delta
let isPerfectCube (x:int) =
   let y = System.Math.Pow(float x, 1.0/3.0)
   abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
printfn "The first element that is both a square and a cube is %d and its index is %d." element index

编译并执行程序时,它会产生以下输出 -

The first element that is both a square and a cube is 64 and its index is 62.

F# - Mutable Lists

List《'T》类表示可以通过索引访问的强类型对象列表。

它是List类的可变对应物。 它类似于数组,因为它可以通过索引访问,但是,与数组不同,列表可以调整大小。 因此,您无需在声明期间指定大小。

创建可变列表

列表是使用new关键字创建的,并调用列表的构造函数。 以下示例演示了这一点 -

(* Creating a List *)
open System.Collections.Generic
let booksList = new List<string>()
booksList.Add("Gone with the Wind")
booksList.Add("Atlas Shrugged")
booksList.Add("Fountainhead")
booksList.Add("Thornbirds")
booksList.Add("Rebecca")
booksList.Add("Narnia")
booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])

编译并执行程序时,它会产生以下输出 -

0: Gone with the Wind
1: Atlas Shrugged
2: Fountainhead
3: Thornbirds
4: Rebecca
5: Narnia

The List(T) Class

List(T)类表示可以通过索引访问的强类型对象列表。 它提供了搜索,排序和操作列表的方法。

下表提供了List(T)类的属性,构造函数和方法 -

属性 (Properties)

属性 描述
Capacity 获取或设置内部数据结构可以保留的元素总数,而无需调整大小。
Count 获取List(T)中包含的元素数。
Item 获取或设置指定索引处的元素。

构造函数 (Constructors)

构造函数 描述
List(T)() 初始化List(T)类的新实例,该实例为空且具有默认初始容量。
List(T)(IEnumerable(T)) 初始化List(T)类的新实例,该实例包含从指定集合复制的元素,并且具有足够的容量来容纳复制的元素数。
List(T)(Int32) 初始化List(T)类的新实例,该实例为空且具有指定的初始容量。

方法 (Method)

方法 描述
Add 将对象添加到List(T)的末尾。
AddRange 将指定集合的​​元素添加到List(T)的末尾。
AsReadOnly 返回当前集合的只读IList(T)包装器。
BinarySearch(T) 使用默认比较器在整个排序列表(T)中搜索元素,并返回元素的从零开始的索引。
BinarySearch(T, IComparer(T)) 使用指定的比较器在整个排序列表(T)中搜索元素,并返回元素的从零开始的索引。
BinarySearch(Int32, Int32, T, IComparer(T)) 使用指定的比较器搜索元素的排序列表(T)中的一系列元素,并返回元素的从零开始的索引。
Clear 从List(T)中删除所有元素。
Contains 确定元素是否在List(T)中。
ConvertAll(TOutput) 将当前List(T)中的元素转换为另一种类型,并返回包含已转换元素的列表。
CopyTo(T[]) 从目标数组的开头开始,将整个List(T)复制到兼容的一维数组。
CopyTo(T[], Int32) 将整个List(T)复制到兼容的一维数组,从目标数组的指定索引处开始。
CopyTo(Int32, T[], Int32, Int32) 从目标数组的指定索引处开始,将List(T)中的一系列元素复制到兼容的一维数组。
Equals(Object) 确定指定的对象是否等于当前对象。 (继承自Object。)
Exists 确定List(T)是否包含与指定谓词定义的条件匹配的元素。
Finalize 允许对象在垃圾收集(继承自Object)回收之前尝试释放资源并执行其他清理操作。
Find 搜索与指定谓词定义的条件匹配的元素,并返回整个List(T)中的第一个匹配项。
FindAll 检索与指定谓词定义的条件匹配的所有元素。
FindIndex(Predicate(T)) 搜索与指定谓词定义的条件匹配的元素,并返回整个List(T)中第一个匹配项的从零开始的索引。
FindIndex(Int32, Predicate(T)) 搜索与指定谓词定义的条件匹配的元素,并返回List(T)中从指定索引扩展到最后一个元素的元素范围内第一个匹配项的从零开始的索引。
FindIndex(Int32, Int32, Predicate(T)) 搜索与指定谓词定义的条件匹配的元素,并返回List(T)中从指定索引开始并包含指定数量的元素的元素范围内的第一个匹配项的从零开始的索引。
FindLast 搜索与指定谓词定义的条件匹配的元素,并返回整个List(T)中的最后一个匹配项。
FindLastIndex(Predicate(T)) 搜索与指定谓词定义的条件匹配的元素,并返回整个List(T)中最后一个匹配项的从零开始的索引。
FindLastIndex(Int32, Predicate(T)) 搜索与指定谓词定义的条件匹配的元素,并返回List(T)中从第一个元素扩展到指定索引的元素范围内的最后一个匹配项的从零开始的索引。
FindLastIndex(Int32, Int32, Predicate(T)) 搜索与指定谓词定义的条件匹配的元素,并返回List(T)中包含指定数量的元素并在指定索引处结束的元素范围内的最后一次出现的从零开始的索引。
ForEach 对List(T)的每个元素执行指定的操作。
GetEnumerator 返回一个遍历List(T)的枚举器。
GetHashCode 用作默认哈希函数。 (继承自Object。)
GetRange 在源列表(T)中创建一系列元素的浅表副本。
GetType 获取当前实例的Type。 (继承自Object。)
IndexOf(T) 搜索指定的对象并返回整个List(T)中第一个匹配项的从零开始的索引。
IndexOf(T, Int32) 搜索指定的对象,并返回List(T)中从指定索引扩展到最后一个元素的元素范围内第一个匹配项的从零开始的索引。
IndexOf(T, Int32, Int32) 搜索指定的对象,并返回List(T)中从指定索引开始并包含指定数量的元素的元素范围内的第一个匹配项的从零开始的索引。
Insert 将元素插入到指定索引处的List(T)中。
InsertRange 将集合的元素插入到指定索引处的List(T)中。
LastIndexOf(T) 搜索指定的对象并返回整个List(T)中最后一次出现的从零开始的索引。
LastIndexOf(T, Int32) 搜索指定的对象,并返回List(T)中从第一个元素扩展到指定索引的元素范围内最后一个匹配项的从零开始的索引。
LastIndexOf(T, Int32, Int32) 搜索指定的对象,并返回List(T)中包含指定数量的元素并在指定索引处结束的元素范围内的最后一次出现的从零开始的索引。
MemberwiseClone 创建当前Object的浅表副本。 (继承自Object。)
Remove 从List(T)中删除特定对象的第一个匹配项。
RemoveAll 删除与指定谓词定义的条件匹配的所有元素。
RemoveAt 删除List(T)的指定索引处的元素。
RemoveRange 从List(T)中删除一系列元素。
Reverse() 反转整个List(T)中元素的顺序。
Reverse(Int32, Int32) 反转指定范围内元素的顺序。
Sort() 使用默认比较器对整个List(T)中的元素进行排序。
Sort(Comparison(T)) 使用指定的System对整个List(T)中的元素进行排序。 比较(T)。
Sort(IComparer(T)) 使用指定的比较器对整个List(T)中的元素进行排序。
Sort(Int32, Int32, IComparer(T)) 使用指定的比较器对List(T)中的一系列元素中的元素进行排序。
ToArray 将List(T)的元素复制到新数组。
ToString 返回表示当前对象的字符串。 (继承自Object。)
TrimExcess 如果该数字小于阈值,则将容量设置为List(T)中的实际元素数。
TrueForAll 确定List(T)中的每个元素是否与指定谓词定义的条件匹配。

例子 (Example)

(* Creating a List *)
open System.Collections.Generic
let booksList = new List<string>()
booksList.Add("Gone with the Wind")
booksList.Add("Atlas Shrugged")
booksList.Add("Fountainhead")
booksList.Add("Thornbirds")
booksList.Add("Rebecca")
booksList.Add("Narnia")
printfn"Total %d books" booksList.Count
booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])
booksList.Insert(2, "Roots")
printfn("after inserting at index 2")
printfn"Total %d books" booksList.Count
booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])
booksList.RemoveAt(3)
printfn("after removing from index 3")
printfn"Total %d books" booksList.Count
booksList |> Seq.iteri (fun index item -> printfn "%i: %s" index booksList.[index])

编译并执行程序时,它会产生以下输出 -

Total 6 books
0: Gone with the Wind
1: Atlas Shrugged
2: Fountainhead
3: Thornbirds
4: Rebecca
5: Narnia
after inserting at index 2
Total 7 books
0: Gone with the Wind
1: Atlas Shrugged
2: Roots
3: Fountainhead
4: Thornbirds
5: Rebecca
6: Narnia
after removing from index 3
Total 6 books
0: Gone with the Wind
1: Atlas Shrugged
2: Roots
3: Thornbirds
4: Rebecca
5: Narnia

F# - Mutable Dictionary

Dictionary《'TKey, 'TValue》类是F#地图数据结构的可变模拟,包含许多相同的功能。

从F#中的Map章节重述,地图是一种特殊的集合,它将值与键相关联。

创建一个可变字典

使用new关键字创建可变字典并调用列表的构造函数。 以下示例演示了这一点 -

open System.Collections.Generic
let dict = new Dictionary<string, string>()
dict.Add("1501", "Zara Ali")
dict.Add("1502","Rishita Gupta")
dict.Add("1503","Robin Sahoo")
dict.Add("1504","Gillian Megan")
printfn "Dictionary - students: %A" dict

编译并执行程序时,它会产生以下输出 -

Dictionary - students: seq
[[1501, Zara Ali]; [1502, Rishita Gupta]; [1503, Robin Sahoo];
[1504, Gillian Megan]]

The Dictionary(TKey,TValue) Class

Dictionary(TKey,TValue)类表示键和值的集合。

下表提供了List(T)类的属性,构造函数和方法 -

属性 (Properties)

属性 描述
Comparer 获取用于确定字典的键相等性的IEqualityComparer(T)。
Count 获取Dictionary(TKey,TValue)中包含的键/值对的数量。
Item 获取或设置与指定键关联的值。
Keys 获取包含Dictionary(TKey,TValue)中的键的集合。
Values 获取包含Dictionary(TKey,TValue)中的值的集合。

构造函数 (Constructors)

构造函数 描述
Dictionary(TKey, TValue)() 初始化一个新的Dictionary(TKey, TValue)类实例,该类具有默认的初始容量,并使用默认的相等比较器作为键类型。
Dictionary(TKey, TValue)(IDictionary(TKey, TValue)) 初始化Dictionary(TKey, TValue)类的新实例,该类包含从指定的IDictionary(TKey, TValue)复制的元素IDictionary(TKey, TValue)并使用默认的相等比较器作为键类型。
Dictionary(TKey, TValue)(IEqualityComparer(TKey)) 初始化一个新的Dictionary(TKey, TValue)类实例,它是空的,具有默认的初始容量,并使用指定的IEqualityComparer(T).
Dictionary(TKey, TValue)(Int32) 初始化一个新的Dictionary(TKey, TValue)类实例,它是空的,具有指定的初始容量,并使用默认的相等比较器作为键类型。
Dictionary(TKey, TValue)(IDictionary(TKey, TValue), IEqualityComparer(TKey)) 初始化Dictionary(TKey, TValue)类的新实例,该类包含从指定的IDictionary(TKey, TValue)复制的元素并使用指定的IEqualityComparer(T).
Dictionary(TKey, TValue)(Int32, IEqualityComparer(TKey)) 初始化Dictionary(TKey, TValue)类的新实例,该类是空的,具有指定的初始容量,并使用指定的IEqualityComparer(T).
Dictionary(TKey, TValue)(SerializationInfo, StreamingContext) 使用序列化数据初始化ictionary(TKey, TValue)类的新实例。

方法 (Methods)

方法 描述
Add 将指定的键和值添加到字典中。
Clear 从Dictionary(TKey,TValue)中删除所有键和值。
ContainsKey 确定Dictionary(TKey,TValue)是否包含指定的键。
ContainsValue 确定Dictionary(TKey,TValue)是否包含特定值。
Equals(Object) 确定指定的对象是否等于当前对象。 (继承自Object。)
Finalize 允许对象在垃圾回收回收之前尝试释放资源并执行其他清理操作。 (继承自Object。)
GetEnumerator 返回一个遍历Dictionary(TKey,TValue)的枚举器。
GetHashCode 用作默认哈希函数。 (继承自Object。)
GetObjectData 实现System.Runtime.Serialization.ISerializable接口并返回序列化Dictionary(TKey,TValue)实例所需的数据。
GetType 获取当前实例的Type。 (继承自Object。)
MemberwiseClone 创建当前Object的浅表副本。 (继承自Object。)
OnDeserialization 实现System.Runtime.Serialization.ISerializable接口,并在反序列化完成时引发反序列化事件。
Remove 从Dictionary(TKey,TValue)中删除指定键的值。
ToString 返回表示当前对象的字符串。 (继承自Object。)
TryGetValue 获取与指定键关联的值。

例子 (Example)

open System.Collections.Generic
let dict = new Dictionary<string, string>()
dict.Add("1501", "Zara Ali")
dict.Add("1502","Rishita Gupta")
dict.Add("1503","Robin Sahoo")
dict.Add("1504","Gillian Megan")
printfn "Dictionary - students: %A" dict
printfn "Total Number of Students: %d" dict.Count
printfn "The keys: %A" dict.Keys
printf"The Values: %A" dict.Values

编译并执行程序时,它会产生以下输出 -

Dictionary - students: seq
[[1501, Zara Ali]; [1502, Rishita Gupta]; [1503, Robin Sahoo];
[1504, Gillian Megan]]
Total Number of Students: 4
The keys: seq ["1501"; "1502"; "1503"; "1504"]
The Values: seq ["Zara Ali"; "Rishita Gupta"; "Robin Sahoo"; "Gillian Megan"]

F# - Basic I/O

基本输入输出包括 -

  • 读取和写入控制台。
  • 读写文件。

Core.Printf Module

我们使用printfprintfn函数写入控制台。 在本节中,我们将研究F#的Printf模块的细节。

除了上述功能外,F#的Core.Printf模块还有其他各种方法,可以使用%标记作为占位符进行打印和格式化。 下表显示了简要说明的方法 -

描述
bprintf:StringBuilder→BuilderFormat →'T Prints to a StringBuilder.
eprintf:TextWriterFormat →'T 将格式化输出打印到stderr。
eprintfn:TextWriterFormat →'T 将格式化输出打印到stderr,添加换行符。
failwithf:StringFormat →'T 打印到字符串缓冲区并使用给定结果引发异常。
fprintf:TextWriter→TextWriterFormat →'T 打印到文本编写者。
fprintfn:TextWriter→TextWriterFormat →'T 打印到文本编写器,添加换行符。
kbprintf :(单位→'结果)→StringBuilder→BuilderFormat →'T 像bprintf一样,但调用指定的函数来生成结果。
kfprintf :(单位→'结果)→TextWriter→TextWriterFormat →'T 像fprintf一样,但调用指定的函数来生成结果。
kprintf :(字符串→'结果)→StringFormat →'T 像printf一样,但调用指定的函数来生成结果。 例如,这些使得打印力在所有输出都输入到通道之后进行冲洗,而不是之前。
ksprintf :(字符串→'结果)→StringFormat →'T 像sprintf一样,但调用指定的函数来生成结果。
printf:TextWriterFormat →'T 将格式化输出打印到stdout。
printfn:TextWriterFormat →'T 将格式化输出打印到stdout,添加换行符。
sprintf:StringFormat →'T 使用内部字符串缓冲区打印到字符串,并将结果作为字符串返回。

格式规范

格式规范用于根据程序员的需要格式化输入或输出。

这些是带有%标记的字符串,表示格式占位符。

Format占位符的语法是 -

%[flags][width][.precision][type]

type被解释为 -

类型 描述
%b 格式化为bool,格式为truefalse
%cFormats a character.
%s 格式化一个string,格式化为其内容,而不解释任何转义字符。
%d, %i 格式化为十进制整数格式的任何基本整数类型,如果基本整数类型已签名,则为有符号格式。
%u 格式化任何格式为无符号十进制整数的基本整数类型。
%x 格式化为无符号十六进制整数的任何基本整数类型,使用小写字母a到f。
%X 格式化为无符号十六进制整数的任何基本整数类型,使用大写字母A到F.
%o 格式化任何格式为无符号八进制整数的基本整数类型。
%e, %E, %f, %F, %g, %G 格式化使用C样式浮点格式规范格式化的任何基本浮点类型(float, float32)
%e, %E 格式化具有[ - ] d.dddde [sign] ddd形式的带符号值,其中d是单个十进制数字,dddd是一个或多个十进制数字,ddd正好是三位小数,符号是+或 - 。
%f 格式化具有[ - ] dddd.dddd形式的带符号值,其中dddd是一个或多个十进制数字。 小数点前的位数取决于数字的大小,小数点后的位数取决于请求的精度。
%g, %G 格式化以f或e格式打印的带符号值,以给定值和精度更紧凑为准。
%MFormats a Decimal value.
%O 格式化任何值,通过装箱对象并使用其ToString方法打印。
%A, %+A 格式化使用默认布局设置打印的任何值。 使用%+ A打印具有内部和私有表示的区分联合的结构。
%a

通用格式说明符,需要两个参数。 第一个参数是一个接受两个参数的函数:第一个是给定格式化函数的适当类型的上下文参数(例如,TextWriter),第二个是要打印的值,它输出或返回适当的文本。

第二个参数是要打印的特定值。

%t 通用格式说明符需要一个参数:一个函数,它接受给定格式化函数(aTextWriter)的相应类型的上下文参数,并输出或返回适当的文本。 基本整数类型是byte, sbyte, int16, uint16, int32, uint32, int64, uint64, nativeint,unativeint. 基本浮点类型是floatfloat32.

width是可选参数。 它是一个整数,表示结果的最小宽度。 例如,%5d打印一个至少包含5个字符的整数。

有效flags如下表所述 -

描述
0 指定添加零而不是空格以构成所需的宽度。
- 指定在指定宽度内左对齐结果。
+ 如果数字为正数,则指定添加+字符(以匹配负数的 - 符号)。
' ' (空间) 如果数字为正数,则指定添加额外空格(以匹配负数的 - 符号)。
#Invalid.

例子 (Example)

printf "Hello "
printf "World"
printfn ""
printfn "Hello "
printfn "World"
printf "Hi, I'm %s and I'm a %s" "Rohit" "Medical Student"
printfn "d: %f" 212.098f
printfn "e: %f" 504.768f
printfn "x: %g" 212.098f
printfn "y: %g" 504.768f
printfn "x: %e" 212.098f
printfn "y: %e" 504.768f
printfn "True: %b" true

编译并执行程序时,它会产生以下输出 -

Hello World
Hello
World
Hi, I'm Rohit and I'm a Medical Studentd: 212.098000
e: 504.768000
x: 212.098
y: 504.768
x: 2.120980e+002
y: 5.047680e+002
True: true

控制台类

该类是.NET框架的一部分。 它代表控制台应用程序的标准输入,输出和错误流。

它提供了各种读取和写入控制台的方法。 下表显示了方法 -

方法 描述
Beep() 通过控制台扬声器发出哔哔声。
Beep(Int32, Int32) 通过控制台扬声器播放指定频率和持续时间的哔哔声。
Clear 清除控制台缓冲区和显示信息的相应控制台窗口。
MoveBufferArea(Int32, Int32, Int32, Int32, Int32, Int32) 将屏幕缓冲区的指定源区域复制到指定的目标区域。
MoveBufferArea(Int32, Int32, Int32, Int32, Int32, Int32, Char, ConsoleColor, ConsoleColor) 将屏幕缓冲区的指定源区域复制到指定的目标区域。
OpenStandardError() 获取标准错误流。
OpenStandardError(Int32) 获取标准错误流,该错误流设置为指定的缓冲区大小。
OpenStandardInput() 获取标准输入流。
OpenStandardInput(Int32) 获取标准输入流,该输入流设置为指定的缓冲区大小。
OpenStandardOutput() 获取标准输出流。
OpenStandardOutput(Int32) 获取标准输出流,该输出流设置为指定的缓冲区大小。
Read 从标准输入流中读取下一个字符。
ReadKey() 获得用户按下的下一个字符或功能键。 按下的键显示在控制台窗口中。
ReadKey(Boolean) 获得用户按下的下一个字符或功能键。 按下的键可选地显示在控制台窗口中。
ReadLine 从标准输入流中读取下一行字符。
ResetColor 将前景和背景控制台颜色设置为其默认值。
SetBufferSize 将屏幕缓冲区的高度和宽度设置为指定的值。
SetCursorPosition 设置光标的位置。
SetError 将Error属性设置为指定的TextWriter对象。
SetIn 将In属性设置为指定的TextReader对象。
SetOut 将Out属性设置为指定的TextWriter对象。
SetWindowPosition 设置控制台窗口相对于屏幕缓冲区的位置。
SetWindowSize 将控制台窗口的高度和宽度设置为指定的值。
Write(Boolean) 将指定布尔值的文本表示写入标准输出流。
Write(Char) 将指定的Unicode字符值写入标准输出流。
Write(Char[]) 将指定的Unicode字符数组写入标准输出流。
Write(Decimal) 将指定的Decimal值的文本表示写入标准输出流。
Write(Double) 将指定的双精度浮点值的文本表示写入标准输出流。
Write(Int32) 将指定的32位有符号整数值的文本表示写入标准输出流。
Write(Int64) 将指定的64位有符号整数值的文本表示写入标准输出流。
Write(Object) 将指定对象的文本表示写入标准输出流。
Write(Single) 将指定的单精度浮点值的文本表示写入标准输出流。
Write(String) 将指定的字符串值写入标准输出流。
Write(UInt32) 将指定的32位无符号整数值的文本表示写入标准输出流。
Write(UInt64) 将指定的64位无符号整数值的文本表示写入标准输出流。
Write(String, Object) 使用指定的格式信息将指定对象的文本表示写入标准输出流。
Write(String, Object[]) 使用指定的格式信息将指定对象数组的文本表示写入标准输出流。
Write(Char[], Int32, Int32) 将指定的Unicode字符子数组写入标准输出流。
Write(String, Object, Object) 使用指定的格式信息将指定对象的文本表示写入标准输出流。
Write(String, Object, Object, Object) 使用指定的格式信息将指定对象的文本表示写入标准输出流。
Write(String, Object, Object, Object, Object) 使用指定的格式信息将指定对象和可变长度参数列表的文本表示写入标准输出流。
WriteLine() 将当前行终止符写入标准输出流。
WriteLine(Boolean) 将指定布尔值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Char) 将指定的Unicode字符,后跟当前行终止符,值写入标准输出流。
WriteLine(Char[]) 将指定的Unicode字符数组(后跟当前行终止符)写入标准输出流。
WriteLine(Decimal) 将指定的Decimal值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Double) 将指定的双精度浮点值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Int32) 将指定的32位有符号整数值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Int64) 将指定的64位有符号整数值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Object) 将指定对象的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Single) 将指定的单精度浮点值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(String) 将指定的字符串值(后跟当前行终止符)写入标准输出流。
WriteLine(UInt32) 将指定的32位无符号整数值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(UInt64) 将指定的64位无符号整数值的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(String, Object) 使用指定的格式信息将指定对象的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(String, Object[]) 使用指定的格式信息将指定对象数组的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(Char[], Int32, Int32) 将指定的Unicode字符子数组(后跟当前行终止符)写入标准输出流。
WriteLine(String, Object, Object) 使用指定的格式信息将指定对象的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(String, Object, Object, Object) 使用指定的格式信息将指定对象的文本表示形式(后跟当前行终止符)写入标准输出流。
WriteLine(String, Object, Object, Object, Object) 使用指定的格式信息将指定对象和可变长度参数列表的文本表示形式(后跟当前行终止符)写入标准输出流。

以下示例演示从控制台读取并写入其中 -

例子 (Example)

open System
let main() =
   Console.Write("What's your name? ")
   let name = Console.ReadLine()
   Console.Write("Hello, {0}\n", name)
   Console.WriteLine(System.String.Format("Big Greetings from {0} and {1}", "IoWiki", "Absoulte Classes"))
   Console.WriteLine(System.String.Format("|{0:yyyy-MMM-dd}|", System.DateTime.Now))
main()

编译并执行程序时,它会产生以下输出 -

What's your name? Kabir
Hello, Kabir
Big Greetings from IoWiki and Absoulte Classes
|2015-Jan-05|

System.IO命名空间

System.IO命名空间包含用于执行基本I/O的各种有用类。

它包含允许读取和写入文件的类型或类,以及提供基本文件和目录支持的数据流和类型。

用于处理文件系统的类 -

  • System.IO.File类用于创建,附加和删除文件。
  • System.IO.Directory类用于创建,移动和删除目录。
  • System.IO.Path类对表示文件路径的字符串执行操作。
  • System.IO.FileSystemWatcher类允许用户监听目录以进行更改。

用于处理流的类(字节序列) -

  • System.IO.StreamReader类用于从流中读取字符。
  • System.IO.StreamWriter类用于将字符写入流。
  • System.IO.MemoryStream类创建内存中的字节流。

下表显示了命名空间中提供的所有类以及简要说明 -

描述
BinaryReader 将原始数据类型读取为特定编码中的二进制值。
BinaryWriter 将二进制中的原始类型写入流并支持以特定编码写入字符串。
BufferedStream 添加缓冲层以在另一个流上读取和写入操作。
Directory 公开用于创建,移动和枚举目录和子目录的静态方法。
DirectoryInfo 公开用于创建,移动和枚举目录和子目录的实例方法。
DirectoryNotFoundException 无法找到文件或目录的一部分时引发的异常。
DriveInfo 提供对驱动器信息的访问。
DriveNotFoundException 尝试访问不可用的驱动器或共享时引发的异常。
EndOfStreamException 尝试在流结束之后尝试读取时抛出的异常。
ErrorEventArgs 为FileSystemWatcher.Error事件提供数据。
File 提供用于创建,复制,删除,移动和打开单个文件的静态方法,并帮助创建FileStream对象。
FileFormatException 当输入文件或应该符合某种文件格式规范的数据流格式错误时引发的异常。
FileInfo 提供用于创建,复制,删除,移动和打开文件的属性和实例方法,并帮助创建FileStream对象。
FileLoadException 找到托管程序集但无法加载时引发的异常。
FileNotFoundException 尝试访问磁盘上不存在的文件失败时引发的异常。
FileStream 在文件周围显示Stream,支持同步和异步读写操作。
FileSystemEventArgs 提供目录事件的数据 - 已更改,已创建,已删除。
FileSystemInfo 为FileInfo和DirectoryInfo对象提供基类。
FileSystemWatcher 当目录或目录中的文件发生更改时,侦听文件系统更改通知并引发事件。
InternalBufferOverflowException 内部缓冲区溢出时抛出的异常。
InvalidDataException 数据流格式无效时引发的异常。
IODescriptionAttribute 设置可视设计器在引用事件,扩展器或属性时可以显示的描述。
IOException 发生I/O错误时引发的异常。
MemoryStream 创建其后备存储为内存的流。
Path 对包含文件或目录路径信息的String实例执行操作。 这些操作以跨平台方式执行。
PathTooLongException 当路径或文件名长于系统定义的最大长度时引发的异常。
PipeException 在命名管道中发生错误时抛出。
RenamedEventArgs 为重命名的事件提供数据。
Stream 提供字节序列的通用视图。 这是一个抽象类。
StreamReader 实现一个TextReader,它从特定编码的字节流中读取字符。
StreamWriter 实现TextWriter,以便以特定编码将字符写入流中。 要浏览此类型的.NET Framework源代码,请参阅参考源。
StringReader 实现从字符串中读取的TextReader。
StringWriter 实现TextWriter以将信息写入字符串。 信息存储在底层的StringBuilder中。
TextReader 表示可以读取一系列连续字符的阅读器。
TextWriter 表示可以编写一系列连续字符的编写器。 这个类是抽象的。
UnmanagedMemoryAccessor 提供对托管代码的非托管内存块的随机访问。
UnmanagedMemoryStream 提供从托管代码访问非托管内存块的权限。
WindowsRuntimeStorageExtensions 在开发Windows应用商店应用时,包含Windows运行时中IStorageFile和IStorageFolder接口的扩展方法。
WindowsRuntimeStreamExtensions 包含用于在Windows运行时中的流与.NET for Windows Store应用程序中的托管流之间进行转换的扩展方法。

例子 (Example)

以下示例创建一个名为test.txt的文件,在其中写入消息,从文件中读取文本并将其打印在控制台上。

Note - 执行此操作所需的代码量要少得多!

open System.IO // Name spaces can be opened just as modules
File.WriteAllText("test.txt", "Hello There\n Welcome to:\n IOWIKI")
let msg = File.ReadAllText("test.txt")
printfn "%s" msg

编译并执行程序时,它会产生以下输出 -

Hello There
Welcome to:
IOWIKI

F# - Generics

泛型允许您在类或方法中延迟编程元素的数据类型的指定,直到它在程序中实际使用。 换句话说,泛型允许您编写可以使用任何数据类型的类或方法。

您可以使用数据类型的替换参数编写类或方法的规范。 当编译器遇到类的构造函数或方法的函数调用时,它会生成处理特定数据类型的代码。

在F#中,函数值,方法,属性和聚合类型(如类,记录和区分联合)可以是通用的。

通用构造包含至少一个类型参数。 通用函数和类型使您能够编写适用于各种类型的代码,而无需重复每种类型的代码。

语法 (Syntax)

编写泛型构造的语法如下 -

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
   function-body
// Explicitly generic method.
[ static ] member object-identifer.method-name<type-parameters> parameter-list [ return-type ] =
   method-body
// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

例子 (Examples)

(* Generic Function *)
let printFunc<'T> x y =
   printfn "%A, %A" x y
printFunc<float> 10.0 20.0

编译并执行程序时,它会产生以下输出 -

10.0, 20.0

您还可以使用单引号语法使函数通用 -

(* Generic Function *)
let printFunction (x: 'a) (y: 'a) =
   printfn "%A %A" x y
printFunction 10.0 20.0

编译并执行程序时,它会产生以下输出 -

10.0 20.0

请注意,使用泛型函数或方法时,可能不必指定类型参数。 但是,如果存在歧义,您可以像在第一个示例中那样在尖括号中提供类型参数。

如果您有多个类型,则使用逗号分隔多个类型参数。

通用类

与泛型函数一样,您也可以编写泛型类。 以下示例演示了这一点 -

type genericClass<'a> (x: 'a) =
   do printfn "%A" x
let gr = new genericClass<string>("zara")
let gs = genericClass( seq { for i in 1 .. 10 -> (i, i*i) } )

编译并执行程序时,它会产生以下输出 -

"zara"
seq [(1, 1); (2, 4); (3, 9); (4, 16); ...]

F# - Delegates

委托是一个引用类型变量,用于保存对方法的引用。 可以在运行时更改引用。 F#委托类似于C或C ++中的函数指针。

宣布代表

委托声明确定委托可以引用的方法。 委托可以引用一种方法,该方法具有与委托相同的签名。

委托声明的语法是 -

type delegate-typename = delegate of type1 -> type2

例如,考虑代表 -

// Delegate1 works with tuple arguments.
type Delegate1 = delegate of (int * int) -> int
// Delegate2 works with curried arguments.
type Delegate2 = delegate of int * int -> int

两个委托都可以用于引用具有两个int参数的任何方法,并返回一个int类型变量。

在语法中 -

  • type1表示参数类型。

  • type2表示返回类型。

请注意 -

  • 参数类型是自动curry。

  • 代理可以附加到函数值,静态或实例方法。

  • F#函数值可以作为参数直接传递给委托构造函数。

  • 对于静态方法,通过使用类的名称和方法来调用委托。 对于实例方法,使用对象实例和方法的名称。

  • 委托类型上的Invoke方法调用封装的函数。

  • 此外,通过引用不带括号的Invoke方法名称,可以将委托作为函数值传递。

以下示例演示了该概念 -

例子 (Example)

type Myclass() =
   static member add(a : int, b : int) =
      a + b
   static member sub (a : int) (b : int) =
      a - b
   member x.Add(a : int, b : int) =
      a + b
   member x.Sub(a : int) (b : int) =
      a - b
// Delegate1 works with tuple arguments.
type Delegate1 = delegate of (int * int) -> int
// Delegate2 works with curried arguments.
type Delegate2 = delegate of int * int -> int
let InvokeDelegate1 (dlg : Delegate1) (a : int) (b: int) =
   dlg.Invoke(a, b)
let InvokeDelegate2 (dlg : Delegate2) (a : int) (b: int) =
   dlg.Invoke(a, b)
// For static methods, use the class name, the dot operator, and the
// name of the static method.
let del1 : Delegate1 = new Delegate1( Myclass.add )
let del2 : Delegate2 = new Delegate2( Myclass.sub )
let mc = Myclass()
// For instance methods, use the instance value name, the dot operator, and the instance method name.
let del3 : Delegate1 = new Delegate1( mc.Add )
let del4 : Delegate2 = new Delegate2( mc.Sub )
for (a, b) in [ (400, 200); (100, 45) ] do
   printfn "%d + %d = %d" a b (InvokeDelegate1 del1 a b)
   printfn "%d - %d = %d" a b (InvokeDelegate2 del2 a b)
   printfn "%d + %d = %d" a b (InvokeDelegate1 del3 a b)
   printfn "%d - %d = %d" a b (InvokeDelegate2 del4 a b)

编译并执行程序时,它会产生以下输出 -

400 + 200 = 600
400 - 200 = 200
400 + 200 = 600
400 - 200 = 200
100 + 45 = 145
100 - 45 = 55
100 + 45 = 145
100 - 45 = 55

F# - Enumerations

枚举是一组命名的整数常量。

在F#中, enumerations,也称为enums,是整数类型,其中标签分配给值的子集。 您可以使用它们代替文字,以使代码更易读和可维护。

声明枚举

声明枚举的一般语法是 -

type enum-name =
   | value1 = integer-literal1
   | value2 = integer-literal2
...

以下示例演示了枚举的用法 -

例子 (Example)

// Declaration of an enumeration.
type Days =
   | Sun = 0
   | Mon = 1
   | Tues = 2
   | Wed = 3
   | Thurs = 4
   | Fri = 5
   | Sat = 6
// Use of an enumeration.
let weekend1 : Days = Days.Sat
let weekend2 : Days = Days.Sun
let weekDay1 : Days = Days.Mon
printfn "Monday: %A" weekDay1
printfn "Saturday: %A" weekend1
printfn "Sunday: %A" weekend2

编译并执行程序时,它会产生以下输出 -

Monday: Mon
Saturday: Sat
Sunday: Sun

F# - Pattern Matching

模式匹配允许您“将数据与逻辑结构或结构进行比较,将数据分解为组成部分,或以各种方式从数据中提取信息”。

换句话说,它提供了一种更灵活,更强大的方法,可以根据一系列条件测试数据,并根据满足的条件执行一些计算。

从概念上讲,它就像一系列if ... then语句。

语法 (Syntax)

在高级术语中,模式匹配遵循F#中的这种语法 -

match expr with
| pat1 - result1
| pat2 -> result2
| pat3 when expr2 -> result3
| _ -> defaultResult

Where,

  • 每个| 符号定义条件。
  • - >符号表示“如果条件为真,则返回此值......”。
  • _符号提供默认模式,这意味着它匹配所有其他内容,如通配符。

例子1 (Example 1)

以下示例使用模式匹配语法计算斐波纳契数 -

let rec fib n =
   match n with
   | 0 -> 0
   | 1 -> 1
   | _ -> fib (n - 1) + fib (n - 2)
for i = 1 to 10 do
   printfn "Fibonacci %d: %d" i (fib i)

编译并执行程序时,它会产生以下输出 -

Fibonacci 1: 1
Fibonacci 2: 1
Fibonacci 3: 2
Fibonacci 4: 3
Fibonacci 5: 5
Fibonacci 6: 8
Fibonacci 7: 13
Fibonacci 8: 21
Fibonacci 9: 34
Fibonacci 10: 55

您还可以将多个条件链接在一起,这些条件返回相同的值。 例如 -

例子2 (Example 2)

let printSeason month =
   match month with
   | "December" | "January" | "February" -> printfn "Winter"
   | "March" | "April" -> printfn "Spring"
   | "May" | "June" -> printfn "Summer"
   | "July" | "August" -> printfn "Rainy"
   | "September" | "October" | "November" -> printfn "Autumn"
   | _ -> printfn "Season depends on month!"
printSeason "February"
printSeason "April"
printSeason "November"
printSeason "July"

编译并执行程序时,它会产生以下输出 -

Winter
Spring
Autumn
Rainy

模式匹配功能 (Pattern Matching Functions)

F#允许您使用function关键字编写模式匹配函数 -

let getRate = function
   | "potato" -> 10.00
   | "brinjal" -> 20.50
   | "cauliflower" -> 21.00
   | "cabbage" -> 8.75
   | "carrot" -> 15.00
   | _ -> nan (* nan is a special value meaning "not a number" *)
printfn "%g"(getRate "potato")
printfn "%g"(getRate "brinjal")
printfn "%g"(getRate "cauliflower")
printfn "%g"(getRate "cabbage")
printfn "%g"(getRate "carrot")

编译并执行程序时,它会产生以下输出 -

10
20.5
21
8.75
15

向模式添加过滤器或防护

您可以使用when关键字向模式添加过滤器或保护。

例子1 (Example 1)

let sign = function
   | 0 -> 0
   | x when x < 0 -> -1
   | x when x > 0 -> 1
printfn "%d" (sign -20)
printfn "%d" (sign 20)
printfn "%d" (sign 0)

编译并执行程序时,它会产生以下输出 -

-1
1
0

例子2 (Example 2)

let compareInt x =
   match x with
   | (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
   | (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
   | (var1, var2) -> printfn "%d equals %d" var1 var2
compareInt (11,25)
compareInt (72, 10)
compareInt (0, 0)

编译并执行程序时,它会产生以下输出 -

11 is less than 25
72 is greater than 10
0 equals 0

模式与元组匹配

以下示例演示了与元组匹配的模式 -

let greeting (name, subject) =
   match (name, subject) with
   | ("Zara", _) -> "Hello, Zara"
   | (name, "English") -> "Hello, " + name + " from the department of English"
   | (name, _) when subject.StartsWith("Comp") -> "Hello, " + name + " from the department of Computer Sc."
   | (_, "Accounts and Finance") -> "Welcome to the department of Accounts and Finance!"
   | _ -> "You are not registered into the system"
printfn "%s" (greeting ("Zara", "English"))
printfn "%s" (greeting ("Raman", "Computer Science"))
printfn "%s" (greeting ("Ravi", "Mathematics"))

编译并执行程序时,它会产生以下输出 -

Hello, Zara
Hello, Raman from the department of Computer Sc.
You are not registered into the system

与记录匹配的模式

以下示例演示了与记录匹配的模式 -

type Point = { x: float; y: float }
let evaluatePoint (point: Point) =
   match point with
   | { x = 0.0; y = 0.0 } -> printfn "Point is at the origin."
   | { x = xVal; y = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
   | { x = 0.0; y = yVal } -> printfn "Point is on the y-axis. Value is %f." yVal
   | { x = xVal; y = yVal } -> printfn "Point is at (%f, %f)." xVal yVal
evaluatePoint { x = 0.0; y = 0.0 }
evaluatePoint { x = 10.0; y = 0.0 }
evaluatePoint { x = 0.0; y = 10.0 }
evaluatePoint { x = 10.0; y = 10.0 }

编译并执行程序时,它会产生以下输出 -

Point is at the origin.
Point is on the x-axis. Value is 10.000000.
Point is on the y-axis. Value is 10.000000.
Point is at (10.000000, 10.000000).

F# - Exception Handling

例外是在执行程序期间出现的问题。 F#异常是对程序运行时出现的异常情况的响应,例如尝试除以零。

例外提供了一种将控制从程序的一个部分转移到另一个部分的方法。 F#异常处理提供以下结构 -

构造 描述
raise exprRaises the given exception.
failwith expr 引发System.Exception异常。
try expr with rules 捕获与模式规则匹配的表达式。
尝试expr finally expr 在计算成功和引发异常时执行finally表达式。
| :? ArgumentException的 与给定.NET异常类型匹配的规则。
| :? ArgumentException为e 与给定.NET异常类型匹配的规则,将名称e绑定到异常对象值。
| 失败(msg)→expr 与给定的携带数据的F#异常匹配的规则。
| exn→expr 匹配任何异常的规则,将名称exn绑定到异常对象值。
| 当expr→expr时exn 在给定条件下匹配异常的规则,将名称exn绑定到异常对象值。

让我们从异常处理的基本语法开始。

语法 (Syntax)

F#异常处理块的基本语法是 -

exception exception-type of argument-type

Where,

  • exception-type是新F#异常类型的名称。

  • argument-type表示引发此类型的异常时可以提供的参数的类型。

  • 可以使用参数类型的元组类型指定多个参数。

try...with表达式用于F#语言中的异常处理。

try ... with语法的语法是 -

try
   expression1
with
   | pattern1 -> expression2
   | pattern2 -> expression3
...

try...finally表达式允许您执行清理代码,即使代码块引发异常也是如此。

try ... finally语法的语法是 -

try
   expression1
finally
   expression2

raise函数用于指示发生了错误或异常情况。 它还捕获有关异常对象中的错误的信息。

提升功能的语法是 -

raise (expression)

failwith函数会生成F#异常。

failwith函数的语法是 -

failwith error-message-string

invalidArg函数生成参数异常。

invalidArg parameter-name error-message-string

异常处理示例

例子1 (Example 1)

以下程序通过简单的尝试显示基本的异常处理...使用块 -

let divisionprog x y =
   try
      Some (x/y)
   with
      | :? System.DivideByZeroException -> printfn "Division by zero!"; None
let result1 = divisionprog 100 0

编译并执行程序时,它会产生以下输出 -

Division by zero!

例子2 (Example 2)

F#提供了一种用于声明异常的exception类型。 您可以在try...with expression中直接在过滤器中使用异常类型。

以下示例演示了这一点 -

exception Error1 of string
// Using a tuple type as the argument type.
exception Error2 of string * int
let myfunction x y =
   try
      if x = y then raise (Error1("Equal Number Error"))
      else raise (Error2("Error Not detected", 100))
   with
      | Error1(str) -> printfn "Error1 %s" str
      | Error2(str, i) -> printfn "Error2 %s %d" str i
myfunction 20 10
myfunction 5 5

编译并执行程序时,它会产生以下输出 -

Error2 Error Not detected 100
Error1 Equal Number Error

例子3 (Example 3)

以下示例演示嵌套异常处理 -

exception InnerError of string
exception OuterError of string
let func1 x y =
   try
      try
         if x = y then raise (InnerError("inner error"))
         else raise (OuterError("outer error"))
      with
         | InnerError(str) -> printfn "Error:%s" str
   finally
      printfn "From the finally block."
let func2 x y =
   try
      func1 x y
   with
      | OuterError(str) -> printfn "Error: %s" str
func2 100 150
func2 100 100
func2 100 120

编译并执行程序时,它会产生以下输出 -

From the finally block.
Error: outer error
Error:inner error
From the finally block.
From the finally block.
Error: outer error

例子4 (Example 4)

以下函数演示了failwith函数 -

let divisionFunc x y =
   if (y = 0) then failwith "Divisor cannot be zero."
   else
      x/y
let trydivisionFunc x y =
   try
      divisionFunc x y
   with
      | Failure(msg) -> printfn "%s" msg; 0
let result1 = trydivisionFunc 100 0
let result2 = trydivisionFunc 100 4
printfn "%A" result1
printfn "%A" result2

编译并执行程序时,它会产生以下输出 -

Divisor cannot be zero.
0
25

例子5 (Example 5)

invalidArg函数生成参数异常。 以下程序演示了这一点 -

let days = [| "Sunday"; "Monday"; "Tuesday"; "Wednesday"; "Thursday"; "Friday"; "Saturday" |]
let findDay day =
   if (day > 7 || day < 1)
      then invalidArg "day" (sprintf "You have entered %d." day)
   days.[day - 1]
printfn "%s" (findDay 1)
printfn "%s" (findDay 5)
printfn "%s" (findDay 9)

编译并执行程序时,它会产生以下输出 -

Sunday
Thursday
Unhandled Exception:
System.ArgumentException: You have entered 9.
…

根据系统的不同,还将显示有关系统中导致错误的文件和变量的其他一些信息。

F# - Classes

类是表示可以具有属性,方法和事件的对象的类型。 “它们用于模拟应用程序中的操作,流程和任何概念实体”。

语法 (Syntax)

定义类类型的语法如下 -

// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
   [ class ]
      [ inherit base-type-name(base-constructor-args) ]
      [ let-bindings ]
      [ do-bindings ]
      member-list
      ...
   [ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...

Where,

  • type-name是任何有效的标识符。 默认访问修饰符是public

  • type-params描述了可选的泛型类型参数。

  • parameter-list描述了构造函数参数。 主构造函数的默认访问修饰符是public

  • 与可选的as关键字一起使用的identifier as实例变量或self-identifier,提供了名称self-identifier,可以在类型定义中使用该名称来引用该类型的实例。

  • inherit关键字允许您指定类的基类。

  • let绑定允许您声明类的本地字段或函数值。

  • do-bindings部分包括在对象构造时要执行的代码。

  • member-list包含其他构造函数,实例和静态方法声明,接口声明,抽象绑定以及属性和事件声明。

  • 标记定义的开头和结尾的关键字classend是可选的。

一个类的构造函数

构造函数是用于创建类类型实例的代码。

在F#中,构造函数与其他.Net语言的工作方式差别不大。 在类定义中,主构造函数的参数被描述为parameter-list。

构造函数的主体由letdo绑定组成。

您可以使用new关键字添加其他构造函数来添加成员 -

new (argument-list) = constructor-body

以下示例说明了这一概念 -

例子 (Example)

以下程序创建一个行类以及一个构造函数,该构造函数在创建类的对象时计算行的长度 -

type Line = class
   val X1 : float
   val Y1 : float
   val X2 : float
   val Y2 : float
   new (x1, y1, x2, y2) as this =
      { X1 = x1; Y1 = y1; X2 = x2; Y2 = y2;}
      then
         printfn " Creating Line: {(%g, %g), (%g, %g)}\nLength: %g"
            this.X1 this.Y1 this.X2 this.Y2 this.Length
   member x.Length =
      let sqr x = x * x
      sqrt(sqr(x.X1 - x.X2) + sqr(x.Y1 - x.Y2) )
end
let aLine = new Line(1.0, 1.0, 4.0, 5.0)

编译并执行程序时,它会产生以下输出 -

Creating Line: {(1, 1), (4, 5)}
Length: 5

让绑定

类定义中的let绑定允许您为F#类定义私有字段和私有函数。

type Greetings(name) as gr =
   let data = name
   do
      gr.PrintMessage()
   member this.PrintMessage() =
      printf "Hello %s\n" data
let gtr = new Greetings("Zara")

编译并执行程序时,它会产生以下输出 -

Hello Zara

请注意Greetings类使用self-identifier gr

F# - Structures

F#中的结构是值类型数据类型。 它可以帮助您创建单个变量,保存各种数据类型的相关数据。 struct关键字用于创建结构。

语法 (Syntax)

定义结构的语法如下 -

[ attributes ]
type [accessibility-modifier] type-name =
   struct
      type-definition-elements
   end
// or
[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
   type-definition-elements

有两种语法。 主要使用第一种语法,因为如果使用structend关键字,则可以省略StructAttribute属性。

结构定义要素提供 -

  • 成员声明和定义。
  • 构造函数和可变和不可变字段。
  • 成员和接口实现。

与类不同,结构不能被继承,也不能包含let或do bindings。 因为,结构没有让绑定; 您必须使用val关键字在结构中声明字段。

使用val关键字定义字段及其类型时,无法初始化字段值,而是将它们初始化为零或null。 因此,对于具有隐式构造函数的结构, val声明将使用DefaultValue属性进行注释。

例子 (Example)

以下程序与构造函数一起创建一个行结构。 程序使用结构计算线的长度 -

type Line = struct
   val X1 : float
   val Y1 : float
   val X2 : float
   val Y2 : float
   new (x1, y1, x2, y2) =
      {X1 = x1; Y1 = y1; X2 = x2; Y2 = y2;}
end
let calcLength(a : Line)=
   let sqr a = a * a
   sqrt(sqr(a.X1 - a.X2) + sqr(a.Y1 - a.Y2) )
let aLine = new Line(1.0, 1.0, 4.0, 5.0)
let length = calcLength aLine
printfn "Length of the Line: %g " length

编译并执行程序时,它会产生以下输出 -

Length of the Line: 5

F# - Operator Overloading

您可以重新定义或重载F#中可用的大多数内置运算符。 因此,程序员也可以使用具有用户定义类型的运算符。

运算符是具有特殊名称的函数,括在括号中。 必须将它们定义为静态类成员。 与任何其他函数一样,重载运算符具有返回类型和参数列表。

以下示例显示了复数上的+运算符 -

//overloading + operator
static member (+) (a : Complex, b: Complex) =
Complex(a.x + b.x, a.y + b.y)

上面的函数为用户定义的类Complex实现了加法运算符(+)。 它添加了两个对象的属性并返回生成的Complex对象。

运算符重载的实现

以下程序显示了完整的实现 -

//implementing a complex class with +, and - operators
//overloaded
type Complex(x: float, y : float) =
   member this.x = x
   member this.y = y
   //overloading + operator
   static member (+) (a : Complex, b: Complex) =
      Complex(a.x + b.x, a.y + b.y)
   //overloading - operator
   static member (-) (a : Complex, b: Complex) =
      Complex(a.x - b.x, a.y - b.y)
   // overriding the ToString method
   override this.ToString() =
      this.x.ToString() + " " + this.y.ToString()
//Creating two complex numbers
let c1 = Complex(7.0, 5.0)
let c2 = Complex(4.2, 3.1)
// addition and subtraction using the
//overloaded operators
let c3 = c1 + c2
let c4 = c1 - c2
//printing the complex numbers
printfn "%s" (c1.ToString())
printfn "%s" (c2.ToString())
printfn "%s" (c3.ToString())
printfn "%s" (c4.ToString())

编译并执行程序时,它会产生以下输出 -

7 5
4.2 3.1
11.2 8.1
2.8 1.9

F# - Inheritance

面向对象编程中最重要的概念之一是继承。 继承允许我们根据另一个类定义一个类,这使得创建和维护应用程序变得更容易。 这也提供了重用代码功能和快速实现时间的机会。

在创建类时,程序员可以指定新类应该继承现有类的成员,而不是编写全新的数据成员和成员函数。 此现有类称为基类,新类称为派生类。

继承的想法实现了IS-A关系。 例如,哺乳动物是动物,狗是IS-A哺乳动物,因此也是狗IS-A动物,等等。

基类和子类

子类派生自已定义的基类。 子类继承基类的成员,并且具有自己的成员。

使用inherit关键字定义子类,如下所示 -

type MyDerived(...) =
   inherit MyBase(...)

在F#中,一个类最多只能有一个直接基类。 如果未使用inherit关键字指定基类,则该类将隐式继承自Object。

请注意 -

  • 派生类的用户可以使用基类的方法和成员,例如派生类的直接成员。

  • 让绑定和构造函数参数对类是私有的,因此无法从派生类访问。

  • 关键字base指的是基类实例。 它像自我标识符一样使用。

例子 (Example)

type Person(name) =
   member x.Name = name
   member x.Greet() = printfn "Hi, I'm %s" x.Name
type Student(name, studentID : int) =
   inherit Person(name)
   let mutable _GPA = 0.0
   member x.StudentID = studentID
   member x.GPA
      with get() = _GPA
      and set value = _GPA <- value
type Teacher(name, expertise : string) =
   inherit Person(name)
   let mutable _salary = 0.0
   member x.Salary
      with get() = _salary
      and set value = _salary <- value
   member x.Expertise = expertise
//using the subclasses
let p = new Person("Mohan")
let st = new Student("Zara", 1234)
let tr = new Teacher("Mariam", "Java")
p.Greet()
st.Greet()
tr.Greet()

编译并执行程序时,它会产生以下输出 -

Hi, I'm Mohan
Hi, I'm Zara
Hi, I'm Mariam

重写方法

您可以覆盖基类方法的默认行为,并在子类或派生类中以不同方式实现它。

默认情况下,F#中的方法不可覆盖。

要覆盖派生类中的方法,必须使用abstractdefault关键字将方法声明为可覆盖,如下所示 -

type Person(name) =
   member x.Name = name
   abstract Greet : unit -> unit
   default x.Greet() = printfn "Hi, I'm %s" x.Name

现在,可以在派生类中重写Person类的Greet方法。 以下示例演示了这一点 -

例子 (Example)

type Person(name) =
   member x.Name = name
   abstract Greet : unit -> unit
   default x.Greet() = printfn "Hi, I'm %s" x.Name
type Student(name, studentID : int) =
   inherit Person(name)
   let mutable _GPA = 0.0
   member x.StudentID = studentID
   member x.GPA
      with get() = _GPA
      and set value = _GPA <- value
   override x.Greet() = printfn "Student %s" x.Name
type Teacher(name, expertise : string) =
   inherit Person(name)
   let mutable _salary = 0.0
   member x.Salary
      with get() = _salary
      and set value = _salary <- value
   member x.Expertise = expertise
   override x.Greet() = printfn "Teacher %s." x.Name
//using the subclasses
let p = new Person("Mohan")
let st = new Student("Zara", 1234)
let tr = new Teacher("Mariam", "Java")
//default Greet
p.Greet()
//Overriden Greet
st.Greet()
tr.Greet()

编译并执行程序时,它会产生以下输出 -

Hi, I'm Mohan
Student Zara
Teacher Mariam.

抽象类

有时您需要提供对象的不完整实现,而实际上不应该实现。 稍后,其他程序员应该为完整的实现创建抽象类的子类。

例如,学校管理系统中不需要Person类。 但是,将需要学生或教师课程。 在这种情况下,您可以将Person类声明为抽象类。

AbstractClass属性告诉编译器该类有一些抽象成员。

您无法创建抽象类的实例,因为该类未完全实现。

以下示例演示了这一点 -

例子 (Example)

[<AbstractClass>]
type Person(name) =
   member x.Name = name
   abstract Greet : unit -> unit
type Student(name, studentID : int) =
   inherit Person(name)
   let mutable _GPA = 0.0
   member x.StudentID = studentID
   member x.GPA
      with get() = _GPA
      and set value = _GPA <- value
   override x.Greet() = printfn "Student %s" x.Name
type Teacher(name, expertise : string) =
   inherit Person(name)
   let mutable _salary = 0.0
   member x.Salary
      with get() = _salary
      and set value = _salary <- value
   member x.Expertise = expertise
   override x.Greet() = printfn "Teacher %s." x.Name
let st = new Student("Zara", 1234)
let tr = new Teacher("Mariam", "Java")
//Overriden Greet
st.Greet()
tr.Greet()

编译并执行程序时,它会产生以下输出 -

Student Zara
Teacher Mariam.

F# - Interfaces

接口提供了一种编写类的实现细节的抽象方式。 它是一个模板,用于声明类必须实现并公开公开的方法。

语法 (Syntax)

接口指定其他类实现的相关成员集。 它具有以下语法 -

// Interface declaration:
[ attributes ]
type interface-name =
   [ interface ]
      [ inherit base-interface-name ...]
      abstract member1 : [ argument-types1 -> ] return-type1
      abstract member2 : [ argument-types2 -> ] return-type2
      ...
   [ end ]
// Implementing, inside a class type definition:
interface interface-name with
   member self-identifier.member1 argument-list = method-body1
   member self-identifier.member2 argument-list = method-body2
// Implementing, by using an object expression:
[ attributes ]
let class-name (argument-list) =
   { new interface-name with
      member self-identifier.member1 argument-list = method-body1
      member self-identifier.member2 argument-list = method-body2
      [ base-interface-definitions ]
   }
member-list

请注意 -

  • 在接口声明中,未实现成员。

  • 成员是抽象的,由abstract关键字声明。 但是,您可以使用default关键字提供默认实现。

  • 您可以使用对象表达式或使用类类型来实现接口。

  • 在类或对象实现中,您需要为接口的抽象方法提供方法体。

  • 标记定义开始和结束的关键字interfaceend,是可选的。

例如,

type IPerson =
   abstract Name : string
   abstract Enter : unit -> unit
   abstract Leave : unit -> unit

调用接口方法

接口方法是通过接口调用的,而不是通过类的实例或类型实现接口。 要调用接口方法,请使用:》运算符(向上运算符)向上转换为接口类型。

例如,

(s :> IPerson).Enter()
(s :> IPerson).Leave()

以下示例说明了这一概念 -

例子 (Example)

type IPerson =
   abstract Name : string
   abstract Enter : unit -> unit
   abstract Leave : unit -> unit
type Student(name : string, id : int) =
   member this.ID = id
   interface IPerson with
      member this.Name = name
      member this.Enter() = printfn "Student entering premises!"
      member this.Leave() = printfn "Student leaving premises!"
type StuffMember(name : string, id : int, salary : float) =
   let mutable _salary = salary
   member this.Salary
      with get() = _salary
      and set(value) = _salary <- value
   interface IPerson with
      member this.Name = name
      member this.Enter() = printfn "Stuff member entering premises!"
      member this.Leave() = printfn "Stuff member leaving premises!"
let s = new Student("Zara", 1234)
let st = new StuffMember("Rohit", 34, 50000.0)
(s :> IPerson).Enter()
(s :> IPerson).Leave()
(st :> IPerson).Enter()
(st :> IPerson).Leave()

编译并执行程序时,它会产生以下输出 -

Student entering premises!
Student leaving premises!
Stuff member entering premises!
Stuff member leaving premises!

接口继承

接口可以从一个或多个基接口继承。

以下示例显示了该概念 -

type Interface1 =
   abstract member doubleIt: int -> int
type Interface2 =
   abstract member tripleIt: int -> int
type Interface3 =
   inherit Interface1
   inherit Interface2
   abstract member printIt: int -> string
type multiplierClass() =
   interface Interface3 with
      member this.doubleIt(a) = 2 * a
      member this.tripleIt(a) = 3 * a
      member this.printIt(a) = a.ToString()
let ml = multiplierClass()
printfn "%d" ((ml:>Interface3).doubleIt(5))
printfn "%d" ((ml:>Interface3).tripleIt(5))
printfn "%s" ((ml:>Interface3).printIt(5))

编译并执行程序时,它会产生以下输出 -

10
15
5

F# - Events

事件允许类在彼此之间发送和接收消息。

在GUI中,事件是用户操作,如按键,单击,鼠标移动等,或者某些事件,如系统生成的通知。 应用程序需要在事件发生时对其进行响应。 例如,中断。 事件用于进程间通信。

对象通过同步消息传递相互通信。

事件附加到其他功能; 对象将callback函数注册到事件,并且当某个对象触发事件(以及如果)时,将执行这些回调。

事件类和事件模块

Control.Event 类有助于创建可观察对象或事件。

它有以下实例成员来处理事件 -

会员 描述
Publish 将观察结果作为第一类值发布。
Trigger 使用给定参数触发观察。

Control.Event模块提供管理事件流的功能 -

描述
添加:('T→单位)→事件→单位 每次触发给定事件时运行给定的函数。
选择:('T→'U选项)→IEvent →IEvent 返回一个新事件,该事件触发原始事件中的一系列消息。 选择功能将原始消息带到可选的新消息。
filter:('T→bool)→IEvent →IEvent 返回一个侦听原始事件的新事件,并仅在事件的参数传递给定函数时触发结果事件。
map:('T→'U)→IEvent →IEvent 返回传递给定函数转换的值的新事件。
合并:IEvent →IEvent →IEvent 触发任一输入事件时触发输出事件。
pairwise:IEvent →IEvent 返回在第二次和随后触发输入事件时触发的新事件。 输入事件的Nth触发将来自第N-1thNth触发的参数作为一对传递。 传递给第N-1th触发的参数保持在隐藏的内部状态,直到第Nth触发发生。
分区:('T→bool)→IEvent →IEvent * IEvent 返回一个侦听原始事件的新事件,如果事件参数的谓词应用程序返回true,则返回第一个结果事件,如果返回false,则返回第二个事件。
扫描:('U→'T→'U)→'U→IEvent →IEvent 返回一个新事件,该事件由将给定累积函数应用于在输入事件上触发的连续值的结果组成。 内部状态项记录状态参数的当前值。 在执行累加功能期间内部状态未锁定,因此应注意输入IEvent不是由多个线程同时触发的。
拆分:('T→选择)→IEvent →IEvent * IEvent 返回一个新事件,该事件侦听原始事件,如果函数应用程序返回Choice1Of2,则返回第一个结果事件;如果返回Choice2Of2,则返回第二个事件。

创建事件

通过Event类创建和使用Event 。 Event构造函数用于创建事件。

例子 (Example)

type Worker(name : string, shift : string) =
   let mutable _name = name;
   let mutable _shift = shift;
   let nameChanged = new Event<unit>() (* creates event *)
   let shiftChanged = new Event<unit>() (* creates event *)
   member this.Name
      with get() = _name
      and set(value) = _name <- value
   member this.Shift
      with get() = _shift
      and set(value) = _shift <- value

在此之后,您需要将nameChanged字段公开为公共成员,以便侦听器可以挂钩事件,您使用事件的Publish属性 -

type Worker(name : string, shift : string) =
   let mutable _name = name;
   let mutable _shift = shift;
   let nameChanged = new Event<unit>() (* creates event *)
   let shiftChanged = new Event<unit>() (* creates event *)
   member this.NameChanged = nameChanged.Publish (* exposed event handler *)
   member this.ShiftChanged = shiftChanged.Publish (* exposed event handler *)
   member this.Name
      with get() = _name
      and set(value) = _name <- value
      nameChanged.Trigger() (* invokes event handler *)
   member this.Shift
      with get() = _shift
      and set(value) = _shift <- value
   shiftChanged.Trigger() (* invokes event handler *)

接下来,将回调添加到事件处理程序。 每个事件处理程序都具有IEvent 类型,它提供了几种方法 -

方法 描述
val添加:事件:('T→单位)→单位 将侦听器功能连接到事件。 触发事件时将调用侦听器。
val AddHandler:'del→unit 将处理程序委托对象连接到事件。 稍后可以使用RemoveHandler删除处理程序。 触发事件时将调用侦听器。
val RemoveHandler:'del→unit 从事件侦听器存储中删除侦听器委托。

以下部分提供了一个完整的示例。

例子 (Example)

以下示例演示了上面讨论的概念和技术 -

type Worker(name : string, shift : string) =
   let mutable _name = name;
   let mutable _shift = shift;
   let nameChanged = new Event<unit>() (* creates event *)
   let shiftChanged = new Event<unit>() (* creates event *)
   member this.NameChanged = nameChanged.Publish (* exposed event handler *)
   member this.ShiftChanged = shiftChanged.Publish (* exposed event handler *)
   member this.Name
      with get() = _name
      and set(value) = 
         _name <- value
         nameChanged.Trigger() (* invokes event handler *)
   member this.Shift
      with get() = _shift
      and set(value) = 
         _shift <- value
         shiftChanged.Trigger() (* invokes event handler *)
let wk = new Worker("Wilson", "Evening")
wk.NameChanged.Add(fun () -> printfn "Worker changed name! New name: %s" wk.Name)
wk.Name <- "William"
wk.NameChanged.Add(fun () -> printfn "-- Another handler attached to NameChanged!")
wk.Name <- "Bill"
wk.ShiftChanged.Add(fun () -> printfn "Worker changed shift! New shift: %s" wk.Shift)
wk.Shift <- "Morning"
wk.ShiftChanged.Add(fun () -> printfn "-- Another handler attached to ShiftChanged!")
wk.Shift <- "Night"

编译并执行程序时,它会产生以下输出 -

Worker changed name! New name: William
Worker changed name! New name: Bill
-- Another handler attached to NameChanged!
Worker changed shift! New shift: Morning
Worker changed shift! New shift: Night
-- Another handler attached to ShiftChanged!

F# - Modules

根据MSDN库,F#模块是一组F#代码构造,例如类型,值,函数值和do绑定中的代码。 它被实现为仅具有静态成员的公共语言运行时(CLR)类。

根据情况,整个文件是否包含在模块中,有两种类型的模块声明 -

  • 顶级模块声明
  • 本地模块声明

在顶级模块声明中,整个文件包含在模块中。 在这种情况下,文件中的第一个声明是模块声明。 您不必在顶级模块中缩进声明。

在本地模块声明中,只有在该模块声明下缩进的声明才是模块的一部分。

语法 (Syntax)

模块声明的语法如下 -

// Top-level module declaration.
module [accessibility-modifier] [qualified-namespace.]module-name
   declarations
// Local module declaration.
module [accessibility-modifier] module-name =
   declarations

请注意,accessibility-modifier可以是以下之一 - public,private,internal。 默认为public

以下示例将演示概念 -

例子1 (Example 1)

模块文件Arithmetic.fs -

module Arithmetic
let add x y =
   x + y
let sub x y =
   x - y
let mult x y =
   x * y
let div x y =
   x/y

程序文件main.fs -

// Fully qualify the function name.
open Arithmetic
let addRes = Arithmetic.add 25 9
let subRes = Arithmetic.sub 25 9
let multRes = Arithmetic.mult 25 9
let divRes = Arithmetic.div 25 9
printfn "%d" addRes
printfn "%d" subRes
printfn "%d" multRes
printfn "%d" divRes

编译并执行程序时,它会产生以下输出 -

34
16
225
2
110
90
1000
10

例子2 (Example 2)

// Module1
module module1 =
   // Indent all program elements within modules that are declared with an equal sign.
   let value1 = 100
   let module1Function x =
      x + value1
// Module2
module module2 =
   let value2 = 200
   // Use a qualified name to access the function.
   // from module1.
   let module2Function x =
      x + (module1.module1Function value2)
let result = module1.module1Function 25
printfn "%d" result
let result2 = module2.module2Function 25
printfn "%d" result2

编译并执行程序时,它会产生以下输出 -

125
325

F# - Namespaces

namespace旨在提供一种方法来保持一组名称与另一组名称分离。 在一个名称空间中声明的类名称不会与在另一个名称空间中声明的相同类名称冲突。

根据MSDN库, namespace允许您将代码附加到一组程序元素,从而将代码组织到相关功能的区域中。

声明命名空间

要在命名空间中组织代码,必须将命名空间声明为文件中的第一个声明。 然后整个文件的内容成为命名空间的一部分。

namespace [parent-namespaces.]identifier

以下示例说明了这一概念 -

例子 (Example)

namespace testing
module testmodule1 =
   let testFunction x y =
      printfn "Values from Module1: %A %A" x y
module testmodule2 =
   let testFunction x y =
      printfn "Values from Module2: %A %A" x y
module usermodule =
   do
      testmodule1.testFunction ( "one", "two", "three" ) 150
      testmodule2.testFunction (seq { for i in 1 .. 10 do yield i * i }) 200

编译并执行程序时,它会产生以下输出 -

Values from Module1: ("one", "two", "three") 150
Values from Module2: seq [1; 4; 9; 16; ...] 200
↑回到顶部↑
WIKI教程 @2018