Condicional (programação de computador) - Conditional (computer programming)

Diagrama de fluxo If-Then-Else
Um diagrama de fluxo aninhado "If – Then – Else"

Na ciência da computação , condicionais (isto é, declarações condicionais , expressões condicionais e construções condicionais ) são comandos de linguagem de programação para lidar com decisões. Especificamente, as condicionais executam cálculos ou ações diferentes dependendo se uma condição booleana definida pelo programador é avaliada como verdadeira ou falsa. Em termos de fluxo de controle , a decisão é sempre alcançada alterando seletivamente o fluxo de controle com base em alguma condição (exceto no caso de predicação de ramal ).

Embora o despacho dinâmico não seja geralmente classificado como uma construção condicional, é outra maneira de selecionar alternativas em tempo de execução .

Terminologia

Em linguagens de programação imperativas , o termo " declaração condicional " é geralmente usado, enquanto na programação funcional , os termos " expressão condicional " ou "construção condicional" são preferidos, porque todos esses termos têm significados distintos.

Se – então (–outro)

A if–thenconstrução (às vezes chamada if–then–else) é comum em muitas linguagens de programação. Embora a sintaxe varie de idioma para idioma, a estrutura básica (na forma de pseudocódigo ) é assim:

If (boolean condition) Then
    (consequent)
Else
    (alternative)
End If

Por exemplo:

If stock=0 Then
    message= order new stock
Else
    message= there is stock
End If

No código de exemplo acima, a parte representada por (condição booleana) constitui uma expressão condicional , tendo valor intrínseco (por exemplo, pode ser substituída por qualquer um dos valores Trueou False), mas não tendo nenhum significado intrínseco. Em contraste, a combinação dessa expressão, o Ife ao seu Thenredor e o consequente que se segue constituem uma declaração condicional , tendo significado intrínseco (por exemplo, expressando uma regra lógica coerente), mas nenhum valor intrínseco.

Quando um interpretador encontra um If, ele espera uma condição booleana - por exemplo ,,x > 0 que significa "a variável x contém um número maior que zero" - e avalia essa condição. Se a condição for true, as instruções após o thenserão executadas. Caso contrário, a execução continua na ramificação seguinte - ou no else bloco (que geralmente é opcional), ou se não houver elseramificação, depois de end If.

Após a execução de qualquer um dos ramos, o controle retorna ao ponto após o end If.

História e desenvolvimento

Nas primeiras linguagens de programação, especialmente alguns dialetos do BASIC nos computadores domésticos da década de 1980 , uma if–theninstrução só poderia conter GOTOinstruções (equivalente a uma instrução de desvio ). Isso levou a um estilo de programação difícil de ler conhecido como programação espaguete , com programas nesse estilo chamados de código espaguete . Como resultado, a programação estruturada , que permite (virtualmente) instruções arbitrárias serem colocadas em blocos de instruções dentro de uma ifinstrução, ganhou popularidade, até se tornar a norma, mesmo na maioria dos círculos de programação BASIC. Tais mecanismos e princípios foram baseados na família de linguagens ALGOL mais antiga, porém mais avançada , e linguagens semelhantes a ALGOL, como Pascal e Modula-2, influenciaram as variantes BASIC modernas por muitos anos. Embora seja possível usar apenas GOTOinstruções em if–theninstruções para escrever programas que não são código espaguete e são tão bem estruturados e legíveis quanto programas escritos em uma linguagem de programação estruturada, a programação estruturada torna isso mais fácil e o impõe. Instruções estruturadas if–then–elsecomo o exemplo acima são um dos elementos-chave da programação estruturada e estão presentes nas linguagens de programação de alto nível mais populares, como C , Java , JavaScript e Visual Basic .

O problema do "outro dangling"

A elsepalavra-chave é feita para direcionar uma if–theninstrução específica que a precede, mas para instruções aninhadas if–then , linguagens de programação clássicas como ALGOL 60 lutaram para definir qual instrução específica visar. Sem limites claros para qual instrução é qual, uma elsepalavra - chave pode ter como alvo qualquer if–theninstrução anterior no aninhamento, conforme analisado.

if a then if b then s else s2

pode ser analisado como

if a then (if b then s) else s2

ou

