Instrução de troca

Em linguagens de programação de computador , uma instrução switch é um tipo de mecanismo de controle de seleção usado para permitir que o valor de uma variável ou expressão altere o fluxo de controle da execução do programa por meio de pesquisa e mapa.

As instruções switch funcionam de maneira semelhante à ifinstrução usada em linguagens de programação como C / C++ , C# , Visual Basic .NET , Java e existe na maioria das linguagens de programação imperativas de alto nível , como Pascal , Ada , C / C++ , C# [1] , Visual Basic .NET , Java [2] e em muitos outros tipos de linguagem, usando palavras -chave como switch, ou . caseselectinspect

As instruções switch vêm em duas variantes principais: uma switch estruturada, como em Pascal, que usa exatamente uma ramificação, e uma switch não estruturada, como em C, que funciona como um tipo de goto . Os principais motivos para usar um switch incluem melhorar a clareza, reduzindo a codificação repetitiva e (se a heurística permitir) também oferecer o potencial para uma execução mais rápida por meio de uma otimização mais fácil do compilador em muitos casos.

Instrução switch em C
switch ( idade ) { case 1 : printf ( "Você é um." ); quebrar ; caso 2 : printf ( "Vocês são dois." ); quebrar ; caso 3 : printf ( "Você é três." ); case 4 : printf ( "Você tem três ou quatro." ); quebrar ; padrão : printf ( "Você não é 1, 2, 3 ou 4!" ); }  
                 
                 
     
       
   

História

Em seu texto de 1952, Introdução à Metamatemática , Stephen Kleene provou formalmente que a função CASE (sendo a função IF-THEN-ELSE sua forma mais simples) é uma função recursiva primitiva , onde ele define a noção definition by casesda seguinte maneira:

"#F. A função φ definida assim
φ(x 1 , ... , x n ) =
  • φ 1 (x 1 , ... , x n ) se Q 1 (x 1 , ... , x n ),
  • . . . . . . . . . . . .
  • φ m (x 1 , ... , x n ) se Q m (x 1 , ... , x n ),
  • φ m+1 (x 1 , ... , x n ) caso contrário,
onde Q 1 , ... , Q m são predicados mutuamente exclusivos (ou φ(x 1 , ... , x n ) deve ter o valor dado pela primeira cláusula que se aplica) é primitivo recursivo em φ 1 , ... , φ m+1 , Q 1 , ..., Q m+1 . [3]

Kleene fornece uma prova disso em termos das funções recursivas de tipo booleano "sign-of" sg( ) e "not sign of" ~sg( ) (Kleene 1952:222-223); o primeiro retorna 1 se sua entrada for positiva e −1 se sua entrada for negativa.

Boolos-Burgess-Jeffrey faz a observação adicional de que a "definição por casos" deve ser mutuamente exclusiva e coletivamente exaustiva . Eles também oferecem uma prova da recursividade primitiva dessa função (Boolos-Burgess-Jeffrey 2002:74-75).

O IF-THEN-ELSE é a base do formalismo de McCarthy : seu uso substitui a recursão primitiva e o operador mu .

Sintaxe típica

Na maioria das linguagens, os programadores escrevem uma instrução switch em muitas linhas individuais usando uma ou duas palavras-chave. Uma sintaxe típica envolve:

  • o primeiro select, seguido por uma expressão que geralmente é chamada de expressão de controle ou variável de controle da instrução switch
  • linhas subsequentes que definem os casos reais (os valores), com sequências correspondentes de instruções para execução quando ocorre uma correspondência
  • Em linguagens com comportamento fallthrough, uma breakinstrução geralmente segue uma caseinstrução para encerrar a referida instrução. [Poços]
  • Em algumas linguagens, por exemplo, PL/I , a expressão de controle é opcional; se não houver expressão de controle, cada alternativa começa com uma WHENcláusula contendo uma expressão booleana e ocorre uma correspondência para o primeiro caso para o qual essa expressão é avaliada como verdadeira. Esse uso é semelhante às estruturas if/then/elseif/else em algumas outras linguagens, por exemplo, Perl .
  • Em algumas linguagens, por exemplo, Rexx , nenhuma expressão de controle é permitida e cada alternativa começa com uma WHENcláusula contendo uma expressão booleana e ocorre uma correspondência para o primeiro caso para o qual essa expressão é avaliada como verdadeira.

Cada alternativa começa com o valor específico, ou lista de valores (veja abaixo), que a variável de controle pode corresponder e que fará com que o controle vá para a sequência de instruções correspondente. O valor (ou lista/intervalo de valores) geralmente é separado da sequência de instrução correspondente por dois pontos ou por uma seta de implicação. Em muitos idiomas, cada caso também deve ser precedido por uma palavra-chave como caseou when.

