Por que os idiomas exigem parênteses em torno de expressões quando usados com “if” e “while”?

67

Linguagens como C, Java e C ++ exigem parênteses em torno de uma expressão inteira quando usadas em if , while ou switch .

if (true) {
    // Do something
}

ao contrário de

if true {
    // Do something
}

Isso parece estranho para mim porque os parênteses são redundantes. Neste exemplo, true é uma expressão única por si só. Os parênteses não transformam seu significado de qualquer maneira que eu saiba. Por que essa sintaxe ímpar existe e por que é tão comum? Há algum benefício que eu não conheça?

    
por Velovix 07.11.2016 / 03:26
fonte

10 respostas

155

Precisa haver alguma maneira de dizer onde a condição termina e o ramo começa. Existem muitas maneiras diferentes de fazer isso.

Em alguns idiomas, não há condicionais , por exemplo em Smalltalk, Self, Newspeak, Io, Ioke, Seph e Fancy. A ramificação condicional é simplesmente implementada como um método normal, como qualquer outro método. O método é implementado em objetos booleanos e é chamado em um booleano. Desse modo, a condição é simplesmente o receptor do método, e os dois ramos são dois argumentos, por exemplo. em Smalltalk:

aBooleanExpression ifTrue: [23] ifFalse: [42].

Caso você esteja mais familiarizado com o Java, isso é equivalente ao seguinte:

aBooleanExpression.ifThenElse(() -> 23, () -> 42);

Na família de linguagens Lisp, a situação é similar: condicionais são apenas funções normais (na verdade, macros) e o primeiro argumento é a condição, o segundo e terceiro são os ramos, então são apenas argumentos normais de função, e não há nada de especial necessário para delimitá-los:

(if aBooleanExpression 23 42)

Algumas linguagens usam palavras-chave como delimitadores, por exemplo, Algol, Ada, BASIC, Pascal, Modula-2, Oberon, Oberon-2, Oberon ativo, Componente Pascal, Zonnon, Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

Em Ruby, você pode usar uma palavra-chave ou um separador de expressão (ponto-e-vírgula ou nova linha):

if a_boolean_expression then 23 else 42 end

if a_boolean_expression; 23 else 42 end

# non-idiomatic, the minimum amount of whitespace required syntactically
if a_boolean_expression
23 else 42 end

# idiomatic, although only the first newline is required syntactically
if a_boolean_expression
  23
else
  42
end

Ir exige que as ramificações sejam blocos e não permite expressões ou instruções, o que torna as chaves obrigatórias. Portanto, parênteses não são necessários, embora você possa adicioná-los se quiser; Perl6 e Rust são semelhantes a este respeito:

if aBooleanExpression { return 23 } else { return 42 }

Alguns idiomas usam outros caracteres não alfanuméricos para delimitar a condição, por exemplo, Python:

if aBooleanExpression: return 23
else: return 42

A linha inferior é: você precisa de algum modo de dizer onde a condição termina e o ramo começa. Existem muitas maneiras de fazer isso, parênteses são apenas um deles.

    
por 07.11.2016 / 10:38
fonte
70

Os parênteses são desnecessários apenas se você usar chaves.

if true ++ x;

Por exemplo, torna-se ambíguo sem eles.

    
por 07.11.2016 / 03:51
fonte
21

Os parênteses em uma instrução if não possuem o mesmo significado que os parênteses usados em uma expressão aritmética. Parênteses em uma expressão aritmética são usados para agrupar expressões. Parênteses em uma instrução if são usados para delimitar a expressão booleana; isto é, para diferenciar a expressão booleana do resto da instrução if .

Em uma instrução if , os parênteses não executam uma função de agrupamento (embora, na instrução if , você ainda possa usar parênteses para agrupar expressões aritméticas. O conjunto externo de parênteses serve para delimitar toda a expressão booleana ). Torná-los necessários simplifica o compilador, já que o compilador pode contar com esses parênteses sempre presentes.

    
por 07.11.2016 / 03:38
fonte
16

Como outros já salientaram parcialmente, isso se deve ao fato de que as expressões também são instruções válidas e, no caso de um bloco com apenas uma instrução, você pode soltar as chaves. Isso significa que o seguinte é ambíguo:

if true
    +x;

Porque pode ser interpretado como:

if (true + x) {}

em vez de:

if (true) {+x;}

Vários idiomas (por exemplo, Python) permitem evitar parênteses, mas ainda têm um marcador de condição final:

if True: +x

No entanto, você está certo de que nós poderíamos definir um idioma onde os parênteses nunca são necessários: uma linguagem em que uma expressão é não uma declaração válida não terá esse problema.

Infelizmente, isso significa que coisas como:

 ++x;
 functionCall(1,2,3);