if a then (if b then s else s2)

dependendo se o elseestá associado ao primeiro ifou ao segundo if. Isso é conhecido como problema dangling else e é resolvido de várias maneiras, dependendo do idioma (geralmente por meio da end ifinstrução ou {...}colchetes).

Senão se

Ao usar else if, é possível combinar várias condições. Apenas as instruções após a primeira condição considerada verdadeira serão executadas. Todas as outras declarações serão ignoradas.

if condition then
   --statements
elseif condition then
    -- more statements
elseif condition then
    -- more statements;
...
else
    -- other statements;
end if;

Por exemplo, para uma loja que oferece até 30% de desconto em um item:

if discount < 11% then
    print (you have to pay $30)
elseif discount<21% then
    print (you have to pay $20)
elseif discount<31% then
    print (you have to pay $10)
end if;

A elseifdeclaração, na linguagem Ada , por exemplo, é simplesmente um açúcar sintático para elseseguido de if. Em Ada, a diferença é que apenas um end ifé necessário, se for usado em elseifvez de elseseguido por if. O PHP usa a elseifpalavra - chave tanto para chaves quanto para sintaxes de dois pontos. Perl fornece a palavra elsif- chave para evitar o grande número de colchetes que seriam exigidos por várias instruções ife else. Python usa a palavra-chave especial elifporque a estrutura é denotada por indentação em vez de colchetes, portanto, um uso repetido de elsee ifexigiria maior indentação após cada condição. Algumas implementações do BASIC , como Visual Basic , ElseIftambém usam . Da mesma forma, os shells UNIX anteriores (posteriormente reunidos na sintaxe do shell POSIX) também usam elif, mas dando a opção de delimitar com espaços, quebras de linha ou ambos.

No entanto, em muitas linguagens descendentes mais diretamente de Algol, como Simula , Pascal , BCPL e C , essa sintaxe especial para o else ifconstruto não está presente, nem está presente em muitos derivados sintáticos de C, como Java , ECMAScript e em breve. Isso funciona porque, nessas linguagens, qualquer instrução única (neste caso ...) pode seguir uma condicional sem ser incluída em um bloco. if cond

Esta escolha de design tem um pequeno "custo". Cada else iframificação adiciona efetivamente um nível de aninhamento extra. Isso complica o trabalho do compilador (ou das pessoas que o escrevem), porque o compilador deve analisar e implementar else ifcadeias arbitrariamente longas recursivamente.

Se todos os termos na sequência de condicionais estão testando o valor de uma única expressão (por exemplo, if x=0... else if x=1... else if x=2...), uma alternativa é a instrução switch , também chamada de instrução case ou instrução select. Por outro lado, em linguagens que não possuem uma instrução switch, elas podem ser produzidas por uma sequência de else ifinstruções.

Expressões If – then – else

Muitas linguagens oferecem suporte a expressões if , que são semelhantes às instruções if, mas retornam um valor como resultado. Portanto, são expressões verdadeiras (que avaliam um valor), não declarações (que podem não ser permitidas no contexto de um valor).

Família Algol

ALGOL 60 e alguns outros membros da família ALGOL permitem if–then–elsecomo expressão:

  myvariable := if x > 20 then 1 else 2

Dialetos Lisp

Nos dialetos do Lisp  - Scheme , Racket e Common Lisp  - o primeiro dos quais foi inspirado em grande parte pelo ALGOL:

;; Scheme
(define myvariable (if (> x 12) 1 2))   ; Assigns 'myvariable' to 1 or 2, depending on the value of 'x'
;; Common Lisp
(let ((x 10))
  (setq myvariable (if (> x 12) 2 4)))  ; Assigns 'myvariable' to 2

Haskell

Em Haskell 98, há apenas uma expressão if , nenhuma instrução if , e a elseparte é obrigatória, pois toda expressão deve ter algum valor. A lógica que seria expressa com condicionais em outras linguagens é geralmente expressa com correspondência de padrões em funções recursivas.

Como Haskell é preguiçoso , é possível escrever estruturas de controle, como if , como expressões comuns; a avaliação preguiçosa significa que uma função if pode avaliar apenas a condição e o branch adequado (onde uma linguagem estrita avaliaria todos os três). Pode ser escrito assim:

