Как мне написать перечисление в F # без явного присвоения числовых литералов? - PullRequest
5 голосов
/ 16 августа 2011

У меня есть перечисление в F #, например:

type Creature =
   | SmallCreature = 0
   | MediumCreature = 1
   | GiantCreature = 2
   | HumongousCreature = 3
   | CreatureOfNondescriptSize = 4

Мне не нравится вводить числа вручную, и я хочу позже легко добавить больше элементов в перечисление без необходимости сдвигать числа.

Я пробовал это

type Creature =
   | SmallCreature
   | MediumCreature
   | GiantCreature
   | HumongousCreature
   | CreatureOfNondescriptSize

, но позже это вызвало ошибку The type 'Creature' is not a CLI enum type в программе

let input = Int32.Parse(Console.ReadLine())
let output = match EnumOfValue<int, Creature>(input) with // <---Error occurs here
    | Creature.SmallCreature -> "Rat"
    | Creature.MediumCreature -> "Dog"
    | Creature.GiantCreature -> "Elephant"
    | Creature.HumongousCreature -> "Whale"
    | Creature.CreatureOfNondescriptSize -> "Jon Skeet"
    | _ -> "Unacceptably Hideous Monstrosity"

Console.WriteLine(output)
Console.WriteLine()
Console.WriteLine("Press any key to exit...")
Console.Read() |> ignore

Как определить перечисление без ручного присвоения числовых значенийк каждому предмету?

Ответы [ 2 ]

5 голосов
/ 16 августа 2011

К сожалению, вы не можете. Важны ли числовые значения? Если это так, то это несколько выходит за рамки предполагаемого использования перечислений (кроме флагов). Вы можете рассмотреть класс или дискриминационный союз в этом случае.

Ваш второй пример, на самом деле, дискриминационный союз. Но ваше последующее использование EnumOfValue, которое ожидает перечисление, вызывает ошибку.

Другой вариант - сохранить в словаре сопоставление перечисления и числа и заменить поиск по шаблону поиском по словарю. Тогда числовое значение перечисления не имеет значения.

Я согласен, что ручное управление значениями перечисления является обременительным. Надеюсь, это будет исправлено в следующей версии.

4 голосов
/ 16 августа 2011

Как говорит Даниэль, вы не можете определить перечисление без указания числовых эквивалентов.Однако вы можете определить функцию, которая преобразует число в соответствующий случай различенного объединения:

open Microsoft.FSharp.Reflection

let intToDU<'t> n =
    if not (FSharpType.IsUnion typeof<'t>) then
        failwithf "%s is not a discriminated union" typeof<'t>.Name
    let cases = FSharpType.GetUnionCases(typeof<'t>)
    if n >= cases.Length || n < 0 then
        failwithf "%i is out of the range of %s's cases (0 - %i)" n typeof<'t>.Name (cases.Length - 1)
    let uc = cases.[n]
    if uc.GetFields().Length > 0 then 
        failwithf "%s.%s requires constructor arguments" typeof<'t>.Name uc.Name
    FSharpValue.MakeUnion(uc, [||]) :?> 't

Затем вы можете использовать эту обобщенную функцию, например:

type Creature =   
| SmallCreature   
| MediumCreature   
| GiantCreature   
| HumongousCreature   
| CreatureOfNondescriptSize

let input = int (System.Console.ReadLine())

let output = 
    match intToDU input with 
    | SmallCreature -> "Rat"    
    | Creature.MediumCreature -> "Dog"    
    | Creature.GiantCreature -> "Elephant"    
    | Creature.HumongousCreature -> "Whale"    
    | Creature.CreatureOfNondescriptSize -> "Jon Skeet"    

Это дает дополнительное преимущество, так как совпадение с образцом теперь является полным.

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