F# 学习之路(3) 如何组织程序(上)

     一、模块(module)

     在F#中模块通常用来封装一组变量和对某种数据结构的操作,例如F#中的List,Map,Seq等模块,这些模块用来针对List列表、Map键值对、Seq序列等数据结构进行操作。

     1、定义一个模块

     在F#中定义一个模块,使用关键字module。如果你在一个源代码文件中(.fs文件)没有显式定义一个模块,F#默认你定义了一个以源文件名大写的模块,其作用域为从文件开头至文件结束,上一篇已经有介绍。  

 

Code

 

 

     文件1定义了一个模块A,定义了一个列表l,文件2 定义了一个模块B,使用print_any函数打印模块B的l列表。

     F#中列表使用方括号,元素之间使用分号分隔,并且一旦定义就不可改变 

     F#中模块有两种,一种是顶级模块,另一种是子模块。所谓顶级模块指的是自身没有嵌套在别的模块中,而一个嵌套在别的模块中的则称为子模块。这两种模块定义的方式也不一样,子模块将在后面的命名空间中介绍。 

     顶级模块定义的方式,有两种,除了前面代码一中定义的方法,还有一种

     

     module A.B.C

     

     这种使用点号分隔的形式,它表示在当前名称空间(namespace)下定义一个名称空间A.B,并在A.B下定义模块C 

     顶级模块的定义要求除名称空间的定义或指令外的第一句。

     在F#中模块等同于c#中静态密封类。你可以使用reflector查看,在模块中定义的类型、值、函数都成为这个静态类中的静态成员 

 

 