Um caso padrão opcional normalmente também é permitido, especificado por uma palavra-chave default, otherwiseou else. Isso é executado quando nenhum dos outros casos corresponde à expressão de controle. Em algumas linguagens, como C, se nenhum caso corresponder e o defaultfor omitido, a switchinstrução simplesmente será encerrada. Em outros, como PL/I, um erro é gerado.

Semântica

Semanticamente, existem duas formas principais de instruções switch.

A primeira forma são switches estruturados, como em Pascal, onde exatamente um desvio é feito, e os casos são tratados como blocos separados e exclusivos. Isso funciona como uma condicional generalizada if-then-else , aqui com qualquer número de ramificações, não apenas duas.

A segunda forma são switches não estruturados, como em C, onde os casos são tratados como rótulos dentro de um único bloco, e o switch funciona como um goto generalizado. Esta distinção é referida como o tratamento de fallthrough, que é elaborado abaixo.

Cair em

Em muitas linguagens, apenas o bloco correspondente é executado e a execução continua no final da instrução switch. Estes incluem a família Pascal (Object Pascal, Modula, Oberon, Ada, etc.) bem como PL/I , formas modernas de Fortran e dialetos BASIC influenciados por Pascal, a maioria das linguagens funcionais e muitas outras. Para permitir que vários valores executem o mesmo código (e evitar a necessidade de duplicar o código ), as linguagens do tipo Pascal permitem qualquer número de valores por caso, fornecidos como uma lista separada por vírgulas, como um intervalo ou como uma combinação.

Linguagens derivadas da linguagem C e, mais geralmente, aquelas influenciadas pelo GOTO computado de Fortran , em vez disso, apresentam fallthrough, onde o controle se move para o caso correspondente e, em seguida, a execução continua ("cai") para as instruções associadas ao próximo caso no texto de origem . Isso também permite que vários valores correspondam ao mesmo ponto sem nenhuma sintaxe especial: eles são apenas listados com corpos vazios. Os valores podem ser condicionados de forma especial com código no corpo do caso. Na prática, o fallthrough geralmente é evitado com uma breakpalavra-chave no final do corpo correspondente, que encerra a execução do bloco switch, mas isso pode causar erros devido ao fallthrough não intencional se o programador esquecer de inserir a instrução break. Isso é visto por muitos [4]como uma verruga de linguagem e advertido em algumas ferramentas lint. Sintaticamente, os casos são interpretados como rótulos, não blocos, e as instruções switch e break alteram explicitamente o fluxo de controle. Algumas linguagens influenciadas por C, como JavaScript , retêm o fallthrough padrão, enquanto outras removem o fallthrough ou o permitem apenas em circunstâncias especiais. Variações notáveis ​​disso na família C incluem C# , no qual todos os blocos devem ser terminados com um breakou returna menos que o bloco esteja vazio (ou seja, fallthrough é usado como uma forma de especificar vários valores).

Em alguns casos, os idiomas fornecem fallthrough opcional. Por exemplo, Perl não falha por padrão, mas um caso pode fazê-lo explicitamente usando uma continuepalavra-chave. Isso evita falhas não intencionais, mas permite quando desejado. Da mesma forma, o padrão do Bash é não cair quando finalizado com ;;, mas permitir falha [5] com ;&ou ;;&em vez disso.

Um exemplo de uma instrução switch que depende de fallthrough é o dispositivo de Duff .

Compilação

Os compiladores de otimização, como GCC ou Clang , podem compilar uma instrução switch em uma tabela de ramificação ou em uma pesquisa binária por meio dos valores nos casos. [6] Uma tabela de ramificação permite que a instrução switch determine com um número pequeno e constante de instruções qual ramificação executar sem ter que passar por uma lista de comparações, enquanto uma pesquisa binária leva apenas um número logarítmico de comparações, medido no número de casos na instrução switch.

Normalmente, o único método de descobrir se essa otimização ocorreu é observando o assembly resultante ou a saída do código de máquina que foi gerado pelo compilador.

Vantagens e desvantagens

Em algumas linguagens e ambientes de programação, o uso de uma instrução caseor switché considerado superior a uma série equivalente de instruções if else if porque é:

  • Mais fácil de depurar (por exemplo, definir pontos de interrupção no código versus uma tabela de chamada, se o depurador não tiver capacidade de ponto de interrupção condicional)
  • Mais fácil para uma pessoa ler
  • Mais fácil de entender e, consequentemente, mais fácil de manter
  • Profundidade fixa: uma sequência de instruções "if else if" pode resultar em aninhamento profundo, tornando a compilação mais difícil (especialmente em código gerado automaticamente)
  • Mais fácil de verificar se todos os valores são manipulados. Os compiladores podem emitir um aviso se alguns valores de enumeração não forem manipulados.

