NETFramework中的函数式编程技术_第1页
NETFramework中的函数式编程技术_第2页
NETFramework中的函数式编程技术_第3页
NETFramework中的函数式编程技术_第4页
NETFramework中的函数式编程技术_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、.NET Framework 中的函数式编程技术 懒人ABC发表于2020-09-07 00:20原文链接阅读:2评论:2 F#入门使用.NET Framework中的函数式编程技术Ted Neward本文讨论:安装F#F#语言根底.NET互操作性异步F#本文使用了以下技术:.NET Framework,F#目录为什么要使用F#?安装F#您好,F#Let表达式关键字For管道F#也可以处理对象异步F#与F#合作作为Microsoft.NET Framework家族的新成员,F#提供类型平安、性能以及类似脚本语言的工作才能,所有这些都是.NET环境的一部分。此函数式语言由Microsoft研究院

2、的Don Syme创造,作为CLR的OCaml语法兼容变体,但F#已经迅速地从科研转为投入实际应用。随着函数式编程的概念通过.NET泛型和LINQ等技术越来越多地渗入主流语言例如C#和Visual Basic,F#在.NET社区里的知名程度也不断进步-因此,2007年11月Microsoft宣布将F#确定为受支持的.NET编程语言。多年来,大家一直认为函数式语言领域ML、Haskell等更适宜用于学术研究,而不适用于专业开发。但这并不代表这些语言没有过人之处。事实上,.NET的一些重要的功能增强例如泛型、LINQ、PLINQ和Futures都是将一些函数式编程概念全新应用到语言所致。以往对这些

3、语言的关注程度不高主要是因为它们的目的平台与专为Windows编写程序的开发人员关系不大、不能与底层平台很好集成,或者不支持关系数据库访问、XML解析和进程外通信机制等主要功能。但是,CLR及其"多种语言,单一平台"的方法将使此类语言在Windows开发中的应用越来越广泛。并且顺理成章地引起在一线工作的程序员们的注意。F#即是这样一门语言。在本文中,我将为您介绍一些F#的根本概念和优点。然后,为了帮助您初步理解F#,我将详细介绍它的安装过程并编写几个简单的小程序。为什么要使用F#?对于小部分.NET程序员来说,学习一门.NET Framework函数化语言无疑将使自己在编写

4、功能强大软件方面前进一大步。而对其他程序员来说,学习F#的理由就因人而异了。F#能为开发人员提供哪些好处?随着多核CPU的普及,平安并发程序已成为过去三年来的关注焦点。函数式语言倡导一种固定不变的数据构造,可在线程和机器之间传递,而无需担忧线程平安或原子访问,开发人员可以利用这一特点支持并发操作。函数式语言还可更轻松地编写更支持并发特性的库,如稍后将在本文中介绍的F#异步工作流。尽管对于专攻面向对象开发的程序员而言,可能对这种语言感觉不是这么强烈,但在很多情况下,函数式程序确实可以简化某些应用程序的编写和维护。例如,编写一个将XML文档转换成其他格式数据的程序。虽然完全可以通过编写一个C#程序

5、,让它解析整个XML文档并应用各种if语句确定在文档中的不同位置采取何种措施,但实际上更好的方法是编写可扩展款式表语言转换XSLT程序。当然,XSLT肯定包含大量的内置函数机制,如同SQL一样。F#强烈建议不要使用空值null,而是提倡使用固定不变的数据构造。这些特性可以减少需要编写的特例代码量,从而有助于降低编程出错的频率。使用F#编写的程序还更加简洁。您可以实在地从两方面减少键入的内容:击键次数更少并且必需要向编译器通告变量类型、参数或返回类型的位置点也更少。这意味着需要维护的代码将大大减少。F#具有与C#相似的性能特点。但是,与简洁程度相似的语言特别是那些动态和脚本语言相比,它的性能特点

6、要好得多。并且,F#也包含通过编写程序段并交互式执行查看数据的工具,这一点与许多动态语言类似。安装F#F#可从research.microsoft /fsharp/fsharp.aspx免费下载,它不仅会安装所有命令行工具,而且还会安装Visual Studio扩展软件包,该软件包提供彩色语法突出显示、工程和文件模板包括作为入门指南的详细F#例如代码以及IntelliSense支持。同时它还提供可在Visual Studio内部运行的F#交互式shell,它使开发人员可以从源文件窗口中提取表达式、将表达式粘贴到交互式shell窗口,并立即得到代码段的结果-在类似增强的Immediate窗口中显