seria não instruções válidas, então você teria que introduzir uma sintaxe estranha para poder executar tais ações sem criar expressões. Uma maneira simples de fazer isso é simplesmente preceder a expressão por um marcador como [statement] :

[statement] ++x;
[statement] functionCall(1,2,3);

Agora, a ambigüidade desaparece, pois você teria que escrever:

if true
    [statement] ++x;

Mas, como você pode ver, não vejo essa linguagem ser difundida, pois colocar os parênteses em torno de uma if -condition (ou um : em seu final) é muito melhor, colocando esse marcador para cada expressão declaração.

Observação : o uso de um marcador [statement] é a sintaxe mais simples que eu pude imaginar. No entanto, você poderia ter duas sintaxes completamente distintas para expressões e instruções sem ambigüidade entre elas, o que não exigiria tal marcador. O problema é: a linguagem seria extremamente estranha, já que para fazer as mesmas coisas em uma expressão ou declaração, você teria que usar uma sintaxe completamente diferente.

Uma coisa que vem à mente para ter duas sintaxes separadas sem um marcador tão explícito seria, por exemplo: forçar instruções a usar símbolos unicode (então, em vez de for você usaria alguma variação unicode das letras f , o e r ), enquanto as expressões são apenas ASCII.

    
por 07.11.2016 / 10:49
fonte
10

É comum que os idiomas da família C exijam esses parênteses, mas não universais.

Uma das alterações sintáticas mais notáveis do Perl 6 é que eles modificaram a gramática para que você não precise fornecer aos parênteses as condições if , for e as declarações similares. Então, algo assim é perfeitamente válido em Perl 6:

if $x == 4 {
    ...
}

como é

while $queue.pop {
    ...
}

No entanto, como são apenas expressões, é possível colocar parênteses em torno deles, se desejar, caso em que são apenas agrupamentos comuns, em vez de uma parte obrigatória da sintaxe, como em C, C #, Java etc.

O Rust tem uma sintaxe semelhante ao Perl 6 neste departamento:

if x == 4 {
    ...
}

Parece-me que uma característica das linguagens mais modernas inspiradas em C é observar coisas desse tipo e pensar em removê-las.

    
por 07.11.2016 / 09:49
fonte
6

Há um aspecto que surpreende que nenhuma das respostas existentes tenha trazido.

C, e muitos derivados C e similares, tem uma peculiaridade em que o valor de uma atribuição é o valor atribuído. Uma conseqüência disso é que uma atribuição pode ser usada onde um valor é esperado.

Isso permite que você escreva coisas como

if (x = getValue() == 42) { ... }

ou

if (x == y = 47) { ... }

ou

unsigned int n = 0 /* given m == SOME_VALUE */;
while (n < m && *p1++ = *p2++) { n++; }

(que é implicitamente tratado como while (n < m && *p1++ = *p2++ != 0) { n++; } porque C trata não-zero como verdadeiro; aliás, acho que é apenas sobre strncpy () na biblioteca padrão C)

ou até mesmo

if (x = 17);

e tudo é válido. Nem todas as combinações sintaticamente válidas são necessariamente úteis (e os compiladores modernos avisam especificamente sobre atribuições dentro de condicionais, porque é um erro comum), mas algumas delas são realmente úteis .

Analisar tais declarações provavelmente seria muito mais difícil se não houvesse uma maneira inequívoca de determinar onde a expressão condicional começa e termina.

Os parênteses já eram usados para delimitar os nomes de função dos argumentos de função, então acho que eles pareciam uma escolha natural também para delimitar palavras-chave de argumentos de palavra-chave.

Claro, sintaxes alternativas podem ser definidas para fazer a mesma coisa. Mas isso aumentaria a complexidade, particularmente no analisador, que precisaria então lidar com dois conjuntos diferentes de sintaxe para basicamente a mesma coisa. Quando o C estava sendo projetado, o poder de computação (tanto em termos de capacidade de processamento de números, memória de trabalho e capacidade de armazenamento) era extremamente limitado; qualquer coisa que reduzisse a complexidade com pouco ou nenhum custo para a legibilidade era quase certamente uma mudança bem-vinda.

O uso de parênteses pode parecer um pouco arcaico hoje em dia, mas não é assim, dado a alguém com alguma familiaridade com a linguagem, prejudica a legibilidade em comparação com alguma outra sintaxe que seja capaz de expressar as mesmas coisas.

    
por 08.11.2016 / 11:40
fonte
5

O motivo é principalmente histórico.

No momento em que o primeiro compilador C foi escrito, os computadores têm ram, cpu e compiladores muito limitados, onde são escritos “manualmente” com poucas ferramentas para ajudar os criadores de compiladores. Portanto, regras complexas eram caras para serem implementadas em um compilador. C ++, C #, Java, etc foram todos projetados para ser fácil para os programadores C aprenderem, portanto, não houve mudanças desnecessárias.

