F#程序设计-函数式编程之值的可变性

来源:互联网 发布:网络作家收入排行榜 编辑:程序博客网 时间:2024/05/29 15:19

在.NET中,变量的存放可以放在两个地方堆栈(stack)和堆(heap)中,放在堆栈的变量类型被称为值类型,而放在堆中的被称作为引用类型。值类型就是在堆栈中拥有固定大小的空间,比如int、float类型,引用类型,在堆栈上仅仅存储一个指针,指向堆中内容的地址。尽管虽然指针具有固定的大小,比如4个字节或者8个字节,但是它指向堆中存放的内容可以是更大的。

 

在F#中,变量的存放机制跟.NET的其它语言是一样的,对于基本类型的值都是存放于堆栈中(string类型除外),其它的比如List、Array和Type类型都是存放于堆中。然后,由于F#是一种函数式编程语言,它的值一经定义,是不能改变的。要改变值,只能把值申明为改变类型(mutable)或者是引用类型(ref)。

 

一、Mutable

mutable关键字用于定义值的前面,表示这个值是可变的,改变一个可变值,不是像C#一样,使用"=",而是使用"<-",比如:

> let mutable message = "World";;
val mutable message : string = "World"
> printfn "Hello, %s" message;;
Hello, World
val it : unit = ()
> message <- "Universe";;
val it : unit = ()
> printfn "Hello, %s" message;;
Hello, Universe
val it : unit = ()

 

在这里,对于可变值的使用,还是有一些的限制。这主要是CLR处于安全方面的考虑,这样可以防止编写代码的时候使用随处使用可变值,在F#中,使用可变值的限制有:

1、可变值不能从函数中返回

2、可变值不能被内嵌函数使用

 

比如,下面的示例,在函数invalidUseOfMutable()中定义了一个可变值x和一个内嵌函数incrementX(),x在函数incrementX()中使用将是非法的,将会导致异常:

> let invalidUseOfMutable() =
- let mutable x = 0
- let incrementX() = x <- x + 1
- incrementX()
- x;;
let incrementX() = x <- x + 1
-----------------------^^^^^^^^^^^
stdin(16,24): error FS0407: The mutable variable 'x' is used in an invalid way.
Mutable variables may not be captured by closures. Consider eliminating this use
of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'.

 

二、Reference

为了解决能把申明的可变值用于内嵌的函数中,可以利用ref关键字把可变值的存放方式由堆栈改为堆。ref关键字的作用就是允许把可变值存放于堆中,是你可以绕过在堆栈中使用可变值的限制。为了获取ref类型的值,可以使用操作符!,同时利用操作符:=来设置ref类型的值,ref关键之不仅仅可用于一个类型中,还可以用在函数,就像C#的方法返回一个引用一样,它仅仅返回的是函数的副本。下面的两个示例分别说明了利用ref把值类型存放在堆中以及返回一个函数的引用。

1)改变值类型的存放

> let x = ref 0;;

val x : int ref = {contents = 0;}

> !x;;
val it : int = 0
> x := 1;;
val it : unit = ()
> !x;;
val it : int = 1

 

2)返回函数的副本

> let funcCopy = ref (fun x y -> x + y);;

val funcCopy : (int -> int -> int) ref = {contents = <fun:funcCopy@13>;}

> !funcCopy 1 2;;
val it : int = 3

 

三、Mutable Records

可变性不仅仅能用于改变单一的值,记录中的字段也可以标识为可变,这就允许你在命令式编程风格中使用记录了。为了标记一个字段为可变,只需要简单的在字段前面使用mutable关键字,比如下面的示例:

> open System
- type MutableCar = { Make : string; Model : string; mutable Miles :
- let driveForASeason car =
-     let rng = new Random()
-     car.Miles <- car.Miles + rng.Next() % 10000;;

type MutableCar =
  {Make: string;
   Model: string;
   mutable Miles: int;}
val driveForASeason : MutableCar -> unit

> let kitt = { Make = "Pontiac"; Model = "Trans Am"; Miles = 0 }
- driveForASeason kitt
- driveForASeason kitt;;

val kitt : MutableCar = {Make = "Pontiac";
                         Model = "Trans Am";
                         Miles = 16168;}

 

 

原创粉丝点击