Além disso, uma implementação otimizada pode ser executada muito mais rapidamente do que a alternativa, porque geralmente é implementada usando uma tabela de ramificação indexada . [7] Por exemplo, decidir o fluxo do programa com base no valor de um único caractere, se implementado corretamente, é muito mais eficiente do que a alternativa, reduzindo consideravelmente os comprimentos do caminho da instrução . Quando implementado como tal, uma instrução switch se torna essencialmente um hash perfeito .

Em termos do gráfico de fluxo de controle , uma instrução switch consiste em dois nós (entrada e saída), mais uma aresta entre eles para cada opção. Por outro lado, uma sequência de instruções "if...else if...else if" tem um nó adicional para cada caso, exceto o primeiro e o último, junto com uma aresta correspondente. O gráfico de fluxo de controle resultante para as sequências de "se" tem, portanto, muito mais nós e quase o dobro de arestas, sem adicionar nenhuma informação útil. No entanto, as ramificações simples nas instruções if são individualmente mais fáceis conceitualmente do que a ramificação complexa de uma instrução switch. Em termos de complexidade ciclomática , ambas as opções aumentam em k −1 se dados k casos.

Alternar expressões

As expressões switch são introduzidas no Java SE 12 , 19 de março de 2019, como um recurso de visualização. Aqui, toda uma expressão switch pode ser usada para retornar um valor. Há também uma nova forma de rótulo de caso, case L->onde o lado direito é uma única expressão. Isso também evita quedas e exige que os casos sejam exaustivos. No Java SE 13, a yieldinstrução é introduzida e, no Java SE 14, as expressões switch se tornam um recurso de linguagem padrão. [8] [9] [10] Por exemplo:

int dias = switch ( mês ) { case JAN , MAR , MAY , JUL , AGO , OUT , DEZ -> 31 ; caso APR , JUN , SET , NOV -> 30 ; caso FEB -> { if ( ano % 400 == 0 ) rendimento 29 ; senão se ( ano %    
             
          
       
               
            100 == 0 ) rende 28 ; else if ( ano % 4 == 0 ) rendimento 29 ; senão rende 28 ; } };    
                
           

Usos alternativos

Muitas linguagens avaliam expressões dentro de switchblocos em tempo de execução, permitindo uma série de usos menos óbvios para a construção. Isso proíbe certas otimizações do compilador, portanto, é mais comum em linguagens de script e dinâmicas em que a flexibilidade aprimorada é mais importante do que a sobrecarga de desempenho.

PHP

Por exemplo, em PHP , uma constante pode ser usada como a "variável" a ser verificada, e a primeira instrução case que resultar nessa constante será executada:

switch  ( true )  { 
    case  ( $x  ==  'olá' ) : 
        foo (); 
        quebrar ; 
    case  ( $z  ==  'howdy' ) :  break ; 
} 
switch  ( 5 )  { 
    case  $x :  break ; 
    caso  $y :  quebrar ; 
}

Esse recurso também é útil para verificar várias variáveis ​​em relação a um valor, em vez de uma variável em relação a muitos valores. COBOL também suporta este formulário (e outros formulários) na EVALUATEdeclaração. O PL/I tem uma forma alternativa da SELECTinstrução em que a expressão de controle é totalmente omitida e a primeira WHENque for avaliada como verdadeira é executada.

Rubi

Em Ruby , devido ao tratamento de ===igualdade, a instrução pode ser usada para testar a classe da variável:

case input quando Array então coloca 'input is an Array!' quando Hash então coloca 'a entrada é um Hash!' fim 
    
    

Ruby também retorna um valor que pode ser atribuído a uma variável e, na verdade, não requer casenenhum parâmetro (agindo um pouco como uma else ifinstrução):

catfood = caso quando cat . idade <= 1 júnior quando gato . idade > 10 sênior senão normal final 
  
     
    
     
    
  
    
  

montador

Uma instrução switch em linguagem assembly :

interruptor: 
cmp ah , 00h je a cmp ah , 01h je b jmp swtend ; Nenhum caso corresponde ou código "padrão" aqui a: push ah mov al , 'a' mov ah , 0Eh mov bh , 00h int 10h pop ah jmp swtend ; Equivalente a "break" b: push ah mov al , 'b' mov ah , 0Eh mov bh    
   
    
   
      

   
    
    
    
   
   
      

   
    
    
   , 00h int 10h pop ah jmp swtend ; Equivalente a "quebrar" ... swtend: 
   
   
      
  

Pitão

Para Python 3.10.6, os PEPs 634-636 foram aceitos, que adicionaram matche casepalavras-chave. [11] [12] [13] [14] Ao contrário de outras linguagens, o Python notavelmente não exibe comportamento de falha.