Code

 

 

     上面的代码文件3中定义一个带有名称空间的模块,在代码文件4中,调用了3中模块定义的length函数及扩展字符串一个属性成成员Link

     这里使用了open关键字,明确要求编译器在Util.StringExtensions下查找值和类型、函数等。这样我们就可以不使用完全限定名了。在F#中open不仅可以打开名称空间,也可以打开模块。前面说过模块等同于静态类(我没有发现F#中有明确定义静态类的方法),所以模块用来放置OO中让人垢病的静态工具类,是一个理想的场所。  

     2、模块的访问限定

     模块可以使用关键字public ,internal,private来限定访问范围(模块是静态类,你可以使用原有的c#概念来推测它的行为)

     public表示公有,意味着任何程序代码都可以访问,internal表示程序集范围中都可以访问,private表示只有模块中代码可以访问 

 

 

Code

 

 

     上面代码使用public来限定范围,表示可以不受限制的访问,注意public的位置。

     在F#语言中,public,internal,private这三个关键字不仅可以用在module上来限定模块的访问范围,还可以用在let绑定上,和type上,来指定值和类型的访问边界。

     

     3、模块间引用顺序

     如果你按照这篇博客的顺序依次在VS中添加文件,你会发现一个问题,我来描述一下:

     当你添加文件一、文件二,编译执行,一切正常。

     当你添加文件三、文件四,编译执行,发现了什么问题?你的控制台上是不是一片空白,不要害怕,按一下回车吧。他打印出来了。

     当你添加文件五,编译执行,发现了什么事情?为什么控制台一闪而过,消失了。 

     

     为了继续下面的问题。我将改写文件4,来演示前面所述的模块间的访问限定。  

 

Code

 

 

     你发现了什么问题,Util. NumberExtensions没有定义,我明明定义了呀。

     好吧,我不在继续下去了,我来回答你的这些回答吧。

     回答上面的问题,需要我们思考一个问题,就是F#这种没有明确入口点的语言,依据什么来确定编译和执行的顺序。

     在C#语言中我们的控制台和window程序需要明确的告诉编译器程序入口点,需要你定义一个main方法(当然你也可以定义多个main方法,那样的话,你需要告诉编译器你使用哪 一个main方法作为入口点)。 

     F#在编译时,依赖于你传入给编译器源代码文件的顺序。在VS的msbuild文件下记载了你加入文件的顺序,vs在编译时,会使用下面的命令来编译程序。

     

     fsc.exe "a.fs" " b.fs" "stringExtension.fs" " test.fs" " numberExtensions.fs" –o "项目名.exe"

 

     这在编译test.fs时,会因为找不到Util. NumberExtensions的定义而出错。但可以通过明确告诉编译器查找的路径,来避免编译出错。  

 

 

Code

 

  

     通过#I指令来告诉fsc编译器查找路径。上面代码使用了工程项目的路径

     通过#r指令来告诉fsc编译器应在那个程序集中查找名称空间和模块  

 

     那么F#如何确定程序入口点了,答案是:你传递给编译器fsc命令的最后一个源代码文件,在本例中就是numberExtensions.fs文件,因为numberExtensions.fs文件中只定义了常量和类型定义、函数,F#编译器会生成一个空的方法。F#通过这种规定,帮助我们生成了一个_main方法,来做为程序入口点。另外,需要指出的是F#中的模块是按需加载的,这个本质上跟c#是类似的,也就是说,在入口点中未引用的模块是不会执行。这也是在加载文件三、四执行时,会执行文件三中的System.Console.ReadKey(true),而在加载文件五时,前面的所有代码都不会执行的原因。 

     刚才我们讲到F#编译器是按照传递给它文件的顺序来进行编译的,那我们只要将我们依赖的源代码文件改变顺序,同样可以解决上面的问题。 不过目前版本的F#项目服务还需要你手工完成排序操作。高兴的是下一个CTP版本为我们提供了不错的帮助。为了你能够在CTP版本未发布之前也能够使用,我将介绍如何手工改变源代码文件顺序的办法。

     (1)首先鼠标右击项目,选择卸载项目(unload project)

     (2)然后鼠标右击项目,选择编辑项目(Edit …)

     (3)在打开的项目文件中,找到下面的位置 

 

Code

 

     将numberExtensions.fs放到test.fs前面,保存。

     (4)右击项目,然后 加载项目(reload project)

 

     CTP版本中的项目服务已经可以简单的通过移上移下的方式来改变顺序了。 

 

    

      上面的代码虽然能够正常找到了Util. NumberExtensions模块了,可又出现了length函数冲突,如何解决了?我将在下篇博客中介绍命名空间和名称空间冲突的解决办法。

     F#学习之路(3)如何组织程序(下)

Tag标签: F#
posted @ 2008-08-14 15:22 lvxuwen 阅读(1878) 评论(14)  编辑 收藏 网摘 所属分类: F#

  回复  引用    
#1楼2008-08-14 16:10 | colder[未注册用户]
给源代码文件改名 换换大小写就行
改过名字的文件会自动跑到最下面

我记得在hubfs上有个从C#转到F#的人问到代码排序问题 并且粘出了源代码 是用很OO的方式写的 所有函数都包含在一个类里面
结果回答的人把上百行代码精简成了10行 一个文件就装下了

我想说的是 用F#规划代码 应该不是以类为单位分文件 而是以重用性来分文件
"底"层核心部分常用的小函数都放在第一个文件里 其他依此类推

再多的话干脆分为多个DLL好了

不过我现在最困惑的是强大的类型推导会让VS显得很卡 在C#里有时候要编辑才会提示的错误 在F#里会立即收到错误
不知道CTP版要怎么解决这个问题

  回复  引用  查看    
#2楼[楼主]2008-08-14 16:29 | lvxuwen      
@colder

你讲的通过改变文件名的方式来排序,我想不大妥当,很显然源代码文件名是经过认真思索后定义的。

F#是多范式的编程范式语言,OO与FP都有适用的范围。重用性上我不认为那个比那个强。OO重用的是行为,FP重用的同样是行为。只不过FP通过函数的组合使代码更精简。但OO和FP可以很好的融合。

这是一个用F#编写的哈夫曼编码解码的文章,比较经典

http://blogs.msdn.com/lukeh/archive/2008/05/05/huffman-coding-with-f.aspx" target="_new">http://blogs.msdn.com/lukeh/archive/2008/05/05/huffman-coding-with-f.aspx




  回复  引用  查看    
#3楼2008-08-14 23:17 | Anders Cui      
不错,挺详细的
我这两天也在看这方面的东东

  回复  引用  查看    
#4楼[楼主]2008-08-15 09:07 | lvxuwen      
@Anders Cui

我希望通过写这个系列博客,让大家能够快速上手,选择有利于实际编程经验开发人员的方式,所以写这个系列不是从最基本的数据类型讲起,而是重点讲一些F#的重要概念。

  回复  引用  查看    
#5楼2008-08-15 10:19 | Anders Cui      
@lvxuwen
我希望能写一个系列
能够包含F#的一些基础知识,呵呵
组织方式跟你有点不同
多多交流 :)

  回复  引用  查看    
#6楼[楼主]2008-08-15 12:29 | lvxuwen      
@Anders Cui
欢迎呀,呵呵,园子里的朋友学习的多了,交流的深度广度都会更好
互相学习:)

  回复  引用  查看    
#7楼2008-08-15 15:27 | 有容乃大      
为何要用F#,博主能否给个答案?
  回复  引用  查看    
#8楼[楼主]2008-08-15 16:05 | lvxuwen      
@有容乃大

我希望我的回答,不会让大家陷入语言之争。

