FSharp проверить, если тип кортежа или нет - PullRequest
1 голос
/ 03 марта 2020

Я хочу отформатировать кортеж указанным c способом, и я пытаюсь сделать это, проверив тип кортежа (2 элемента, 3 элемента и т.д. c.). В третьей строке я получаю сообщение об ошибке:

This runtime coercion of type test from type
'd
to
  'a * ('b * 'c)
involves an indeterminate type based on the information prior to this program point. 
Runtime type tests are not allowed on some type. Further type annotations are needed.

Вот моя попытка:

  let namer x =
    match x with
    | :? ('a * ('b * 'c)) as a, b, c -> sprintf "%s_%s_%s" (a.ToString()) (b.ToString()) (c.ToString())
    | :? ('a * 'b) as a, b -> sprintf "%s_%s" (a.ToString()) (b.ToString())
    | a -> sprintf "%s" (a.ToString())

Как сделать что-то подобное? Я хочу иметь возможность форматировать строку в соответствии с типом кортежа.

В конечном итоге я хочу иметь возможность "сплющить" вложенный кортеж в строку без набора скобок. Например:

// What I want
let x = (1, (2, (3, 4)))
let name = namer x
printfn "%s" name
> 1_2_3_4

Обновление : Это отличается от вопроса "Как я могу конвертировать между F # List и F # Tuple?" найдено здесь . Я знаю, как это сделать. Я хочу уметь определять, есть ли у меня кортеж и какой тип кортежа. Идеальным вариантом является обобщенная функция c, которая может принимать один элемент, кортеж или вложенный двухэлементный кортеж. Например, допустимые аргументы:

let name = namer 1
// or
let name = namer (1, 2)
// or 
let name = namer (1, (2, 3))
// or
let name = namer (1, (2, (3, 4)))

Я также хочу обрабатывать нецелые значения. Например:

let name = namer (1, ("2", (3, "chicken")))

1 Ответ

2 голосов
/ 03 марта 2020

Вы можете добиться этого с помощью небольшого количества размышлений и рекурсивной функции:

let isTuple tuple =
    tuple.GetType() |> Reflection.FSharpType.IsTuple 

let getFields (tuple: obj) = 
    tuple |> Reflection.FSharpValue.GetTupleFields |> Array.toList

let rec flatten fields =
    fields
    |> List.collect(
        fun tuple ->
            if isTuple tuple
            then flatten (getFields tuple)
            else [tuple]
    )

let namer(tuple: obj) = 
    if isTuple tuple
    then tuple |> getFields |> flatten
    else [tuple]

namer(1, "test") |> printfn "%A"
namer(1, ("2", (3, "chicken"))) |> printfn "%A"

Вдохновленный:

...