【问题标题】:Is "implicit overloading" possible?“隐式重载”可能吗?
【发布时间】:2012-01-11 23:36:35
【问题描述】:

我有以下示例:

type Stream (capacity) =
    let data = Array.zeroCreate capacity
    member private s.position = ref 0
    static member private encoder = new Text.UTF8Encoding()
    static member private write (x, o, a : byte[]) = for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256)
    static member private write (x, o, a : byte[]) = for i = 0 to 1 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256s)
    static member private write (x : string, o : int, a : byte[]) = Stream.encoder.GetBytes(x, 0, x.Length, a, o)
    static member format (x : int, s) = let a = Array.create s 0uy in Stream.write(x, 0, a); a
    static member format (x : int16, s) = let a = Array.create s 0uy in Stream.write(x, 0, a); a
    static member format (x : string, s) = let a = Array.create s 0uy in Stream.write(x, 0, a); a

首先,很抱歉代码非常混乱,我只是 F# 的初学者。如您所见,三个format 重载仅在参数类型上有所不同,而它们的主体是相同的(尽管调用write 的不同重载)。是否有可能以某种方式将格式函数减少为一个,也许是内联的?

如果我完全忽略了这里的重点,我深表歉意,但我找不到关于此事的太多信息。

【问题讨论】:

  • 这似乎是pattern matching 可能更合适。
  • 有没有办法不用拳击x?
  • 这是一个很好的问题...您编写此程序的方式看起来更像是一个 OO 程序而不是函数式程序。
  • T0yv0 在这里使用存储序列化函数的类型字典,但他肯定每种类型都有一个序列化函数:gist.github.com/1082622

标签: types f# arguments overloading implicit


【解决方案1】:

我认为你写它的方式可能是最好的选择。

如果不想装箱,那么重载的write函数肯定是需要的,因为不同的类型需要不同的实现序列化。在这种情况下,使用inline 成员也不起作用,因为inline 函数只能需要特定的实例或静态方法on 它获得的某些值,而不是特定的重载。

避免一些重复的合理解决方案是只定义一个重载函数(即write),然后在需要时将其作为参数显式传递给其他函数。你可以这样写:

type Stream (capacity) = 
    let data = Array.zeroCreate capacity 
    member private s.position = ref 0 
    static member private encoder = new Text.UTF8Encoding() 
    static member Write (x, o, a : byte[]) = 
        for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256) 
    static member Write (x, o, a : byte[]) = 
        for i = 0 to 1 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256s) 
    static member Write (x : string, o : int, a : byte[]) = 
        Stream.encoder.GetBytes(x, 0, x.Length, a, o) |> ignore
    // Format takes a writer function 'f' as the first argument
    static member Format (x, s) f = let a = Array.create s 0uy in f(x, 0, a); a 

// When you call 'Format' with 'Stream.Write' as an argument, 
// the compiler chooses the right overload for you
Stream.Format (1s, 100) Stream.Write 
Stream.Format ("foo", 100) Stream.Write 

这避免了定义端的一些代码重复,但它使使用时间更长。如果您不需要像Format 这样的许多函数,那么最好的方法可能是像您最初那样定义带有一些重复代码的重载。

关于inline,您可以使用它来指定参数的类型应该实现某个特定成员(实例或静态),但您不能说应该有特定的重载。如果您为具有静态成员 Write 的所有三种类型(int16string、...)编写了包装器,那么您可以编写:

let inline format< ^T when ^T : 
    (static member Write : ^T * int * byte[] -> unit)> (value:^T) size =
  let a = Array.create size 0uy
  (^T : (static member Write : ^T * int * byte[] -> unit) (value, 0, a)) 
  a

...但这是更复杂的解决方案,在调用format 时还需要编写一些额外的代码,所以我不会真正使用这种方法(但知道它存在可能很有用)。

【讨论】:

  • 非常感谢您的解释和建议的替代方式!
【解决方案2】:

重构format函数很明显;您可以创建一个通用格式函数并将不同的Stream.write 函数作为参数传递。首先,将泛型函数放在类的任何成员之前:

    static let formatGeneric writeFunc x s =
           let a = Array.create s 0uy         
           writeFunc(x, 0, a)         
           a

并使用它来实例化不同的格式函数:

    static member format (x, s) = 
           formatGeneric (fun (x: int, i, a) -> Stream.write(x, i, a)) x s
    static member format (x, s) = 
           formatGeneric (fun (x: int16, i, a) -> Stream.write(x, i, a)) x s
    static member format (x, s) = 
           formatGeneric (fun (x: string, i, a) -> Stream.write(x, i, a)) x s

请注意,Stream.write 以完整形式编写,x 的类型被注释以选择合适的重载。

【讨论】:

    猜你喜欢
    • 2010-09-10
    • 1970-01-01
    • 2013-06-17
    • 2013-10-20
    • 1970-01-01
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 2010-10-06
    相关资源
    最近更新 更多