A linguagem
Tuplas e desestruturação
Devolver e agrupar vários valores sem declarar um record, e desmontá-los com var (A, B) := F.
O que é e por que existe#
Uma função que precisa devolver dois valores (quociente e resto, valor e erro) costumava obrigar a declarar um record só para isso, ou a usar parâmetros out. Uma tupla agrupa de 2 a 8 valores, de tipos quaisquer, sem declaração prévia.
function DivMod(A, B: Int32): (Int32, Int32); // tipo tupla anônimo
begin
Exit((A div B, A mod B)); // literal de tupla
end function;
type
TEntry = tuple<Int32, UString>; // tupla com nomeUsando#
| Forma | Significa |
|---|---|
LPair.Item1, .Item2… | acesso por posição (também T[0] e T.0, base 0) |
var (LQ, LR) := DivMod(17, 5); | declara e preenche duas variáveis de uma vez |
var (_, LRem) := DivMod(9, 4); | _ descarta um componente |
(LQuot, LRem) := DivMod(9, 4); | desestrutura em variáveis que já existem |
(X, Y) := (Y, X); | troca sem temporária (o lado direito é lido inteiro antes) |
A = B, A <> B | compara componente a componente |
A < B, A <= B… | ordem lexicográfica, em tuplas do mesmo tipo cujos elementos têm ordem |
program Tuples;
// Demo: tuplas e desestruturação. Uma tupla agrupa valores sem declarar um record.
type
TEntry = tuple<Int32, UString>; // apelido para um tipo tupla
// Uma função pode devolver vários valores de uma vez: o tipo de retorno é (Int32, Int32).
function DivMod(A, B: Int32): (Int32, Int32);
begin
Exit((A div B, A mod B)); // literal de tupla entre parênteses
end function;
function Lookup(AKey: Int32): TEntry;
begin
Result := (AKey, 'chave ' + IntToUStr(AKey));
end function;
// Declarações `var (..)` e literais soltos só valem dentro de rotinas.
procedure Run;
var
LPair: (Int32, Int32);
LQuot, LRem, X, Y: Int32;
LEntry: TEntry;
begin
LPair := DivMod(7, 2);
WriteLn('Item1=', LPair.Item1, ' Item2=', LPair.Item2); // acesso por Item1..ItemN
var (LQ2, LR2) := DivMod(17, 5); // declara e preenche duas variáveis de uma vez
WriteLn('17 div 5 = ', LQ2, ', resto ', LR2);
var (_, LOnlyRem) := DivMod(9, 4); // `_` descarta o componente
WriteLn('só o resto de 9/4: ', LOnlyRem);
(LQuot, LRem) := DivMod(9, 4); // desestrutura em variáveis que já existem
WriteLn(LQuot, ' ', LRem);
X := 1;
Y := 2;
(X, Y) := (Y, X); // troca sem variável temporária
WriteLn('depois da troca: X=', X, ' Y=', Y);
LEntry := Lookup(5);
WriteLn(LEntry.Item1, ' -> ', LEntry.Item2);
// tuplas se comparam componente a componente
WriteLn('iguais? ', LPair = DivMod(7, 2), ' diferentes? ', LPair <> DivMod(8, 3));
end procedure;
begin
Run;
end.Item1=3 Item2=1
17 div 5 = 3, resto 2
só o resto de 9/4: 1
2 1
depois da troca: X=2 Y=1
5 -> chave 5
iguais? TRUE diferentes? TRUEDemo 41. Retorno múltiplo, desestruturação, descarte com _, troca e comparação.
Itens nomeados#
Como em C#, os itens podem ter nome. Os nomes são apelidos de ItemK, ligados ao tipo escrito na declaração:
function Search(AId: Int32): (Name: UString, Age: Int32);
begin
Exit((Name: 'Ana', Age: 30));
end function;
var LPerson := Search(1); // herda os nomes do retorno
WriteLn(LPerson.Name, LPerson.Age, LPerson.Item1);Os nomes não fazem parte do tipo: (Name: UString, Age: Int32) e (UString, Int32) são o mesmo tipo. A desestruturação continua posicional.
Armadilhas e erros típicos#
- Atribuir uma tupla a um escalar é erro de tipos (
FE1302: "tipos incompatíveis (Int32 esperado, (Int32, Int32) encontrado)"). type X = (Int32, UString);continua sendo um enumerado. Para dar nome a uma tupla, escrevatuple<…>.var (A, B) :=só vale dentro de rotina. No corpo do programa use variáveis declaradas; o literal(A, B) := (B, A)vale em qualquer comando, mas não no inicializador de umaconst.- Sem tupla de 1 elemento nem de mais de 8.
- O elemento precisa de um tipo com nome:
array of Xpede um apelido antes. - Os nomes de item não atravessam argumento de genérico (
TList<TPessoa>: use.Item1),property, o.fxdde um package nem ponteiro. T.0.1(índice duplo numa tupla aninhada) lê e grava, mas o elemento 0 tem de ser tupla.- O aviso opt-in
TupleNames(ligado peloLint strict) acusa nomes de item que não casam entre dois tipos.
Veja também#
- Tipos soma: choice e Fenner.Result, para devolver "valor ou erro".
- Demo
41-tuplas.