zoukankan      html  css  js  c++  java
  • F# 的血继限界(1)

           如我们所见,在 F# 的产生和发展过程中,给 C# 引进了很多函数式的革新理念。然而这些革新仍未足以让 C# 进行完全函数式编程。C# 进行函数式编程时存在如下问题:

    1、C# 中不能声明“
    局部不可变,不能处理尾递归优化。
            很遗憾,不可变量和尾递归,可以说是函数式语言的绝对标识,至于有什么好处就不赘言。 而且这两点在C++都支持,把它们放在一起说也就是因为这个原因,它们可以算是 C# 相对 C++ ,进化不完全的“罪证”。 C++中的 const 关键字,在C#里分成 const 和 readonly 两个,前者代表常量,而后者代表不可变,可惜 readonly 只能用来标识字段。替换方案:
            a、使用单元素 Tuple。.net 中支持只有一个元素的 
    Tuple,这下可派上用场了。但是不推荐,因为 Tuple 在.net 里面是 Object,每个变量开一个 Tuple 开销大了点,如果使用多元素 Tuple,却无法使用自定义的变量名,反而增加阅读代码的难度。另外,Tuple 只能保证 Tuple 里面的成员值不变,Tuple 还是可以整个被替换掉这点倒可以忽略,这种替换稍嫌麻烦,我们只要达到变量不会被轻易误修改的目的就行。
     
            b、 使用匿名类型,例如:var locals = new { Value = 10 };
                    这种方法比用 Tuple 好一点。如果多个变量一起放在大括号里,可以保证只读性,也没有太大开销。
                    只是代码需要相当多的调整。会不会因此反而丢失可读性,看施主的因缘际遇了。

            c、最实用的方法,也是没有办法的办法:放弃不可变的追求,把函数缩短,尽量写短的函数,增强可读性,方便重构,好处多多。

            至于尾递归的替换方案,无非就是少用递归,多用循环,也没什么好的建议了。


    2、C# 中缺乏元组、记录( record )。

            元组其实很像 C 里面的 struct,只是它不保证内存是连续的。F# 语法上表示的元组,实际上传递的是松散值,
    只有在对外接口时,它被才被编译成 Tuple 对象。所以元组在 F# 中被广泛应用,而不影响程序性能。C# 中不能表达元组的概念,如果不想每用一个元组就声明一个struct,也可以用 Tuple 对象来表示,只是有一定额外开销,对于通常只有两三个值的元组来说,Tuple 太不轻便了。而且它的元素只能用 Item1,Item2 ... 来表示,很不直观。Tuple 刚推出来的时候,不少文章说可以用 Tuple 来替代 out, 但是这种意见很快就被反对的声音淹没了,毕竟不是同一个重量级的东西啊。

            record 是个轻量级的类,某 MVP 说,它就是个带标签的元组。但是我们看到,
    record 是对象 ,而不是值类型。
            record 某种程度上也很像 C 里面的 struct,相对元组来说,它继承的是 C struct 的另一些特性:
            第一,它的成员带名字标签。C struct 是 C++ class之祖,希望不要介意我这样说。 
            
    第二,它在 F# 内部可以当接口使用。因为 record 的属性可以是函数类型,C 的 struct 也可以包含函数指针。所以它们都常常用来做简单的接口。
            
    而 record 第三个跟 
    C stuct 相像的特性,在函数式编程里面尤其重要: 它没有构造函数,不可继承,它的属性一旦声明就会公开它是如此简单的一个类,只要检查 record, 看看它的属性是否可变,就可以知道它是不是一个不可变的类。我们可以把不可变的 record 当成一个值来优化(类似不可变的 string )。
     

    3、 C# 声明序列的方式很单调。
     

            函数式编程的一个重要特征,就是延迟式计算,而序列是延迟计算的一个常用方式,
            譬如,我要对一个大范围的 IP 地址发查询码,直至有一个 IP 有回应为止,我无须一开始就生成大波 IP 地址,只须写一个 IP 地址枚举器(也就是序列),每发一个码,生成一个新的 IP 即可,(仅作例子,不谈实际操作)。
            在 F# 中,到处都可以使用 seq { ... } 的方式声明一个序列(F# 的大括号系列有很多方便的应用,以后会一一提到)。
            C# 只有两种方式产生序列的方式:
                a、可以通过 Linq.Enumerable,通常你必须先有另一个序列,不是通用方式。
                b、写一个返回 IEnumerable 接口的函数,是通用方式,但是不方便。另外,匿名函数里面不支持 yield return。增加了使用 
    IEnumerable 的死板系数。基本上,你要用 IEnumerable 接口,你就只能在你当前的函数外面另外写一个函数,因此,你就不能做闭包采集,有时不得不写出一大串调用参数。

     
     
  • 相关阅读:
    洛谷 P2969 [USACO09DEC]音符Music Notes
    洛谷 P2646 数数zzy
    洛谷 P1605 迷宫
    洛谷 P1157 组合的输出
    洛谷 P1449 后缀表达式
    洛谷 P1205 [USACO1.2]方块转换 Transformations
    洛谷 P1599 结算日
    洛谷 P2909 [USACO08OPEN]牛的车Cow Cars
    洛谷 P2118 比例简化
    3.2、spark集群运行应用之第三方jar的处理方式
  • 原文地址:https://www.cnblogs.com/greatim/p/3990949.html
Copyright © 2011-2022 走看看