1、C#开发了F#,所以F#能做的c#一定能做

2、上述第一点,是针对C#高手来说的,正如我认同C++,Java能做的,c肯定也能够做,我曾经看过用c来进行面向对象编程的示例,我只能仰天常叹。

3、我没有那么高的天份,但我想得到那些高人的能力。

4、F#让我能更容易的实现一些目标。更容易地编写并发程序、更容易地编写DSL。


  回复  引用    
#9楼2008-08-22 08:51 | colder[未注册用户]
@lvxuwen

C#开发了F# 这句何解?
在F#的源代码当中, 找不到半点C#的影子.
反倒是C#3.0的很多新概念是先在F#上实验通过的 而且难得的是F#是基于.NET2.0的

另外我所说的给源代码文件改名 是指改第一个字母的大小写
这样做即不影响module名 也不影响输出结果
事实上如果不指定输出程序的文件名 将会采用项目名 而且都是小写的.

  回复  引用  查看    
#10楼[楼主]2008-08-22 09:23 | lvxuwen      
@colder

C#开发了F#,我没讲清楚,我指的是C#开发了F#的编译器,当然我也没求证,因为我也没有F#编译器的源代码,只是看了一些报导,如果colder知道,请赐教。

至于你所说的C#3.0的新概念,来自于F#,其实我认为这只是一种宣传罢了。

因为F#本身也没有做出什么了不起的新语言要素。大多数语言概念来自OCaml,

它的计算表达式(Computation Expression)来源于Haskell(本质上是单子),

活动模式(Active Pattern)可能是创新。F#作为微软研究院的语言,进行语言探索应该是他的职责所在吧,而作为微软的其他语言,拿来主义也不能说明这是F#的发明。如果非要说是创新,那也只能说是把其他语言搬到.NET平台的创新

对于我来说,对文件名改名来达到排列的目的,从来没有想过,我想任何一个有着严格的工程化思想的人来说,严格遵守代码规范是非常有必要的。你可能说F#还没有最佳实践,不过从F#自身的代码库来看,文件名小写,且不使用默认的文件名作为模块名。即使F#没有代码规范,但Ocaml却有代码规范。

我想F#项目组开发的菜单帮助也正是出于像我这种吹毛求疵的人吧

  回复  引用    
#11楼2008-08-22 10:05 | colder[未注册用户]
@lvxuwen

研究院版本的F#是开源的 详见安装目录下的source文件夹
既然是与C#平等的一等公民 如果F#的编译器由C#来实现 那C#的编译器由谁来实现呢?

F#是否是C#3.0的第一块实验田 这个没有宣传的必要

同样 对于任何一个有着严格的工程化思想的人来说 他也不会考虑将一个实验性的语言用于产品 我所说的只是个小技巧 所谓"改名"是指"main.fs"到"Main.fs"的互换

  回复  引用  查看    
#12楼[楼主]2008-08-22 10:20 | lvxuwen      
@colder
F#代码库的位置,我已经知道,并且已经看过,谢谢告知。
至于是说会不会将F#用于产品,这个将另说,F#到目前来说,还是个实验性的语言,但不久以后,就不好说了,正是看好F#在将来会是.net平台一个不错的编写DSL的语言,才让我学习F#的。事实上,最近一周我已经使用F#开发了一个小工具。作为一种尝试,我认为从使用F#开发小模块,小部件是一个不错的起点。
谢谢colder的小技巧,使用F#编写小脚本的时候,是个不错的方法。
最后,"C#的编译器由谁来实现呢? " 呵呵,我还真没办法问答,我很想回答这个问题,不过我仅有的一点点编译器原理知识,还是无法回答你个问题。


  回复  引用    
#13楼2008-08-22 10:41 | colder[未注册用户]
@lvxuwen

正如你所说 不想陷入语言之争
我只是个人喜好而已 真要说有多好 目前这么粗陋 能有多好...

上学的时候 不只一次下决心学C# 到头来一直困惑不已 没想到碰上F# 发现这么对我的思路

于是当有人问我为什么要学F#, 我会开玩笑的说:"F#是给左撇子的人用的"
我就是这样理解的 命令式和函数式的区别 就是左右手的区别 如果能左右手一起开发当然好 但事实上左撇子的人只占约5%左右

呵呵~

  回复  引用  查看    
#14楼[楼主]2008-08-22 12:21 | lvxuwen      
@colder
不管使用左手右手,找到适合自己的方法做事,就很好了。看来colder对F#确实是给予了很高的评价。不过,F#作为一种多范式的语言,语言规则很复杂,不适合作为入门语言。

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1267812




相关文章:

相关链接: