【问题标题】:How do I make F# execute a method just once?如何让 F# 只执行一次方法?
【发布时间】:2012-03-28 06:59:24
【问题描述】:

我想定义一个类型,例如:

type blah =
     AThing
   | AnotherThing
   with 
     static member ofString : string -> blah
     override x.ToString () : string

我想确保这两种方法始终保证一致。这样做的一个好方法是从相同的对列表中构造两个映射,然后将它们转换为这两种方法的明显实现。大致:

let pairs = [Athing, "AThing"; AnotherThing, "AnotherThing"]
let tostr = Map.ofList pairs
let toblah = pairs |> List.map swap |> Map.ofList

我认为这段代码只能定义在静态成员函数中。静态位暗示它需要可从 ofString 访问,这是静态的。它不能在类型之前定义,因为列表依赖于它。它不能在之后定义,因为 F# 不允许稍后声明和实现方法(以与例如 C++ 相同的方式)。所以这就留下了静态成员和静态 let 之间的选择。编译器说在扩充中不允许使用静态 let。 (?)

将代码放入静态成员函数时可以正常工作,但是每次需要时都会生成映射。不用说,这甚至比线性搜索效率低。请问我该怎么做?非常感谢。

【问题讨论】:

    标签: f#


    【解决方案1】:

    这是按要求工作的代码:

    type blahFactory() =
        static let pairs = printf "array initialized; "; [AThing, "AThing"; AnotherThing, "AnotherThing"]
        static member tostr = Map.ofList pairs
        static member toblah = pairs |> List.map swap |> Map.ofList
    and blah =
        | AThing
        | AnotherThing
        with
        static member ofString (string) = blahFactory.toblah.[string]
        override x.ToString () = blahFactory.tostr.[x]
    

    我已经放置了printf 指令来演示数组只初始化一次。

    您可能认为有用的几个想法。
    首先,使用 DU 是提供样本的开销。如果您的数据如此简单,您可以考虑使用enum 类型。
    如果你真的是说 DU,那可能是未来可能出现问题的迹象。考虑一下:

    type blah =
        | AThing of string
        | AnotherThing of int * int
    

    在这种情况下,构造和比较都是不可能的。

    你有什么理由不使用标准的 XML 序列化吗?

    【讨论】:

    • 非常感谢,这似乎是最好的解决方案。我不确定为什么枚举在效率方面应该如此不同?我的解析器读取由一些 C++ 代码编写的 XML 文件。我需要两个可执行文件才能在 Linux 下工作,而 AFAIK 没有 Linux .net C++ 编译器。我希望这可以排除使用任何标准 XML 序列化的可能性。我的设计基本上是一个使用 Linq XElement 和活动模式的递归下降解析器。它似乎比 Xerces 有了很大的改进,尽管最好不要考虑堆栈的使用。这里有任何听起来很愚蠢的选择,请告诉我。
    【解决方案2】:

    我相信您正在尝试执行以下操作:对于任何受歧视的联盟都有能力从特定的 DU 案例转到其名称并返回,而无需对每对的关系进行硬编码,这在 Brian 和垫。

    如果您牺牲 ToString() 的覆盖来代替等效的静态成员,则可以通过以下方法完成:

    open Microsoft.FSharp.Reflection
    type Blah =
        | AThing
        | AnotherThing
    
    [<AutoOpenAttribute>]
    module BlahExt =
        let cases = FSharpType.GetUnionCases typeof<Blah>
        let toBlah = dict [for case in cases do yield (case.Name, FSharpValue.MakeUnion(case, [||]) :?> Blah)]
        let fromBlah = dict [for case in cases do yield ((FSharpValue.MakeUnion(case, [||]) :?> Blah), case.Name)]
        type Blah     
        with
            static member ofString name =
                if toBlah.ContainsKey name then (toBlah.Item name) else failwith "bad string"
            static member toString x = fromBlah.Item x
    

    现在printfn "ofString: %A" (Blah.ofString "AThing") 显示联合大小写AThing,反之亦然printfn "toString: %s" (Blah.toString AThing) 显示字符串AThing

    您可以注意到,我没有列出您的 DU 成员,这是通过反射实现的,并保证自动正确映射。这种方法适用于任意数量的单元案例 - 两两百 - 无需对特定案例进行硬编码。

    【讨论】:

    • 非常感谢。这是一个非常好的解决方案,但是解析 XML 文件需要 ofString 函数,因此字符串值会随着时间而改变(我宁愿这不需要更改源代码,只需要更改受影响的字符串)。但是,您的解决方案是我没有想到的,所以这是一次非常有帮助的学习经历。
    【解决方案3】:

    这会编译(带有警告)

    type blah = 
         AThing 
       | AnotherThing 
    
    let pairs = [AThing, "AThing"; AnotherThing, "AnotherThing"] 
    let tostr = Map.ofList pairs 
    let toblah = pairs |> List.map (fun (x,y)->y,x) |> Map.ofList 
    
    type blah
       with  
         static member ofString s = toblah.[s]
         override x.ToString () = tostr.[x]
    

    并演示一个扩充(定义类型,执行其他代码,然后执行type blah with 以定义更多成员)。

    【讨论】:

      【解决方案4】:

      我不明白你的意思。有什么问题:

      type blah =
         | AThing
         | AnotherThing
         with 
           static member ofString = function
                  | "AThing" -> AThing
                  | "AnotherThing" -> AnotherThing
                  | _ -> failwith "Unwellformed string"
      
           override x.ToString () =
                  match x with
                  | AThing -> "AThing"
                  | AnotherThing -> "AnotherThing"
      

      模式匹配是 Θ(1),这在 F# 中非常有效。

      【讨论】:

      • ToString () |> ofString 是不是身份一点都不清楚,尤其是在很多情况下。我怀疑模式匹配是 O(1) 的说法不太可能是相关的。我想要在子句数量中加入 O(log n) 的内容。可能是 F# 实现了这一点,但我认为没有保证。在任何情况下,这都是以一种有用的方式表示的渐近最优复杂度。我相信您可以像使用一元实现 Knapsack 的人一样为 O(1) 声明辩护。除了转换为一元很容易自动化,而在这里我必须打字:-)
      • 与非实习字符串的模式匹配不是 Θ(1),因为不存在这样的算法来在 O(1) 中进行搜索。
      猜你喜欢
      • 1970-01-01
      • 2020-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多