Как заставить F # выполнить метод только один раз? - PullRequest
2 голосов
/ 11 марта 2012

Я хотел бы определить тип, такой как:

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 не допускаются в дополнении.(?)

Код отлично работает, когда помещается в статическую функцию-член, однако он создает карты каждый раз, когда они необходимы.Излишне говорить, что это даже менее эффективно, чем линейный поиск.Как мне сделать это правильно, пожалуйста?Большое спасибо.

Ответы [ 4 ]

3 голосов
/ 12 марта 2012

Компилируется (с предупреждением)

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, чтобы определить больше членов).

2 голосов
/ 12 марта 2012

Вот код, который работает как требуется:

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?

2 голосов
/ 12 марта 2012

Я полагаю, что вы пытались сделать следующее: для любого Дискриминационного Союза есть возможность перейти от конкретного дела ОУ к его названию и обратно без бремени жесткого кодирования отношений для каждой пары, что имеет место в обоих ответах Брайана иpad.

Если вы жертвуете переопределением 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 вообще, это достигается после размышления и гарантируется правильное отображение автоматически.Этот подход будет работать для любого количества единичных случаев - двух или двух сотен - без какой-либо необходимости жестко кодировать конкретные случаи.

2 голосов
/ 11 марта 2012

Я не понимаю твою точку зрения.Что не так с:

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 #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...