7、示结果。在我撰写本专栏时,F#在Visual Studio内作为外部工具运行,所以它缺少某些开发人员可以从C#或Visual Basic中获得的无缝集成的才能。此外,F#还缺少ASP.NET页面设计器支持。这并不是说F#不能在ASP.NET中使用,完全不是。这仅表示Visual Studio并没有为F#提供类似C#和Visual Basic的那种现成拖放式开发体验。尽管如此,当前版本的F#还是可以在可以使用其他.NET兼容语言的任何位置使用。在接下来的几页中,您将看到一些例如。您好,F#介绍任何语言的特有方式就是通过那几乎成为标准的"Hello,World"程序。F#也不例

8、外:复制代码printf"Hello,world!"虽然不能引起您太大的兴趣,但这个很小的例子显示出F#属于不需要显式入口点C#、Visual Basic和C+/CLI都需要显式入口点的语言;该语言假设程序的第一行即为入口点并将从此处开场执行。要运行此程序,刚入门的F#开发人员有两个选择:编译或解释。在F#解释程序中运行此程序fsi.exe很简单。只需从命令行启动fsi.exe并在出现的提示中输入上面一行内容即可,如图1所示。图1在F#解释程序中运行'Hello,World'单击该图像获得较大视图请注意,在shell中,每条语句必须以两个分号结尾。这是交互形

9、式的特殊要求,编译过的F#程序并不需要使用这种方式。要将此例如作为标准的.NET可执行程序运行,请正常启动Visual Studio并创立一个新的F#工程可以在"其他工程类型"中找到这个工程。最初,F#工程包含一个F#源文件,称为file1.fs。翻开此文件将发现大量例如F#代码集合。阅读一下这些内容即可大概理解F#的语法构造。然后将整个文件交换成前面所显示的"Hello,world!"代码。运行此应用程序,毫无疑问,在控制台应用程序窗口中将出现"Hello,world!"。假设更喜欢命令行方式,可以使用F#安装目录中bin子目录里的

10、fsc.exe工具编译这段代码。请注意:fsc.exe与大多数命令行编译器的工作方式相似,它从命令行获取源代码并生成可执行程序作为结果。大多数命令行开关都有相关文档,假设曾经使用过csc.exe或cl.exe编译器,那您可能已经熟悉其中许多开关。不过请注意,F#目前对MSBuild的支持尚不完美;在当前安装版本撰写本文时为中不能直接支持由MSBuild驱动的编译。假设希望"Hello,world!"中更具图形化特点,F#可以通过CLR平台包括Windows Forms库轻松地提供完好的逼真度和互操作性。尝试以下代码:复制代码System.Windows.For

11、ms.MessageBox.Show"Hello World"利用.NET Framework类库和F#库的才能使得F#语言不仅对那些早已使用如OCaml或Haskell之类函数式语言进展数学和科学计算的社区极具吸引力,而且受到全世界现有.NET开发人员的青睐。Let表达式让我们看看比传统"Hello,world!"更复杂一些的F#代码例如。请看以下代码:复制代码let results=for iin 0.100-i,i*iprintfn"results=%A"results在该F#语法中,let表达式是令人产生好奇的元素。它是整个语

12、言中最重要的表达式。更正式地说,let可以为标识符赋值。用Visual Basic和C#开发人员的行话来说,"它可以定义一个变量"。但这并不确切。在F#中,标识符包含两个要素。首先,标识符一旦定义就不能再更改。这即是F#帮助程序员创立并发平安程序的方法,因为它不提倡可变的状态。第二,标识符不仅可以是基元类型或对象类型如C#和Visual Basic中所使用的类型,而且还可以是函数类型,这一点与LINQ相似。同时还请注意,标识符从不显式定义为类型。例如,从不定义结果标识符;它将从后面表达式的右侧进展推断。这称为类型推断,并且它代表编译器分析代码、确定返回值和自动插入返回值的才

13、能这与新的C#推断类型表达式通过变量关键字进展推断的方法类似。Let表达式不仅可以处理数据,还可以使用它来定义函数,F#将函数看作第一级概念。下面的例如定义了一个加法函数,它使用两个参数a和b:复制代码let add ab=a+b完全按照您预期的方式工作:将a与b相加,并将结果显式返回给调用者。这意味着从技术上讲,F#中的每个函数都将返回一个值,即使返回的不一定是值,也会返回一个特殊的名称unit。这将在F#代码中产生一些有趣的暗示,特别是与.NET Framework类库相交的部分。但目前,C#和Visual Basic开发人员可以把unit大致看作是与void一样的类型。有时函数应该忽略传

14、递给它的参数。要在F#中达此目的,仅需使用下划线作为该参数的占位符即可:复制代码let return10 _=add 55/12 is effectively ignored,and ten is set to the resulting/value of add 55 let ten=return10 12 printf"ten=%dn"ten与许多函数式语言类似,F#允许根据其调用进展currying可以仅部分定义函数的应用,以便提供其余的参数:复制代码let add5 a=add a5在某种程度上,这与创立一个承受不同的参数集并调用其他方法的重载方法相似:复制代码pu

15、blic class Adderspublic static int addint a,int breturn a+b;public static int add5int areturn adda,5;但两者还是有一点细微的差异。请注意,在F#版本中,没有显式定义类型。这表示编译器将采用自己的类型推断方法确定add5的参数是否是与加上整数5兼容的类型,并确定是按照这种方式编译,还是将其标记为错误。事实上,F#语言主要使用隐式类型参数化即使用泛型。在Visual Studio中,将指针停放在前面所显示的ten的定义上时,将说明其类型声明为:复制代码val ten:'a-int在F#中,这

16、表示ten是一个值,一个获取任意类型的参数并产生整数结果的函数。这种记号语法大致等同于C#中的T语法,所以对C#函数最贴切的说法是:ten是类型参数化方法的委托实例,您想要忽略其类型但在C#规那么下不可忽略:复制代码delegate int Transformer TT ignored;public class Apppublic static int return10object ignoredreturn 5+5;static void MainTransformer object ten=return10;System.Console.WriteLine"ten=0"

17、,return100;关键字For如今让我们看一下第一个例如中的for关键字:复制代码#light let results=for iin 0.100-i,i*iprintfn"results=%A"results先看代码的顶部,注意#light语法。这是为非OCaml程序员开场使用F#而做的让步,放宽某些OCaml语言的语法要求,并使用大量空白定义代码块。虽然不一定必要,但对于那些本来使用C#或Visual Basic的普通开发人员来说,它确实使语法更易于解析,因此它常出如今F#例如和公开的代码段中,并已成为事实上的F#编程标准。将来版本的F#可能会将#light确定为默

18、认语法,代替其他类似方法。那个看似简单的for循环实际上并不简单。正式地说,这是生成列表,即将生成列表型结果的代码块的另一种说法。列表是函数式语言中经常出现的一种原语构造,在这方面它与数组有许多相似之处。但是,列表不允许基于位置的访问如C#中传统的ai语法。列表可在函数式编程的不同位置出现,大多数情况中,可以将其看作是F#中与.NET Framework中的List T类似,但提供一些增强功能的同等项。列表通常使用一些特殊类型,本例中标识符结果是聚合列表,特别地是F#将此聚合类型标识为类型int*int。假设将此聚合列表看作是SQL中SELECT语句返回的一对列,它的含义就清楚多了。因此,例如

19、本质上是创立一个包含100个工程的整数对列表。通常,在函数式语言中,函数定义可以在出现代码的任何位置使用。因此,假设希望扩展前面的例如,可以编写以下代码:复制代码let compute2 x=x,x*xlet compute3 x=x,x*x,x*x*xlet results2=for iin 0.100-compute2 ilet results3=for iin 0.100-compute3 i遍历列表或数组或其他一些重复构造是函数式语言中很常见的任务,它已成为根本方法调用:List.iter。它仅简单地对列表中的每个元素调用一个函数。其他类似的库函数还可提供一些非常有用的功能。例如,Li

20、st.map将函数作为参数,并将该函数应用于列表中的每个元素,并返回该过程产生的新列表。管道让我们讨论F#中另一个构造-管道操作符,它通过类似命令shell如Windows PowerShell管道的通道获取函数的结果,并将结果用作后一个函数的输入。我们来看图2中显示的F#代码段。该代码段使用System.Net命名空间连接 效劳器,获取相应的HTML并分析结果。Figure 2检索和分析HTML复制代码/Get the contents of the URL via aweb request let url:string=let req=System.Net.WebRequest.Creat

21、eurllet resp=req.GetResponselet stream=resp.GetResponseStreamlet reader=new System.IO.StreamReaderstreamlet html=reader.ReadToEndresp.Closehtml let getWords s=String.split'';'n';'t';'';'';'='s let getStats site=let url="+site let html= url let wor

