zoukankan      html  css  js  c++  java
  • 奇技淫巧:F#泛型特化的最终优化

           上回讲到,F#中 module 里的泛型变量实际上是泛型函数。由此,在一个泛型特化的实现中出了岔子。最后通过泛型得以解决。使用 F# 来做泛型特化,相比 C# 有这个优势:F# 中直接支持函数赋值,而无须使用委托。避免了 C# 例子里面,为了节省 Lumbda 与委托之间的转换,而做出的丑陋的反射、拆箱操作。代码不再这么艰涩,我们重新来看看这段代码:

            module Mithra =

            type Cache<'T>() =

                static member val GetBytes: 'T->byte[] = Unchecked.defaultof< 'T -> byte[] > with get, set 

            Cache<int16>.GetBytes <- BitConverter.GetBytes

            Cache<int32>.GetBytes <- BitConverter.GetBytes

            Cache<int64>.GetBytes <- BitConverter.GetBytes

            Cache<String>.GetBytes <- Encoding.UTF8.GetBytes 

            let GetBytes<'T> = Cache<'T>.GetBytes

     
      还要重申这点:F# 中 module 里的泛型变量实际上是泛型函数。所以,最后这句 let GetBytes<'T> = Cache<'T>.GetBytes     仍然是函数调用。我们使用 let a = GetBytes<String>("123")      时,实际上调用的代码却是:

            let GetBytes = GetBytes<String>()

            let a = GetBytes("123")

           实际上等于多了一层薄薄的函数封装 。但是,即使是 0.01mm,也是隔靴搔痒。
           要在 F# 中想去掉这层封装,却需要点技巧。我们的目的是可以进行这样的调用:
       let a = Cache<String>.GetBytes("123")

         目前不可如此,因为直接调用的话,就不会执行后面的特化语句,就会调出一个 null 函数。C# 的例子是把 Cache<T> 放在另一个静态类型里,而把特化代码放在该静态类型的静态构造中,第一次进入静态类就会调用构造函数,就会执行此特化代码。F# 不可以,它的语法不允许把一个类衔套到另一个类里面。类可以放在 module 里面,但是 module 不完全等于 C# 静态类。调用 module 里面的类不会执行 module 里面的赋值代码。所以只能用互相递归的类来模拟:  

        type Cache<'T>() =

            static member val GetBytes: 'T->byte[] = Unchecked.defaultof< 'T ->byte[] > with get, set

            // 必须为 val,才会执行后面的代码

            static member val private __ = cachEx.__

        andcachEx() =

            static do Cache<int16>.GetBytes <- BitConverter.GetBytes

            static do Cache<int32>.GetBytes <- BitConverter.GetBytes

            static do Cache<int64>.GetBytes <- BitConverter.GetBytes

            static do Cache<String>.GetBytes <- Encoding.UTF8.GetBytes

            static do Cache<slice>.GetBytes <- fun (src, idx, len)-> src.[idx..idx + len - 1]

            // 这里须为属性或者函数,才会执行 static do

            static member internal __ = ()

           当调用 Cache<String>.GetBytes 函数的时候,最先执行的是 Cache<String的静态函数,F# 中没有明确的静态函数,微软的文档里叫做静态绑定,就是执行所有的 static let 和 static do 语句,另外,文档里没有明确提及的,计算 static member val 的值。所以,现在是先计算 Cache<String>.GetBytes 函数的默认值,然后再计算 Cache<String>.__ 的值。后一项计算则调用了递归类的 cachEx().__  属性。 而调用这个属性之前必须先自动调用 cachEx() 的静态函数,就是特化工作,我们的目的达到了。cachEx() 的静态构造只会执行一次,也就意味着特化工作也不会重复执行。最后,为了使用方便,我们还可以:

        letinline GetBytes<'T> = Cache<'T>.GetBytes 

    F# 中的 
    inline 是 100% 内联,不像C++。C++ 是不确定的。有时,不确定的内联带来的不仅仅是性能的不确定,还有可能是行为上的不确定。正如一开始的那段代码。GetBytes 不内联的话,运行是正确的,但是内联了之后,就会出错。如果加了 inline 之后,编译器有时候选择内联,有时候不选择内联,估计编程者要郁闷死了。所幸的是,普通的C++没有泛型,只有C++/Cli有。

  • 相关阅读:
    【leetcode】修剪二叉搜索树
    053-621
    053-620
    053-619
    053-618
    053-617
    053-616
    053-615
    053-614
    053-613
  • 原文地址:https://www.cnblogs.com/greatim/p/3947632.html
Copyright © 2011-2022 走看看