Em condicionais de idiomas 'c like' ( if, while, etc ) não exigem um código block explícito, você pode simplesmente usar uma instrução simples.

if (a == d) doIt()

ou você pode combinar as instruções juntas em um compound statement , colocando-as em {}

Gostamos que o compilador encontre o erro que cometemos e forneça uma mensagem de erro que possamos entender.

    
por 08.11.2016 / 14:34
fonte
3

Java e C ++ foram desenvolvidos depois que o C se tornou uma linguagem de programação muito popular. Uma consideração no design de cada uma dessas línguas foi que seria atraente para programadores C e woo esses programadores para usar o novo idioma. (Eu era um dos programadores de C que eles cortejaram com sucesso.) O C ++ também foi projetado para ser (quase) intercambiável com o código C. Para suportar esses objetivos, tanto o C ++ quanto o Java adotaram grande parte Sintaxe de C, incluindo os parênteses em torno das condições das instruções if , while e switch .

Daí a razão pela qual todos esses idiomas exigem parênteses ao redor as condições dessas declarações é porque C faz, e a questão é realmente apenas porque o C requer esses parênteses.

As origens da linguagem C são descritas em este artigo de Dennis Ritchie, um dos principais autores do seu desenvolvimento (alguns podem até dizer o autor principal de seu desenvolvimento). Como dito naquele artigo, C foi originalmente desenvolvido em início dos anos 1970 como uma linguagem de programação de sistema para computadores com espaço extremamente limitado na memória principal. Era desejável ter uma linguagem que fosse de nível superior a linguagem assembly, mas dado os recursos disponíveis para trabalhar, a facilidade de analisar a linguagem também foi importante. A exigência dos parênteses tornaria relativamente fácil identificar o código condicional.

Também é possível inferir que a capacidade de gravar programas usando menos caracteres foi considerada uma vantagem, e dois parênteses ocupam menos espaço do que a palavra-chave THEN que foi usada em FORTRAN e outras linguagens de alto nível naquele momento; na verdade, como os parênteses também podiam substituir espaços como delimitadores de símbolos, if(a==b) era quatro caracteres inteiros menores que IF a==b THEN .

Em qualquer caso, algum equilíbrio teve que ser alcançado entre quão facilmente humano seres seriam capazes de ler, escrever e entender programas escritos em C, com que facilidade um compilador poderia analisar e compilar programas escritos em C, e quantos kilobytes (!) seriam necessários tanto para o programa fonte e o próprio compilador. E parênteses em torno das condições de if , while e switch declarações foi como as pessoas optaram por atingir esse equilíbrio no projeto de C.

Como evidenciado em várias outras respostas, uma vez que você tira o circunstâncias particulares sob as quais C foi desenvolvido, todos os tipos de formas alternativas de sintaxe foram usadas para os condicionais de várias linguagens de programação. Então os parênteses realmente se resumem a uma decisão de design que foi feita por algumas pessoas sob certas restrições em um determinado momento da história.

    
por 08.11.2016 / 04:34
fonte
3

Muitos aqui argumentam que sem os parênteses a sintaxe seria ambígua e implicaria silenciosamente que isso seria de alguma forma uma situação ruim ou até mesmo impossível.

Na verdade, as linguagens têm muitas maneiras de lidar com as ambiguidades. A precedência do operador é apenas uma instância deste tópico.

Não, a ambigüidade não é o motivo dos parênteses. Eu acho que alguém poderia simplesmente criar uma versão de C que não requer os parênteses em torno da condição (tornando-os assim opcionais) e que ainda cria um código válido em todos os casos. O exemplo de if a ++ b; poderia ser interpretado como sendo equivalente a if (a) ++b; ou if (a++) b; , o que parecer mais apropriado.

A questão de por que Dennis Ritchie escolheu tornar o () obrigatório (e, portanto, cunhar este meme para muitas línguas derivadas) é bastante lingüístico. Eu acho que a noção de afirmar claramente que a condição é uma expressão em vez de um comando foi o pai do pensamento.

E, de fato, o C foi projetado para ser um analisável usando um analisador de uma passagem. Usar uma sintaxe com parênteses obrigatórios em torno da condição suporta esse aspecto.

    
por 09.11.2016 / 17:59
fonte
0

Parênteses em torno de if condições não são necessários em Fortran, Cobol, PL / 1, Algol, Algo-68, Pascal, Modula, XPL, PL / M, MPL, ... ou qualquer outra linguagem que possua um then keyword. then serve para delimitar o condition do seguinte statement .

O parêntese de fechamento em C etc. funciona como then , e o de abertura é formalmente redundante.

As observações acima aplicam-se a idiomas tradicionalmente analisados.

    
por 08.11.2016 / 01:06
fonte