F # Concat двух кортежей без деконструкции - PullRequest
0 голосов
/ 26 ноября 2018

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

Например:

// given two tuples
let tuple1 = (1, 2)
let tuple2 = (3, 4, 5)

// resulting in
let tuple3 = (1, 2, 3, 4, 5)

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

let a b = tuple1
let c d e = tuple2
let tuple4 = (a, b, c, d, e)

Однако это не будет работать с кортежом неизвестной длины.

Другое решение может быть следующим:

let tuple5 = tuple1, tuple2

Но это приводит к кортежу из двух кортежей, подобных этому:

(int * int) * (int * int * int)

Есть ли функция кортежа, которую мне не хватает?Есть ли что-то еще, что я пропускаю?Спасибо.

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

Объяснение состоит в том, что кортежи не предназначены для использования в качестве списков (которые могут иметь неизвестную длину).Вместо этого думайте о кортежах как об очень простых структурах или классах, в которых члены неявно называются «первым», «вторым», «третьим» и т. Д. Итак, ваш вопрос похож на ответ «как я объединяю членов двух структур»?Ответ может быть отражением, но, скорее всего, намекает на некоторую проблему при моделировании типов данных для вашей программы.

Типичными вариантами использования для кортежей являются очень простые специальные типы данных или другие случаи, когда длина и порядокчлены являются естественными.Например:

type Point2D = float * float
type Point3D = float * float * float
type Size = int * int
type Error = int * string
0 голосов
/ 27 ноября 2018

Это не ответ, поскольку он только демонстрирует тщетность попытки обойти систему типа F # для кортежей.Но технически действительно возможно объединить кортежи разной длины с универсальной функцией, которая могла бы использовать свои аргументы безопасным для типов способом;увы, он будет иметь неопределенный тип возвращаемого значения, требующий аннотации.Это достигается за счет использования двух функций, которые по праву не имеют заметного значения в функциональном программировании:

Есть несколько других ограничений: мы не можем закодировать 1-кортеж, такая перегрузка не может быть однозначноразрешен, поскольку он может обозначать n-кортеж по очереди.По аналогичной причине исключается использование двух кортежей разной длины в качестве прямого аргумента для статических членов.

type X = X with
    static member ($) (_ : X, (a, b)) = [|box a; box b|]
    static member ($) (_ : X, (a, b, c)) = [|box a; box b; box c|]
let inline t2a x = X $ x

open Microsoft.FSharp.Reflection
let a2t arg : 'r =
    FSharpValue.MakeTuple(arg, typeof<'r>) :?> 'r

let inline (@@) a b =
    Array.append (t2a a) (t2a b) |> a2t

let (t : int * char * int64 * string) = (1, 'b') @@ (3L, "d")
// val t : int * char * int64 * string = (1, 'b', 3L, "d")
let (u : int * int * int * int * int) = (1, 2, 3) @@ (4, 5)
// val u : int * int * int * int * int = (1, 2, 3, 4, 5)

Учитывая вышеизложенное, конкатенация 4-кортежа с 2-кортежем приведет к компиляцииошибка.

let (v : int * int * int * int * int * int) = (1, 2, 3, 4) @@ (5, 6)
// error FS0001: No overloads match for method 'op_Dollar'.
0 голосов
/ 26 ноября 2018

Конструкция кортежей F # означает, что вам нужно написать код, который знает длину кортежа - кортежи разной длины имеют разные типы, и вы не можете писать код, который является универсальным по длине кортежа.

Если вам нужна структура данных, которая позволяет хранить произвольное количество значений и объединять их, вероятно, лучше использовать списки, которые позволяют объединять данные с помощью оператора @:

let list1 = [1; 2]
let list2 = [3; 4; 5]

let list3 = list1 @ list2

Предостережение заключается в том, чтосписки могут хранить значения только одного типа - например, int в вашем примере.Если вам нужно хранить произвольное количество произвольных вещей, вам, вероятно, понадобится список дискриминированных союзов.

...