if' :: Bool -> a -> a -> a
if' True x _ = x
if' False _ y = y

Linguagens semelhantes a C

Linguagens C e C-like têm um operador ternário especial ( ? :) para expressões condicionais com uma função que pode ser descrita por um modelo como este:

condition ? evaluated-when-true : evaluated-when-false

Isso significa que ele pode ser embutido em expressões, ao contrário das instruções if, em linguagens semelhantes a C:

my_variable = x > 10 ? "foo" : "bar";  // In C-like languages

que pode ser comparada às expressões if – then – else da família Algol (em contraste com uma instrução ) (e semelhantes em Ruby e Scala, entre outros).

Para realizar o mesmo usando uma instrução if, isso levaria mais de uma linha de código (sob as convenções de layout típicas) e exigiria a menção de "my_variable" duas vezes:

if (x > 10)
    my_variable = "foo";
else
    my_variable = "bar";

Alguns argumentam que a instrução if / then explícita é mais fácil de ler e que pode ser compilada em um código mais eficiente do que o operador ternário, enquanto outros argumentam que expressões concisas são mais fáceis de ler do que instruções espalhadas por várias linhas contendo repetição.

Pequeno Básico

x = TextWindow.ReadNumber()
If (x > 10) Then
    TextWindow.WriteLine("My variable is named 'foo'.")
Else
    TextWindow.WriteLine("My variable is named 'bar'.")
EndIf

Primeiro, quando o usuário executa o programa, um cursor aparece esperando que o leitor digite um número. Se esse número for maior que 10, o texto "Minha variável se chama 'foo'." é exibido na tela. Se o número for menor que 10, a mensagem "Minha variável se chama 'bar'." é impresso na tela.

Visual básico

No Visual Basic e em algumas outras linguagens, IIfé fornecida uma função chamada , que pode ser usada como uma expressão condicional. No entanto, ela não se comporta como uma expressão condicional verdadeira, porque tanto as ramificações verdadeiras quanto as falsas são sempre avaliadas; apenas o resultado de um deles é jogado fora, enquanto o resultado do outro é retornado pela função IIf.

Tcl

Em Tcl if não é uma palavra-chave, mas uma função (em Tcl conhecido como comando ou proc). Por exemplo

if {$x > 10} {
   puts "Foo!"
}

invoca uma função chamada ifpassando 2 argumentos: o primeiro sendo a condição e o segundo sendo o verdadeiro ramo. Ambos os argumentos são passados ​​como strings (em Tcl, tudo dentro das chaves é uma string).

No exemplo acima, a condição não é avaliada antes de chamar a função. Em vez disso, a implementação da iffunção recebe a condição como um valor de string e é responsável por avaliar essa string como uma expressão no escopo do chamador.

Esse comportamento é possível usando os comandos uplevele expr:

Uplevel torna possível implementar novas construções de controle como procedimentos Tcl (por exemplo, uplevel pode ser usado para implementar a construção while como um procedimento Tcl).

Por ifser na verdade uma função, ela também retorna um valor:

O valor de retorno do comando é o resultado do script do corpo que foi executado ou uma string vazia se nenhuma das expressões for diferente de zero e não houver corpoN.

Ferrugem

Na Rust , ifé sempre uma expressão. Ele avalia o valor de qualquer ramificação executada ou o tipo de unidade ()se nenhuma ramificação for executada. Se uma ramificação não fornecer um valor de retorno, ela será avaliada como ()por padrão. Para garantir que o iftipo da expressão seja conhecido no momento da compilação, cada ramificação deve ser avaliada como um valor do mesmo tipo. Por esta razão, um elseramo é efetivamente obrigatório, a menos que os outros ramos avaliem como (), porque um ifsem um elsesempre pode avaliar como ()por padrão.

// Assign my_variable some value, depending on the value of x
let my_variable = if x > 20 {
    1
} else {
    2
};

// This variant will not compile because 1 and () have different types
let my_variable = if x > 20 {
    1
};

// Values can be omitted when not needed
if x > 20 {
    println!("x is greater than 20");
}

Aritmética se

Até Fortran 77 , a linguagem Fortran tem uma declaração "aritmética if" que está a meio caminho entre um IF computado e uma declaração de caso, com base na tricotomia x <0, x = 0, x > 0. Esta foi a declaração condicional mais antiga em Fortran:

IF (e) label1, label2, label3

Onde e é qualquer expressão numérica (não necessariamente um número inteiro); isso é equivalente a

IF (e .LT. 0) GOTO label1
IF (e .EQ. 0) GOTO label2
GOTO label3

Como esse IF aritmético é equivalente a várias GOTOinstruções que podem saltar para qualquer lugar, ele é considerado uma instrução de controle não estruturada e não deve ser usado se mais instruções estruturadas puderem ser usadas. Na prática, foi observado que a maioria dos IFenunciados aritméticos referenciavam o enunciado a seguir com um ou dois dos rótulos.

Esta foi a única instrução de controle condicional na implementação original do Fortran no computador IBM 704 . Nesse computador, o código operacional de teste e ramificação tinha três endereços para esses três estados. Outros computadores teriam registros de "sinalização", como positivo, zero, negativo, até mesmo, estouro, transporte, associados às últimas operações aritméticas e usariam instruções como 'Desviar se acumulador negativo' e 'Desviar se acumulador zero' ou similar. Observe que a expressão é avaliada apenas uma vez e, em casos como aritmética de inteiros, onde pode ocorrer estouro, o estouro ou sinalizadores de transporte também seriam considerados.

Implementação orientada a objetos em Smalltalk

Em contraste com outras linguagens, em Smalltalk a declaração condicional não é uma construção de linguagem, mas definida na classe Booleancomo um método abstrato que leva dois parâmetros, ambos encerramentos . Booleantem duas subclasses, Truee False, que definem o método, Trueexecutando apenas o primeiro encerramento, Falseexecutando apenas o segundo encerramento.

var = condition 
    ifTrue: [ 'foo' ]
    ifFalse: [ 'bar' ]

JavaScript

Dois exemplos em JavaScript :

if (Math.random() < 0.5) {
  console.log("You got Heads!");
} else {
  console.log("You got Tails!");
}

var x = Math.random();
if (x < 1/3) {
  console.log("One person won!");
} else if (x < 2/3) {
  console.log("Two people won!");
} else {
  console.log("It's a three-way tie!");
}

Cálculo lambda

No cálculo Lambda , o conceito de uma condicional if-then-else pode ser expresso usando as expressões:

true = λx. λy. x
false = λx. λy. y
ifThenElse = (λc. λx. λy. (c x y))
  1. true leva até dois argumentos e uma vez que ambos são fornecidos (veja currying ), ele retorna o primeiro argumento fornecido.
  2. false leva até dois argumentos e uma vez que ambos são fornecidos (veja currying ), ele retorna o segundo argumento fornecido.
  3. ifThenElse leva até três argumentos e, uma vez que todos são fornecidos, ele passa o segundo e o terceiro argumento para o primeiro argumento (que é uma função que fornece dois argumentos e produz um resultado). Esperamos que ifThenElse aceite apenas verdadeiro ou falso como um argumento, ambos os quais projetam os dois argumentos fornecidos em seu único argumento preferido, que é então retornado.

observação : se ifThenElse receber duas funções como condicionais esquerda e direita; é necessário também passar uma tupla vazia () para o resultado de ifThenElse para realmente chamar a função escolhida, caso contrário, ifThenElse retornará apenas o objeto de função sem ser chamado.

Em um sistema onde os números podem ser usados ​​sem definição (como Lisp, matemática de papel tradicional, etc.), o acima pode ser expresso como um único fecho abaixo:

 ((λtrue. λfalse. λifThenElse.
     (ifThenElse true 2 3)
 )(λx. λy. x)(λx. λy. y)(λc. λl. λr. c l r))

Aqui, true, false e ifThenElse são vinculados às suas respectivas definições, que são passadas para seu escopo no final de seu bloco.

Uma analogia de trabalho do JavaScript (usando apenas funções de variável única para rigor) para isso é:

 var computationResult = ((_true => _false => _ifThenElse => 
     _ifThenElse(_true)(2)(3) 
 )(x => y => x)(x => y => y)(c => x => y => c(x)(y)));

