最容易令初学者混乱的F#命令

开发 开发工具
F#是一门函数式编程语言,函数式编程语言的特点之一便是No Side Effect,Immutable。但是在很多场景下,Mutable可以给我带来很多便利,尤其是在结合命令式编程的场景中。

而对于F#的初学者,或是C#和F#混用的程序员来说,我认为F#中最容易令人混乱的命令是Reference Cells的取值操作了。下面便详细谈谈这么说的原因,及建议的应对办法。

F#是一门函数式编程语言,函数式编程语言的特点之一便是No Side Effect(无副作用),Immutable(不可变)。但是在很多场景下,Mutable(可变)可以给我带来很多便利,尤其是在结合命令式编程的场景中。因此F#提供了将某个“标识符”定义为“可变”的方式,主要有两种:使用mutable关键字或是Reference Cells。

在大部分情况下,我推荐(微软也这么推荐的)使用mutable关键字,因为这样标识符在使用上也已经和普通变量没有任何区别了。与之相对,使用Reference Cell进行读写操作都需要一些特殊的操作/指令。不过的确有一些场景必须使用Reference Cells,您可以关注MSDN上的说明。例如,在mutable的标识符在读取和赋值时,和普通的属性没有什么区别:

  1. let mutable a = 0 
  2. <- 1 // assign mutable variable  
  3. let request = WebRequest.Create("http://www.51cto.com")  
  4. request.ContentType <- "text/xml" // assign property 

但是对于Reference Cells来说,它的读取和写入就需要使用!与:=操作符了:

  1. let a = ref 0  
  2. :1 // assign value  
  3. printfn "%i" !a // retrieve value 

这个感叹号便是引起混乱的源泉,且看以下代码:

  1. let transfer (streamIn: Stream) (streamOut: Stream) buffer =  
  2.     let hasData = ref true  
  3.     while !hasData do  
  4.         let lengthRead = streamIn.Read(buffer, 0, buffer.Length)  
  5.         if lengthRead > 0 then   
  6.             streamOut.Write(buffer, 0, lengthRead)  
  7.         else  
  8.             hasData :false 

上面的代码定义了一个transfer函数,将一个数据流中的数据全部传输到另一个数据流中。在这里我们使用了命令式的编程方式,并使用一个名为hasData的Ref Cell来表明是否读完了数据。

不过,您看到while语句中的!hasData是什么感觉?至少对于我这样混写C#和F#的人来说,我的***反应是“嗯,取反?”,然后才是“哦,只是Ref Cells的取值操作”。对于其他一些场景下可能这点不会成为问题,但如果这个Ref Cell是个布尔值,然后又放在if或while的时候,混乱就这样开始了。因此,我目前可能会倾向于使用这样的方式:

  1. let transfer (streamIn: Stream) (streamOut: Stream) buffer =  
  2.     let hasData = ref true  
  3.     while hasData.Value do  
  4.         let lengthRead = streamIn.Read(buffer, 0, buffer.Length)  
  5.         if lengthRead > 0 then   
  6.             streamOut.Write(buffer, 0, lengthRead)  
  7.         else  
  8.             hasData :false 

在F#中,一个Ref Cell其实是一个Ref<'a>类型的对象,它有一个'a类型(泛型类型)的Value属性,可读写。因此,如果我们在上面的代码中直接使用Value属性,那么我想就不会让任何人混乱了。当然,***的办法可能还是写一些immutable的代码吧,例如:

  1. let rec transfer (streamIn: Stream) (streamOut: Stream) buffer =  
  2.     let lengthRead = streamIn.Read(buffer, 0, buffer.Length)  
  3.     if lengthRead > 0 then  
  4.         streamOut.Write(buffer, 0, lengthRead)  
  5.         transfer streamIn streamOut buffer 

对于F#来说,这样的(尾)递归和之前的实现方式可以说是完全等价的。

【编辑推荐】

  1. F#中的异步及并行模式:反馈进度的事件
  2. F#中的异步及并行模式:代理的高级使用
  3. F#简明教程三:F#语法精要
  4. F#简明教程二:F#类型系统和类型推断机制
  5. 详细介绍Visual Studio 2010F#使用
责任编辑:王晓东 来源: 老赵的博客
相关推荐

2017-03-10 17:00:28

编程语言

2024-03-11 18:17:18

Python字符串分隔符

2018-02-23 10:03:30

Linux命令mkdir

2015-03-13 11:10:03

PHP初学者PHP知识点PHP困惑

2018-05-14 08:53:51

Linux命令shuf

2018-04-08 14:47:06

Linux命令type

2018-02-05 08:45:00

RHELCentOS 7MariaDB

2018-02-10 07:36:20

Linux命令wc

2021-08-05 14:40:45

操作系统UNIXLINUX

2018-02-25 14:00:07

Linux命令whereis

2011-09-16 09:38:19

Emacs

2022-04-24 15:21:01

MarkdownHTML

2011-04-12 10:13:24

2018-03-12 19:43:48

Linux命令 ldd

2009-09-28 09:45:00

CCNA学习经验CCNA

2011-07-04 14:14:54

java

2022-10-10 15:28:45

负载均衡

2015-07-20 13:56:59

SDN

2020-09-08 19:03:41

Java代码初学者

2010-06-13 11:13:38

UML初学者指南
点赞
收藏

51CTO技术栈公众号