22、ds=html|getWords let hrefs=html|getWords|List.filterfun s-s="href"site,html.Length,words.Length,hrefs.Length请注意getStats定义中的words标识符。它获取从URL返回的html值,并对其应用getWords函数。我还可以编写定义读取:复制代码let words=getWords html两者等同。但是hrefs标识符显示了管道操作符的威力,通过管道操作符可以将任意多个应用程序连接起来。此处我获取words的结果列表,并将其通过管道传递给List.filter函

23、数,该函数使用匿名函数查找单词href,并在表达式为true时将其返回。并且,最重要的是,getStats调用的结果将是另一个聚合string*int*int*int。要使用C#编写,需要的远远不止15行代码。图2中的例如还显示出更多F#与.NET Framework的兼容性,以下代码也表现出这一特性:复制代码open System.Collections.Generic let capitals=Dictionary string,stringcapitals."Great Britain"-"London"capitals."France&

24、quot;-"Paris"capitals.ContainsKey"France"确实,这个例如除了练习Dictionary K,V类型外没有什么其他内容,但它显示出在F#中如何指定泛型使用与C#一样的尖括号、如何在F#中使用索引同样与C#一样使用方括号,以及如何执行.NET方法使用与C#中同样的点和圆括号。事实上,这里仅有的新内容是使用左箭头操作符为可变值赋值。这一点是必需的,因为F#与大多数函数式语言一样,保存等号用于比较,以便保持数学符号含义:假设x=y,那么x与y的值相等,而不是将y的值赋给x。真正的数学家们早已对普遍存在或设想过的x=x+1提出

25、异议,甚至偷笑不已。F#也可以处理对象当然,并不是所有开场使用.NET的开发人员都愿意立即承受函数式的概念。事实上,大多数从C#或Visual Basic转向F#的开发人员都需要知道他们在使用这一新语言时可以保存原有的习惯。在某种程度上,这是完全可行的。例如,请看图3顶部所示的二维向量的类定义。其中就有一些有趣的概念。首先,请注意其中没有显式构造函数体;第一行中的参数说明用于构造Vector2D实例的参数本质上就是构造函数。因此长度标识符,以及dx和dy标识符将成为Vector2D类型的私有元素,而member关键字那么说明可以通过标准.NET属性访问获取的Vector2D外部可用成员。本质上

26、,这段F#代码声明了您可在图3底部看到的内容由Reflector报告。Figure 3F#和C#中的矢量变体复制代码VECTOR2D IN F#type Vector2Ddx:float,dy:float=let length=sqrtdx*dx+dy*dymember obj.Length=length member obj.DX=dx member obj.DY=dy member obj.Movedx2,dy2=Vector2Ddx+dx2,dy+dy2VECTOR2D IN C#REFLECTORSerializable,CompilationMappingSourceLevelCon

27、struct.ObjectTypepublic class Vector2D/Fields internal double _dx48;internal double _dy48;internal double _length49;/Methods public Vector2Ddouble dx,double dyHello.Vector2Dthis=this;this._dx48=dx;this._dy48=dy;double d=this._dx48*this._dx48+this._dy48*this._dy48;this._length49=Math.Sqrtd;public Hel

28、lo.Vector2D Movedouble dx2,double dy2return new Hello.Vector2Dthis._dx48+dx2,this._dy48+dy2;/Properties public double DXgetreturn this._dx48;public double DYgetreturn this._dy48;public double Lengthgetreturn this._length49;请记住,F#与大多数函数式语言相似,提倡使用不变的值和状态。当查看图3中的代码时,这一点尤为明显,因为所有属性都为只读属性,并且Move成员不会修改现有的

29、Vector2D,而是从当前Vector2D创立新的副本,并在返回副本之前对其应用修改的值。还请注意,F#版本不仅具备整体线程平安性,而且完全可以通过传统的C#或Visual Basic代码进展访问。这为F#入门提供了一种简便方法:使用它定义想要或者需要线程平安和固定不变的业务对象或其他类型。虽然完全可以在F#中创立提供常用可变操作组设置属性及类似操作的类型,但需要更多的工作,而且需要使用mutable关键字才可完成。在当今并发问题成为日常工作主旋律的世界中,这正如许多人所要求的一样-默认固定不变,必需或想要时可变。在F#中创立类型很有趣,但还是可以用F#去做那些传统C#或Visual Bas

30、ic代码可以做到的工作,如创立简单的Windows窗体应用程序并从用户处搜集输入,如图4所示。Figure 4使用F#编写Windows窗体复制代码#light open System open System.IO open System.Windows.Forms open Printf let form=new FormText="My First F#Form",Visible=truelet menu=form.Menu-new MainMenulet mnuFile=form.Menu.MenuItems.Add"&File"let f

