Получение типизированного массива из дискриминационного объединения массивов разных типов в F#
-
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