Получение типизированного массива из дискриминационного объединения массивов разных типов в F#

StackOverflow https://stackoverflow.com/questions/1939113

  •  20-09-2019
  •  | 
  •  

Вопрос

Если у меня есть дискриминационное объединение массивов разных типов, как я могу преобразовать их в «настоящие» типы?

type ItemStuff = 
   | Colors of string[]
   | Sizes of int[]

let foo = Sizes [|1;2;3|]

После выполнения вышеописанного, когда я получаю значение foo, я вижу:

val foo : ItemStuff = Sizes [|1;2;3|]

Как я могу получить реальный массив int из foo?Мне просто не хватает синтаксиса, который позволяет мне получить доступ к чему-то вродеfoo.[2]?Я не могу перечислять через foo, поэтому не смог использовать карту.Я мог бы написать элемент для ItemStuff, который возвращает правильно типизированный массив для каждого возвращаемого типа массива, но это показалось мне неправильным?

Каковы мои лучшие подходы здесь?

Вот что я в итоге сделал.есть идеи, как лучше это сделать?

type ItemProp =
| Colors of string[]
| Sizes of int[]
| Quants of int[]
member this.GetColors() =
   match this with
   | Colors (stringArray) ->
       stringArray
   | _ -> null
member this.GetIntArr() =
   match this with
   | Sizes (intArray) | Quants (intArray) ->
       intArray
   |_ -> null

foo.GetIntArr()
Это было полезно?

Решение

Как я могу получить реальный массив int из foo?

Это реальная проблема, поскольку foo просто говорят, что он имеет тип ItemStuff.Так что не обязательно содержать Sizes-значение вообще - это может быть Colors также.

Следовательно, ваша программа должна решить здесь

let getIntArray = function
    | Sizes arr -> arr
    | Colors _  -> failwith "Given value doesn't contain an int array`

getIntArray foo будет работать правильно, но getIntArray (Colors [||]) потерпит неудачу, но на уровне типа оба действительны.

Обратите внимание: если вы полностью уверены, что операция завершится успешно, вы можете напрямую использовать сопоставление с образцом:

let getIntArray (Sizes arr) = arr

Другие советы

Обычно вы используете сопоставление с образцом, поскольку не знаете, ItemStuff сохраняет цвета или размеры.Если вы точно знаете, что у вас есть Sizes например, вы можете сделать что-то вроде:

let (Sizes arr) = foo

чтобы вытащить массив обратно.

Если вы на самом деле не представляете сценарий «или/или» (Цвета ИЛИ Размеры), рассмотрите возможность использования Тип записи здесь:

type ItemStuff = 
    { Colors : string[];
      Sizes : int[] } with
    static member Default = { Colors = [||]; Sizes = [||] }

let foo = { ItemStuff.Default with Sizes = [|1;2;3|] }
printfn "%A" foo.Colors    // [||]
printfn "%A" foo.Sizes     // [|1; 2; 3|]
printfn "%d" foo.Sizes.[2] // 3

Вы также можете вернуть тип параметра с помощью None, если распознаваемое объединение не было Sizes.

let getIntArray item =
    match item with
    | Sizes(i) -> Some(i)
    | _        -> None

Но имейте в виду, что в конце F# функция всегда преобразует тип входных данных в тип выходных данных или функция просто выполняет побочный эффект (выводит что-то на экран или вставляет данные в массив).

Так, например, если вы просто хотите распечатать Sizes (или ничего, если это Colors) короче будет написать для этой цели новую функцию:

let printSizes item =
    match item with
    | Sizes(i) -> printfn "%A" i
    | _        -> ()

вместо:

let getIntArray item =
    match item with
    | Sizes(i) -> Some(i)
    | _        -> None

let res = getIntArray foo

(function | Some(i) -> printfn "%A" i | _ -> ()) res
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top