31、ilter="txt files*.txt|*.txt|All files*.*|*.*"let mnuiOpen=new MenuItem"&Open.",new EventHandlerfun _-let dialog=new OpenFileDialogInitialDirectory="c:",Filter=filter;FilterIndex=2,RestoreDirectory=trueif dialog.ShowDialog=DialogResult.OK then match dialog.OpenFilewi

32、th|null-printf"Could not read the file.n"|s-let r=new StreamReadersprintf"First line is:%s!n"r.ReadLine;s.Close;,Shortcut.CtrlOmnuFile.MenuItems.AddmnuiOpenSTAThreaddo Application.Runform任何熟悉Windows窗体的开发人员都可以立即明白这些代码的含义:创立一个简单的窗体、填充一些属性、填入一个事件处理程序,并告诉应用程序开场运行,直到用户单击右上角的"关闭&q

33、uot;按钮。由于标准元素与.NET应用程序一样,所以只需重点关注F#语法即可。Open语句的操作与C#中using语句的作用大致一样,本质上都是翻开.NET命名空间以便在没有正式限制符的情况下使用。Printf命名空间是F#原有的、技术上与OCaml模块具有一样名称的端口。F#不仅具备完好的.NET Framework类库,而且还有最简洁的OCaml库端口,这使得熟悉该语言的程序员可以象使用.NET Framework一样对其运用自如。致好奇心强的程序员:Printf位于FSharp.Core.dll程序集中。您完全可以根据个人偏好随时使用System.Console.WriteLine。窗

34、体标识符的创立利用了F#命名参数,它等同于实例化对象,然后调用一系列属性集来为这些属性填充值。我在下面的几行中使用一样的方法创立对话框标识符。mnuiOpen标识符的定义包含令人感兴趣的构造,该构造对于熟悉.NET Framework 2.0匿名委托或.NET Framework 3.5中lambda表达式的开发人员来说并不陌生。构造与Open MenuItem关联的EventHandler时,您可以看到使用以下语法定义的匿名函数:复制代码fun _-.类似于匿名委托,这段代码创立了一个将会在选中菜单项时调用的函数,但语法略有技巧性。MenuItem定义中对EventHandler的定义是忽略

35、传递给它的两个参数的匿名函数,这两个参数巧妙地对应标准EventHandler委托类型中的发送方和事件参数。该函数规定显示新的OpenFileDialog并在单击"确定"时检查结果.如下所示:复制代码if dialog.ShowDialog=DialogResult.OK then match dialog.OpenFilewith|null-printf"Could not read the file.n"|s-let r=new StreamReadersin printf"First line is:%s!n"r.ReadLin

36、e;s.Close;将使用形式匹配检查结果,该方法是函数化语言世界中一项强大的功能。形式匹配外表上与C#中的switch/case在某些地方存在相似之处,但实际上它名副其实地完成形式匹配工作:它将值与各种不同的形式进展比较这些形式不需要都是常量值,并执行匹配的代码块。因此,以此处所示的匹配块为例,OpenFile的结果可以匹配两种可能的值:null表示无法翻开任何文件,或者分配任何非null值的s,该值将随后用作StreamReader的构造函数来翻开并读取给定文本文件的第一行。形式匹配是大多数函数式语言的重要部分,对它做些研究是完全值得的。它的一个最常见的用处是与可辨识结合discrimin

37、ated union类型C#或Visual Basic中枚举类型的不确切说法配合使用:复制代码/Declaration of the'Expr'type type Expr=|Binary of string*Expr*Expr|Variable of string|Constant of int/Create avalue'v'representing'x+10'let v=Binary"+",Variable"x",Constant 10函数式语言中常用它来创立域特定语言的核心表示,开发人员可以使用它来

38、编写更为复杂和强大的构造。例如,不难想象扩展此语法以创立完全计算式语言,并可简单地通过为Expr类型添加新元素而进一步扩展该语言。这里需要注意的是:使用*字符的语法并不表示使用乘法,它是函数式语言中用于指示某类型中包含多个部分的标准方式。事实上,函数式语言已经非常普遍地应用于编写面向语言的编程工具如解释器和编译器,并且Expr类型最终将成为语言表达式类型的完好集合。F#通过其内置的两个工具:fslex和fsyacc专为获得传统语言输入-lex和yacc文件-并将其编译成F#代码以便简化操作而设计使这一切变得更为简单。假设对此感兴趣,可以下载F#安装程序深化研究;特别是标准F#发行包中的Pars

39、ing例如将提供非常好的入门根底构造。可辨识结合只是形式匹配的优势之一,另一项优势是表达式的执行,如图5所示。位于eval定义中的rec是必需的,它告诉F#编译器在定义主体内迭代调用eval。假设没有它,F#将期望出现一个名为eval的本地嵌套函数。实际使用时,我使用函数getVarValue为变量返回一些预定义的值,getVarValue将检查Dictionary,查找变量创立时确定的返回值。Figure 5表达式执行复制代码let getVarValue v=match vwith|"x"-25|"y"-12|_-0 let rec eval x=m

40、atch xwith|Binaryop,l,r-letlv,rv=eval l,eval rin ifop="+"then lv+rv elifop="-"then lv-rv else failwith"E_UNSUPPORTED"|Variablevar-getVarValue var|Constantn-n do printf"Results=%dn"eval v当调用eval时,它将得到值v并发现该值是一个Binary值。这与第一个子表达式匹配,该表达式随后把值lv,rv绑定到刚检查的Binary值左右两侧

41、的计算结果。未命名的值lv,rv是一个聚合本质上是代表多个部分的单个值,这一点与关系集或C构造相似。当首次调用eval l时,来自Binary实例的l恰好是Variable类型,因此对eval的递归调用匹配该形式匹配块的分支。随后将调用getVarValue,它会返回硬编码25,该值最终将绑定到值lv。对于包含值10的常量r来说顺序一样,因此它将绑定到rv。然后执行代码块的剩余部分if/else-if/else块,熟悉C#、Visual Basic或C+的开发人员可以很容易地读懂该代码块的含义。这里需要再次强调的是:每个表达式都将返回一个值,甚至在形式匹配块内部也一样。在本例中,返回值是一个整

42、型值,该值可能是运算得到的值、从变量中检索到的值或者是常量本身。这一点似乎更容易让习惯于面向对象或过程化编程的开发人员产生微词,因为在C#、Visual Basic或C+中,返回值是可选的,并且甚至在指定返回值的情况下仍可以忽略返回值。在类似F#的函数式语言中,要忽略返回值需要显式编码表达方式。假设出现这种情况,程序员可以将结果传给名为ignore的函数,由它完成适当的操作。异步F#目前为止,我对F#语法的介绍采用以下两种方式中的一种:或者使用相对简单的函数式构造,或者使其看起来比较初级且简洁,象是传统面向对象、.NET兼容语言C#、Visual Basic或C+/CLI的变体。这种介绍很难推

43、动在企业内采用F#。但是请看一以以下图6。它可与前面两种形式截然不同。除了多处出现!字符并使用async修饰符外,这是一段看起来相比照较直观的代码:加载源图像映像、提取其数据、将数据传递到独立的函数进展加工旋转、拉伸或其他操作,并将数据写回输出文件。Figure 6处理图像复制代码let TransformImage pixels i=/Some kind of graphic manipulation of images let ProcessImagei=asyncuse inStream=File.OpenReadsprintf"source%d.jpg"ilet!p

44、ixels=inStream.ReadAsync1024*1024let pixels'=TransformImagepixels,iuse outStream=File.OpenWritesprintf"result%d.jpg"ido!outStream.WriteAsyncpixels'do Console.WriteLine"done!"let ProcessImages=Async.RunAsync.Parallelfor iin 1.numImages-ProcessImagei较不明显的是使用async修饰符使这段代码进入F

45、#所称的异步工作流与Windows Workflow Foundation无关中,这意味着这些加载/处理/保存步骤的每一步都在.NET线程池的平行线程中执行。为了使其更简单,看一以以下图7中的代码。这种特殊的顺序以相对简单且易于理解的方式显示出异步工作流。不用深究细节,我们就可以看出evals是一组待执行的函数,通过Async.Parallel调用使其中每个函数都在线程池中排队等待执行。当执行时,可以看出实际上evals中的函数与awr中的函数在不同的线程中尽管由于.NET系统线程池的特性,部分或全部evals函数有可能在一样的线程中执行。Figure 7异步执行函数复制代码#light open System.Threading let printWithThread str=printfn"ThreadId=%d%s"Thread.CurrentThread.ManagedThreadId str let evals=let z=4.0asyncdo printWithThread"Computing z*zn"return z*z;asyncdo printWithThread"Compu

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论