letra  =  input ( "Coloque uma única letra: " ) . tira ()[ 0 ] . casefold ()  # primeiro caractere sem espaço em branco da entrada, 
letra de correspondência minúscula  : case 'a' | 'e' | 'eu' | 'o' | 'u' : # Ao contrário das condições nas instruções if, a palavra-chave `or` não pode ser usada aqui para diferenciar entre os casos print ( f "Letter { letter } is a vogal!" ) case 'y'
            
    
   
    print ( f "A letra { letra } pode ser uma vogal.) 
  case _ : # `case _` é equivalente a `default` de C e outros print ( f "A letra { letra } não é uma vogal!" )  
    

Manipulação de exceção

Várias linguagens implementam uma forma de instrução switch no tratamento de exceções , onde se uma exceção é gerada em um bloco, uma ramificação separada é escolhida, dependendo da exceção. Em alguns casos, uma ramificação padrão, se nenhuma exceção for levantada, também está presente. Um exemplo inicial é o Modula-3 , que usa a sintaxe TRY... EXCEPT, onde cada um EXCEPTdefine um caso. Isso também é encontrado em Delphi , Scala e Visual Basic .NET .

Alternativas

Algumas alternativas para instruções switch podem ser:

  • Uma série de condicionais if-else que examinam o valor de destino um por vez. O comportamento de falha pode ser alcançado com uma sequência de if condicionais, cada um sem a cláusula else .
  • Uma tabela de consulta , que contém, como chaves, os casevalores e, como valores, a parte sob a caseinstrução.
(Em alguns idiomas, apenas tipos de dados reais são permitidos como valores na tabela de pesquisa. Em outros idiomas, também é possível atribuir funções como valores da tabela de pesquisa, obtendo a mesma flexibilidade de uma switchinstrução real. Consulte o artigo Tabela de controle para obter mais detalhes nisto).
Lua não suporta instruções case/switch. [15] Essa técnica de pesquisa é uma maneira de implementar switchinstruções na linguagem Lua, que não possui switch. [15]
Em alguns casos, as tabelas de pesquisa são mais eficientes do que as instruções não otimizadas switch , pois muitas linguagens podem otimizar as pesquisas na tabela, enquanto as instruções switch não são otimizadas, a menos que o intervalo de valores seja pequeno com poucos intervalos. Uma pesquisa de pesquisa não otimizada e não binária , no entanto, quase certamente será mais lenta do que uma opção não otimizada ou do que as múltiplas declarações if-else equivalentes. [ citação necessária ]
  • Uma tabela de controle (que pode ser implementada como uma tabela de pesquisa simples) também pode ser personalizada para acomodar várias condições em várias entradas, se necessário, e geralmente exibe maior 'compactação visual' do que um switch equivalente (que pode ocupar muitas instruções).
  • Correspondência de padrões , que é usada para implementar a funcionalidade do tipo switch em muitas linguagens funcionais .

Veja também

Referências

  1. ^ Skeet, Jon. C # em Profundidade . Tripulação. pág. 374-375. ISBN 978-1617294532.
  2. ^ Effective Java: Programming Language Guide , terceira edição: ISBN 978-0134685991 , 2018 
  3. ^ "Definição por casos", Kleene 1952:229
  4. ^ van der Linden, Peter (1994). Programação Expert C: Deep C Secrets , p. 38. Prentice Hall, Eaglewood Cliffs. ISBN 0131774298 . 
  5. ^ desde a versão 4.0, lançada em 2009.
  6. ^ Vlad Lazarenko. Da instrução switch para baixo ao código da máquina
  7. ^ Guntheroth, Kurt (27 de abril de 2016). C++ otimizado . Mídia O'Reilly. pág. 182. ISBN 9781491922033.
  8. ^ "JEP 325: Alternar expressões (visualização)" . openjdk.java.net . Recuperado em 28/04/2021 .
  9. ^ "JEP 354: Alternar expressões (segunda visualização)" . openjdk.java.net . Recuperado em 28/04/2021 .
  10. ^ "JEP 361: Alternar expressões" . openjdk.java.net . Recuperado em 28/04/2021 .
  11. ^ Galindo Salgado, Pablo. "O que há de novo no Python 3.10" . Documentação do Python 3.10.6 . Recuperado 2022-08-19 .
  12. ^ Bucher, Brandt; van Rossum, Guido (2020-09-12). "PEP 634 – Correspondência de Padrão Estrutural: Especificação". Propostas de aprimoramento do Python . Recuperado 2022-08-19 .
  13. ^ Kohn, Tobias ; van Rossum, Guido (2020-09-12). "PEP 635 - Correspondência de Padrão Estrutural: Motivação e Racional". Propostas de aprimoramento do Python . Recuperado 2022-08-19 .
  14. ^ Moisset, Daniel F. "PEP 636 – Correspondência de padrão estrutural: Tutorial". Propostas de aprimoramento do Python . Recuperado 2022-08-19 .
  15. ^ ab Instrução switch em Lua

Leitura adicional

0.052218914031982