[Перевод] F#6: Кортежи
Итак, наше путешествие в F # продолжается. Следующая статья будет посвящена типам F #, которые могут иметь или не иметь стандартные эквиваленты .NET. Этот пост о кортежах.
Что такое кортежи
Кортеж — это группа неназванных, но упорядоченных значений, возможно, разных типов.
Создание кортежей
Кортежи очень легко создавать, мы просто делаем что-то вроде показанного ниже. Обратите внимание, как я создал смешанный пакет кортежей, некоторые значения из которых являются числами, а другие являются строками, и мы также можем смешивать и сопоставлять как типы, так и количество значений.
let a = (1,2)
let b = (1,"cat")
let c = (1,"cat")
let d = (1,"cat", 'c')
Универсальные кортежи
Функции, принимающие кортежи, могут также принимать универсальные кортежи без каких-либо проблем. Система логического вывода типа F # с удовольствием справится с выводом правильных типов для кортежа. Вот пример.
let someFunction tup =
let (x,y) = tup
printfn "x is %A and y is %A" x y
do someFunction ("cat","dog")
do someFunction (11,12)
И вот результаты выполнения выше, где можно увидеть, что функция someFunction без проблем принимала и работала с различными типизированными кортежами.
Сигнатуры кортежей
До сих пор мы вообще не касались понимания сигнатур F#, на самом деле это тема, которой я тоже решил посвятить целое сообщение в блоге, так как я считаю, что он достаточна сложна. Однако мы находимся там, где мы есть, то есть здесь и сейчас, и мы изучаем кортежи, поэтому сейчас я просто хотел продемонстрировать, как будет выглядеть сигнатура кортежа.
Допустим, я объявил следующие кортежи в окне FSI:
let a = (1,2)
let b = (1,"codfather")
let c = (1,"c", 12.5)
И затем я рассмотрел их в окне FSI; мы увидим что-то вроде этого:
val a : int * int = (1, 2)
val b : int * string = (1, "codfather")
val c : int * string * float = (1, "c", 12.5)
Это интересно, мы можем увидеть пару вещей здесь, а именно:
- Круглые скобки не являются частью сигнатуры типа
- Система типов F# может правильно выводить тип на основе значений, содержащихся в самом кортеже.
- Запятая заменяется на »*»
Так что кристально чистый кортеж выглядит так:
let a = (1,2)
Будет иметь такую сигнатуру:
int * int
Разбивка кортежей
Итак, мы увидели, как мы можем создавать кортежи, но как насчет разбивки или деконструкции их обратно в отдельные значения. Это возможно? Да, конечно. Как и прежде, давайте начнем с нескольких примеров:
let (a,b) = (1,2)
printfn "(a,b) = (1,2), so value of 'a' should be 1, and it is =%i,\r\n 'b' should be 2, and it is =%i" a b
// используя подстановочный знак, по сути, не создавайте ненужную привязку значения, если вы
// не заинтересован в этом
let (_,z) = (1,2)
printfn "grabbing last value from (1,2) which is = %i" z
let (a,b :string) = (1,"cat")
printfn "grabbing (1,\"cat\") which has values = %i %s" a b
let (a :int,b :string) = (1,"cat")
printfn "grabbing (1,\"cat\") which has values = %i %s" a b
let (a ,b, c) = (1,"cat", 'c')
printfn "grabbing (1,\"cat\",'c') which has values = %i %s %c" a b c
let first = fst (1, 2)
printfn "grabbing fst from (1,2) which has values = %i" first
let second = snd (1, 2)
printfn "grabbing 2nd from (1,2) which has values = %i" second
Результаты печатаются в стандартном окне консоли, следующим образом:
Используя Let
Но как мы получили отдельные части? Ну, все, что вам нужно, в приведенном выше коде, но давайте рассмотрим один пример. Предположим, у нас был такой кортеж:
(1,2)
И я хотел получить значения обоих «значений» кортежа, привязанные к некоторым новым отдельным значениям, мы могли бы просто сделать это:
let (a,b) = (1,2)
Мы также можем выбрать только те значения, которые нам действительно нужны, что делается с использованием подстановочного знака для нежелательных частей. Что гарантирует отсутствие ненужного связывания значений. Вот пример:
let (_,z) = (1,2)
Использование встроенных функций кортежа
Также имеется встроенная поддержка для получения первого и второго значений из кортежа. Что можно сделать с помощью функций »fst» и »snd». Нет никакой поддержки для чего-либо, кроме 2-го элемента (это, вероятно, наиболее распространенные случаи).»fst» и »snd» можно использовать следующим образом:
let first = fst (1, 2)
let second = snd (1, 2)
Теперь я хочу обратить ваше внимание на особый случай, когда у нас может быть несоответствие с количеством значений, которые мы пытаемся взорвать на отдельные значения. Так что это будет что-то вроде примера здесь:
let (a ,b) = (1,"cat", 'c')
Вы можете видеть, что сам кортеж на самом деле содержит 3 значения, но привязка Let имеет только 2 значения, поэтому компилятор предупреждает нас об этом, как вы можете видеть на снимке экрана ниже:
Создание новых кортежей
Возможно, вы захотите создать новые кортежи из существующих кортежей, это достаточно просто, вот пример:
let oldOne = (1,2)
let (x,y) = oldOne
let newOne = (x+1,y+1)
printfn "original = %A, and we did this (x+1,y+1)\r\n to obtain newOne = %A" oldOne newOne
Сравнение кортежей
Кортежи считаются равными, только если
- Они имеют одинаковое количество значений
- ВСЕ значения считаются равными (очевидно, это могут быть пользовательские методы Equals или пользовательские реализации IEquatable и тому подобное)
Давайте посмотрим на какой-нибудь простой пример.
printfn "(1,2) = (3,4) =% b" ((1,2) = (3,4))
printfn "(1,2) = (1,2) =% b" ((1,2) = (1,2))
printfn "('a', 'b') = ('a', 'b') =% b" (('a', 'b') = ('a', 'b'))
printfn "('a', 'b') = ('a', 'c') =% b" (('a', 'b') = ('a', 'c'))
На самом деле, если ваши кортежи имеют разную длину (количество элементов) и вы пытаетесь сравнить их с помощью оператора равенства »=», вы получите предупреждение:
Паттерны сопоставления кортежей
Мы еще не занимались паттернами сопоставления, но позже увидим целый пост на эту тему. А пока просто знайте, что это способ снова сопоставить входные параметры. Вы можете сделать это для кортежей, что делается следующим образом:
let someFunction tup =
match tup with
| 1,2 -> printfn "you passed (1,2)"
| _,_ -> printfn "you passed some other tuple"
do someFunction (11,12)
do someFunction (4,5)
do someFunction (1,2)
do someFunction (13,23)