O código acima com funções multivariáveis ​​é parecido com este:

 var computationResult = ((_true, _false, _ifThenElse) =>
     _ifThenElse(_true, 2, 3)
 )((x, y) => x, (x, y) => y, (c, x, y) => c(x, y));

outra versão do exemplo anterior sem um sistema em que os números são assumidos está abaixo.

O primeiro exemplo mostra a primeira ramificação sendo tomada, enquanto o segundo exemplo mostra a segunda ramificação sendo tomada.

 ((λtrue. λfalse. λifThenElse.
     (ifThenElse true (λFirstBranch. FirstBranch) (λSecondBranch. SecondBranch))
 )(λx. λy. x)(λx. λy. y)(λc. λl. λr. c l r))

 ((λtrue. λfalse. λifThenElse.
     (ifThenElse false (λFirstBranch. FirstBranch) (λSecondBranch. SecondBranch))
 )(λx. λy. x)(λx. λy. y)(λc. λl. λr. c l r))

Smalltalk usa uma ideia semelhante para suas representações true e false, com True e False sendo objetos singleton que respondem a mensagens ifTrue / ifFalse de forma diferente.

Haskell costumava usar este modelo exato para seu tipo booleano, mas no momento em que este artigo foi escrito, a maioria dos programas Haskell usa uma construção sintática "if a then b else c" que, ao contrário de ifThenElse, não compõe a menos que seja envolvida em outra função ou reimplementada conforme mostrado na seção Haskell desta página.

Declarações de caso e troca

As instruções switch (em algumas linguagens, instruções case ou ramificações de múltiplas vias) comparam um determinado valor com constantes especificadas e agem de acordo com a primeira constante a ser correspondida. Geralmente, há uma provisão para uma ação padrão ('else', 'else') a ser executada se nenhuma correspondência for bem-sucedida. As instruções switch podem permitir otimizações do compilador , como tabelas de pesquisa . Em linguagens dinâmicas, os casos não podem ser limitados a expressões constantes e podem se estender para correspondência de padrões , como no exemplo de script de shell à direita, onde o '*)' implementa o caso padrão como uma expressão regular correspondendo a qualquer string.

Pascal : C : Script Shell :
case someChar of
  'a': actionOnA;
  'x': actionOnX;
  'y','z':actionOnYandZ;
  else actionOnNoMatch;
end;
switch (someChar) {
  case 'a': actionOnA; break;
  case 'x': actionOnX; break;
  case 'y':
  case 'z': actionOnYandZ; break;
  default: actionOnNoMatch;
}
case $someChar in 
   a)    actionOnA; ;;
   x)    actionOnX; ;;
   [yz]) actionOnYandZ; ;;
  *)     actionOnNoMatch  ;;
esac

Correspondência de padrões

A correspondência de padrões pode ser vista como uma alternativa para as instruções if – then – else e case . Ele está disponível em muitas linguagens de programação com recursos de programação funcionais, como Wolfram Language , ML e muitos outros. Aqui está um exemplo simples escrito na linguagem OCaml :

match fruit with
| "apple" -> cook pie
| "coconut" -> cook dango_mochi
| "banana" -> mix;;

O poder da correspondência de padrões é a capacidade de corresponder concisamente não apenas ações, mas também valores a padrões de dados. Aqui está um exemplo escrito em Haskell que ilustra esses dois recursos:

map _ []      = []
map f (h : t) = f h : map f t

Este código define um mapa de função , que aplica o primeiro argumento (uma função) a cada um dos elementos do segundo argumento (uma lista) e retorna a lista resultante. As duas linhas são as duas definições da função para os dois tipos de argumentos possíveis neste caso - um onde a lista está vazia (apenas retorna uma lista vazia) e o outro caso onde a lista não está vazia.

A correspondência de padrões não é, estritamente falando, sempre uma construção de escolha, porque é possível em Haskell escrever apenas uma alternativa, que é garantida que sempre será correspondida - nesta situação, ela não está sendo usada como uma construção de escolha, mas simplesmente como um meio para vincular nomes a valores. No entanto, é freqüentemente usado como uma construção de escolha nos idiomas em que está disponível.

Condicionais baseados em hash

Em linguagens de programação que têm arrays associativos ou estruturas de dados comparáveis, como Python , Perl , PHP ou Objective-C , é idiomático usá-los para implementar a atribuição condicional.

pet = input("Enter the type of pet you want to name: ")
known_pets = {
    "Dog": "Fido",
    "Cat": "Meowsles",
    "Bird": "Tweety",
}
my_name = known_pets[pet]

Em linguagens que têm funções anônimas ou que permitem a um programador atribuir uma função nomeada a uma referência de variável, o fluxo condicional pode ser implementado usando um hash como uma tabela de despacho .

Predicação

Uma alternativa às instruções de desvio condicional é a predicação . A predicação é um recurso arquitetônico que permite que as instruções sejam executadas condicionalmente em vez de modificar o fluxo de controle .

Referência cruzada do sistema de escolha

Esta tabela se refere à especificação de idioma mais recente de cada idioma. Para linguagens que não possuem uma especificação, a implementação oficial mais recente é mencionada.

Linguagem de programação Estruturado se switch –select – case Aritmética se Correspondência de padrões
então outro else – if
Ada sim sim sim sim Não Não
APL sim sim sim sim Não Não
Bash shell sim sim sim sim Não sim
C , C ++ sim sim sim Cair em Não Não
C # sim sim Desnecessário sim Não Não
COBOL sim sim Desnecessário sim Não Não
Eiffel sim sim sim sim Não Não
F # sim sim sim Desnecessário Não sim
Fortran 90 sim sim sim sim sim Não
Ir sim sim Desnecessário sim Não Não
Haskell sim Precisava Desnecessário Sim, mas desnecessário Não sim
Java sim sim Desnecessário Cair em Não Não
ECMAScript ( JavaScript ) sim sim Desnecessário Cair em Não Não
Mathematica sim sim sim sim Não sim
Oberon sim sim sim sim Não Não
Perl sim sim sim sim Não Não
PHP sim sim sim Cair em Não Não
Pascal , Object Pascal ( Delphi ) sim sim Desnecessário sim Não Não
Pitão sim sim sim Não Não Não
QuickBASIC sim sim sim sim Não Não
Rubi sim sim sim sim Não sim
Ferrugem sim sim sim Desnecessário Não sim
Scala sim sim Desnecessário Cair em Não sim
SQL sim sim sim sim Não Não
Rápido sim sim sim sim Não sim
Tcl sim sim sim sim Não sim
Visual Basic , clássico sim sim sim sim Não Não
Visual Basic .NET sim sim sim sim Não Não
Windows PowerShell sim sim sim Cair em Não Não
  1. ^ Isso se refere à correspondência de padrões como uma construção condicional distinta na linguagem de programação - em oposição ao mero suporte de correspondência de padrões de string, comoosuporte aexpressões regulares.
  2. 1 2 3 4 5 O frequentemente encontradoelse ifna família C de linguagens, e em COBOL e Haskell, não é um recurso de linguagem, mas um conjunto deinstruçõesif then elseaninhadas e independentescombinadas com um layout de código-fonte específico. No entanto, isso também significa que uma construção else – if distinta não é realmente necessária nessas linguagens.
  3. 1 2 Em Haskell e F #, uma construção de escolha de constante separada é desnecessária, porque a mesma tarefa pode ser feita com correspondência de padrões.
  4. ^ Em umacaseconstruçãoRuby,acorrespondência deexpressão regularestá entre as alternativas de controle de fluxo condicional disponíveis. Para obter um exemplo, consulteestapergunta sobre Stack Overflow.
  5. 1 2 SQL tem duas construções semelhantes que cumprem ambas as funções, ambas introduzidas noSQL-92. UmaCASEexpressão "pesquisada"CASE WHEN cond1 THEN expr1 WHEN cond2 THEN expr2 [...] ELSE exprDflt ENDfunciona comoif ... else if ... else, enquanto umaCASEexpressão "simples":CASE expr WHEN val1 THEN expr1 [...] ELSE exprDflt ENDfunciona como uma instrução switch. Para obter detalhes e exemplos, consulteCase (SQL).
  6. ^ A aritméticaifé obsoleta em Fortran 90.
  7. ^ A correspondência de padrões foi adicionada no Ruby 3.0. Algumas construções de correspondência de padrões ainda são experimentais.

Veja também

Referências

links externos