Estou perguntando em relação ao c #, mas presumo que seja o mesmo na maioria dos outros idiomas.
Alguém tem uma boa definição de expressões e declarações e quais são as diferenças?
Estou perguntando em relação ao c #, mas presumo que seja o mesmo na maioria dos outros idiomas.
Alguém tem uma boa definição de expressões e declarações e quais são as diferenças?
Respostas:
Expressão: Algo que avalia um valor. Exemplo: 1 + 2 / x
Instrução: Uma linha de código que faz alguma coisa. Exemplo: GOTO 100
Nas primeiras linguagens de programação de uso geral, como FORTRAN, a distinção era clara. No FORTRAN, uma declaração era uma unidade de execução, algo que você fez. A única razão pela qual não foi chamada de "linha" foi porque, às vezes, abrangia várias linhas. Uma expressão por si só não poderia fazer nada ... você tinha que atribuí-la a uma variável.
1 + 2 / X
é um erro no FORTRAN, porque não faz nada. Você tinha que fazer algo com essa expressão:
X = 1 + 2 / X
FORTRAN não tinha uma gramática como a conhecemos hoje - essa idéia foi inventada, juntamente com a Forma Backus-Naur (BNF), como parte da definição de Algol-60. Nesse ponto, a distinção semântica ("ter um valor" versus "fazer alguma coisa") foi consagrada na sintaxe : um tipo de frase era uma expressão e outro era uma afirmação, e o analisador podia diferenciá-los.
Designers de línguas posteriores embaçaram a distinção: permitiram que expressões sintáticas fizessem coisas e permitiram declarações sintáticas que tinham valores. O exemplo mais antigo de linguagem popular que ainda sobrevive é C. Os projetistas de C perceberam que nenhum dano era causado se você pudesse avaliar uma expressão e jogar fora o resultado. Em C, toda expressão sintática pode ser transformada em uma declaração apenas com um ponto e vírgula no final:
1 + 2 / x;
é uma afirmação totalmente legítima, embora absolutamente nada aconteça. Da mesma forma, em C, uma expressão pode ter efeitos colaterais - pode mudar alguma coisa.
1 + 2 / callfunc(12);
porque callfunc
pode fazer algo útil.
Depois de permitir que qualquer expressão seja uma instrução, você também pode permitir o operador de atribuição (=) dentro das expressões. É por isso que C permite que você faça coisas como
callfunc(x = 2);
Isso avalia a expressão x = 2 (atribuindo o valor de 2 a x) e depois passa esse (o 2) para a função callfunc
.
Esse desfoque de expressões e instruções ocorre em todos os derivados C (C, C ++, C # e Java), que ainda possuem algumas instruções (como while
), mas permitem que quase qualquer expressão seja usada como uma instrução (na atribuição somente em C #, expressões de chamada, incremento e decremento podem ser usadas como declarações; consulte a resposta de Scott Wisniewski ).
Ter duas "categorias sintáticas" (que é o nome técnico para o tipo de declaração e expressão de coisa) pode levar à duplicação de esforços. Por exemplo, C tem duas formas de condicional, o formulário de declaração
if (E) S1; else S2;
e a forma de expressão
E ? E1 : E2
E, às vezes, as pessoas querem duplicação que não existe: no padrão C, por exemplo, apenas uma instrução pode declarar uma nova variável local - mas essa capacidade é útil o suficiente para que o compilador GNU C forneça uma extensão GNU que permita que uma expressão declare uma variável local. variável local também.
Os designers de outras linguagens não gostaram desse tipo de duplicação e viram desde o início que, se expressões podem ter efeitos colaterais e valores, a distinção sintática entre declarações e expressões não é tão útil - então elas se livraram dela. . Haskell, Icon, Lisp e ML são todos os idiomas que não têm declarações sintáticas - eles só têm expressões. Até o loop estruturado de classe e as formas condicionais são considerados expressões e têm valores - mas não muito interessantes.
callfunc(x = 2);
passa x
para callfunc
, não 2
. Se x
for um flutuador, callfunc(float)
será chamado, não callfunc(int)
. E em C ++, se você passa x=y
para func
, func
pega uma referência e a altera, ela muda x
, não y
.
where
saber por que a cláusula em haskell é considerada uma expressão e não uma declaração. learnyouahaskell.com/syntax-in-functions#where
where
é realmente uma parte da declaração de função, não expressão ou declaração.
Observe que em C, "=" é realmente um operador, que faz duas coisas:
Aqui está um extrato da gramática ANSI C. Você pode ver que C não possui muitos tipos diferentes de instruções ... a maioria das instruções em um programa são instruções de expressão, ou seja, uma expressão com ponto e vírgula no final.
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
Uma expressão é algo que retorna um valor, enquanto uma declaração não.
Por exemplo:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
O grande problema entre os dois é que você pode encadear expressões, enquanto as instruções não podem ser encadeadas.
foo.voidFunc(1);
é uma expressão com um valor nulo. while
e if
são declarações.
return
é considerado uma subestação.
Você pode encontrar isso na wikipedia , mas as expressões são avaliadas com algum valor, enquanto as instruções não têm valor avaliado.
Assim, expressões podem ser usadas em declarações, mas não o contrário.
Observe que algumas linguagens (como Lisp, e eu acredito que Ruby, e muitas outras) não diferenciam declaração x expressão ... nessas linguagens, tudo é uma expressão e pode ser encadeado com outras expressões.
Para uma explicação das diferenças importantes na composibilidade (encadeamento) de expressões versus declarações, minha referência favorita é o artigo do prêmio Turing de John Backus: A programação pode ser libertada do estilo de von Neumann? .
Linguagens imperativas (Fortran, C, Java, ...) enfatizam instruções para estruturar programas e têm expressões como uma espécie de reflexão tardia. Linguagens funcionais enfatizam expressões. Linguagens puramente funcionais têm expressões tão poderosas que as declarações podem ser completamente eliminadas.
Expressões podem ser avaliadas para obter um valor, enquanto que as instruções não retornam um valor (são do tipo nulo ).
Expressões de chamada de função também podem ser consideradas instruções, é claro, mas, a menos que o ambiente de execução possua uma variável interna especial para armazenar o valor retornado, não há como recuperá-lo.
Linguagens orientadas a instruções exigem que todos os procedimentos sejam uma lista de instruções. Linguagens orientadas a expressões, que provavelmente são todas linguagens funcionais, são listas de expressões ou, no caso do LISP, uma expressão S longa que representa uma lista de expressões.
Embora os dois tipos possam ser compostos, a maioria das expressões pode ser composta arbitrariamente, desde que os tipos correspondam. Cada tipo de declaração tem sua própria maneira de compor outras declarações, se elas puderem fazer tudo isso. As instruções foreach e if requerem uma única declaração ou que todas as instruções subordinadas sejam inseridas em um bloco de instruções, uma após a outra, a menos que as subestações tenham permitido suas próprias subestações.
Instruções também podem incluir expressões, onde uma expressão realmente não inclui nenhuma instrução. Uma exceção, no entanto, seria uma expressão lambda, que representa uma função, e portanto pode incluir qualquer coisa que uma função possa incluir, a menos que a linguagem permita apenas lambdas limitadas, como as lambdas de expressão única do Python.
Em uma linguagem baseada em expressão, tudo o que você precisa é de uma única expressão para uma função, pois todas as estruturas de controle retornam um valor (muitas delas retornam NIL). Não há necessidade de uma declaração de retorno, pois a última expressão avaliada na função é o valor de retorno.
Void
não é o tipo inferior. Veja minha resposta .
null
)? Não void
seria mais parecido com o tipo de unidade (mas com seu valor único inacessível)?
void
é o tipo de retorno de uma função que nunca retorna (por exemplo, uma função que throw
é um erro), é o tipo inferior . Caso contrário, void
é o tipo de unidade . Você está certo de que uma declaração que não pode divergir tem o tipo de unidade. Mas uma afirmação que pode divergir é o tipo inferior. Devido ao teorema da parada, geralmente não podemos provar que uma função não diverge, então acho que a unidade é ficção. O tipo de fundo não pode ter um valor, por isso não pode ter um único valor null
.
null
valor é realmente um pseudo-valor que denota que uma referência se refere a algo que não existe.
Simplesmente: uma expressão é avaliada como um valor, uma declaração não.
{}
é uma afirmação. Colocar a palavra entre aspas não muda isso. Declarações são construções sintáticas com semântica. Não existe "camada semântica" - você parece estar se referindo à execução . Você diz que está tentando ser preciso, mas falhou nisso. Sua queixa sobre "a ignorância dos que rejeitam os votos" é pura ad hominem; você não tem informações sobre os estados mentais dos que recusam.
{}
é definido como uma declaração na especificação de linguagem C #.
Algumas coisas sobre linguagens baseadas em expressões:
Mais importante: tudo retorna um valor
Não há diferença entre colchetes e chaves para delimitar blocos e expressões de código, pois tudo é uma expressão. Isso não impede o escopo lexical: Uma variável local pode ser definida para a expressão na qual sua definição está contida e todas as instruções contidas nela, por exemplo.
Em uma linguagem baseada em expressões, tudo retorna um valor. Isso pode ser um pouco estranho a princípio - O que (FOR i = 1 TO 10 DO (print i))
retorna?
Alguns exemplos simples:
(1)
retorna 1
(1 + 1)
retorna 2
(1 == 1)
retorna TRUE
(1 == 2)
retorna FALSE
(IF 1 == 1 THEN 10 ELSE 5)
retorna 10
(IF 1 == 2 THEN 10 ELSE 5)
retorna 5
Alguns exemplos mais complexos:
OpenADoor(), FlushTheToilet()
ou TwiddleYourThumbs()
retornar algum tipo de valor mundano, como OK, Concluído ou Sucesso.(FOR i = 1 TO 10 DO (print i))
, o valor do loop for é "10", faz com que a (print i)
expressão seja avaliada 10 vezes, sempre retornando i como uma string. O tempo final através de retornos 10
, nossa resposta finalGeralmente, é necessária uma ligeira mudança de mentalidade para tirar o máximo proveito de uma linguagem baseada em expressões, pois o fato de tudo ser uma expressão torna possível 'alinhar' muitas coisas
Como um exemplo rápido:
FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO ( LotsOfCode )
é um substituto perfeitamente válido para os não baseados em expressão
IF MyString == "Hello, World!" THEN TempVar = 10 ELSE TempVar = 5 FOR i = 1 TO TempVar DO ( LotsOfCode )
Em alguns casos, o layout que o código baseado em expressão permite parece muito mais natural para mim
Claro, isso pode levar à loucura. Como parte de um projeto de hobby em uma linguagem de script baseada em expressão chamada MaxScript, eu consegui criar essa linha monstruosa
IF FindSectionStart "rigidifiers" != 0 THEN FOR i = 1 TO (local rigidifier_array = (FOR i = (local NodeStart = FindsectionStart "rigidifiers" + 1) TO (FindSectionEnd(NodeStart) - 1) collect full_array[i])).count DO
(
LotsOfCode
)
Uma declaração é um caso especial de uma expressão, uma com void
tipo. A tendência das linguagens em tratar declarações de maneira diferente geralmente causa problemas, e seria melhor se elas fossem generalizadas adequadamente.
Por exemplo, em C #, temos o Func<T1, T2, T3, TResult>
conjunto sobrecarregado muito útil de delegados genéricos. Mas também temos que ter um Action<T1, T2, T3>
conjunto correspondente , e a programação de ordem superior de propósito geral constantemente deve ser duplicada para lidar com essa infeliz bifurcação.
Exemplo trivial - uma função que verifica se uma referência é nula antes de chamar outra função:
TResult IfNotNull<TValue, TResult>(TValue value, Func<TValue, TResult> func)
where TValue : class
{
return (value == null) ? default(TValue) : func(value);
}
O compilador poderia lidar com a possibilidade de TResult
ser void
? Sim. Tudo o que precisa fazer é exigir que o retorno seja seguido por uma expressão do tipo void
. O resultado de default(void)
seria do tipo void
, e a função que está sendo transmitida precisaria ter o formato Func<TValue, void>
(que seria equivalente a Action<TValue>
).
Várias outras respostas sugerem que você não pode encadear declarações como as expressões, mas não tenho certeza de onde essa idéia vem. Podemos pensar no ;
que aparece depois das instruções como um operador de infixo binário, pegando duas expressões do tipo void
e combinando-as em uma única expressão do tipo void
.
Declarações -> Instruções para seguir Sequencialmente
Expressões -> Avaliação que retorna um valor
As instruções são basicamente como etapas ou instruções em um algoritmo, o resultado da execução de uma instrução é a atualização do ponteiro de instruções (chamado de assembler)
Expressões não implicam ordem de execução à primeira vista; seu objetivo é avaliar e retornar um valor. Nas linguagens de programação imperativas, a avaliação de uma expressão tem uma ordem, mas é apenas por causa do modelo imperativo, mas não é sua essência.
Exemplos de declarações:
for
goto
return
if
(todos eles implicam o avanço da linha (declaração) de execução para outra linha)
Exemplo de expressões:
2+2
(não implica a ideia de execução, mas de avaliação)
Uma declaração é um bloco de construção procedural a partir do qual todos os programas C # são construídos. Uma instrução pode declarar uma variável ou constante local, chamar um método, criar um objeto ou atribuir um valor a uma variável, propriedade ou campo.
Uma série de instruções cercadas por chaves forma um bloco de código. Um corpo de método é um exemplo de bloco de código.
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
Instruções em C # geralmente contêm expressões. Uma expressão em C # é um fragmento de código que contém um valor literal, um nome simples ou um operador e seus operandos.
Uma expressão é um fragmento de código que pode ser avaliado em um único valor, objeto, método ou espaço para nome. Os dois tipos mais simples de expressões são literais e nomes simples. Um literal é um valor constante que não tem nome.
int i = 5;
string s = "Hello World";
I e s são nomes simples que identificam variáveis locais. Quando essas variáveis são usadas em uma expressão, o valor da variável é recuperado e usado para a expressão.
if(number >= 0) return true; else return false;
ou ainda melhor bool? IsPositive(int number) { if(number > 0) return true; else if(number < 0) return false; else return null;}
:)
Prefiro o significado statement
no sentido lógico formal da palavra. É aquele que altera o estado de uma ou mais das variáveis na computação, permitindo que uma declaração verdadeira ou falsa seja feita sobre seus valores.
Eu acho que sempre haverá confusão no mundo da computação e na ciência em geral quando novas terminologias ou palavras forem introduzidas, as palavras existentes serão "reaproveitadas" ou os usuários ignorarão a terminologia existente, estabelecida ou "apropriada" para o que estão descrevendo
Não estou realmente satisfeito com nenhuma das respostas aqui. Eu olhei para a gramática do C ++ (ISO 2008) . No entanto, talvez por uma questão de didática e programação, as respostas sejam suficientes para distinguir os dois elementos (embora a realidade pareça mais complicada).
Uma declaração consiste em zero ou mais expressões, mas também pode ser outros conceitos de linguagem. Este é o formulário Extended Backus Naur para a gramática (trecho de instrução):
statement:
labeled-statement
expression-statement <-- can be zero or more expressions
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
Podemos ver os outros conceitos que são considerados declarações em C ++.
case
por exemplo, é uma declaração rotuladaif
if/else
,case
while
, do...while
,for (...)
break
, continue
, return
(pode retornar expressão),goto
try/catch
blocosEste é um trecho que mostra a parte de expressões:
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
+
, -
, *
, /
, &
, |
, &&
, ||
, ...)throw
cláusula também é uma expressãoDeclarações são frases gramaticalmente completas. Expressões não são. Por exemplo
x = 5
lê como "x recebe 5." Esta é uma frase completa. O código
(x + 5)/9.0
lê, "x mais 5 todos divididos por 9,0". Esta não é uma frase completa. A declaração
while k < 10:
print k
k += 1
é uma frase completa. Observe que o cabeçalho do loop não é; "enquanto k <10", é uma cláusula subordinada.
while
é uma expressão em alguns idiomas como o Scala. Você está confundindo gramática com digitação. Veja minha resposta .
while
com um corpo ainda é uma expressão em Scala. Também pode ser uma afirmação, se criar efeitos colaterais, o que minha resposta com muita votação reduzida permite (uma expressão também pode ser uma afirmação). Minha resposta é a única correta. Desculpe a todos os leitores que não conseguem entender.
(x + 5)/9.0
definitivamente pode ficar sozinho como uma declaração. Além disso, se por conclusão gramatical, você quer dizer um programa válido, C não permite que as instruções sejam independentes como um único programa.
Aqui está o verão de uma das respostas mais simples que encontrei.
originalmente Respondido por Anders Kaseorg
Uma instrução é uma linha completa de código que executa alguma ação, enquanto uma expressão é qualquer seção do código que é avaliada como um valor.
As expressões podem ser combinadas “horizontalmente” em expressões maiores usando operadores, enquanto as instruções podem ser combinadas “verticalmente” escrevendo uma após a outra ou com construções de bloco.
Toda expressão pode ser usada como uma instrução (cujo efeito é avaliar a expressão e ignorar o valor resultante), mas a maioria das instruções não pode ser usada como expressões.
A base de fato desses conceitos é:
Expressões : uma categoria sintática cuja instância pode ser avaliada para um valor.
Instrução : Uma categoria sintática cuja instância pode estar envolvida nas avaliações de uma expressão e o valor resultante da avaliação (se houver) não está garantida disponível.
Além do contexto inicial do FORTRAN nas primeiras décadas, as definições de expressões e afirmações na resposta aceita estão obviamente erradas:
sizeof
operador nunca é avaliada.(BTW, eu quero acrescentar [a citação necessária] a essa resposta sobre materiais sobre C, porque não me lembro se o DMR tem essas opiniões. Parece que não, caso contrário, não deve haver razões para preservar a duplicação de funcionalidade no design de C : notavelmente, o operador de vírgula x as instruções.)
(A lógica a seguir não é a resposta direta à pergunta original, mas acho necessário esclarecer algo que já foi respondido aqui.)
No entanto, é duvidoso que necessitemos de uma categoria específica de "declarações" em linguagens de programação de uso geral:
begin
no Esquema) ou por açúcar sintático de estruturas monádicas.++i + ++i
não faz sentido em C.)Então, por que declarações? Enfim, a história já está uma bagunça. Parece que a maioria dos designers de idiomas não faz a escolha com cuidado.
Pior ainda, fornece a alguns entusiastas do sistema de tipos (que não estão familiarizados o suficiente com a história do PL) alguns conceitos errôneos de que os sistemas de tipos devem ter coisas importantes a ver com os projetos de regras mais essenciais na semântica operacional.
Sério, o raciocínio dependendo dos tipos não é tão ruim em muitos casos, mas particularmente não é construtivo neste caso especial. Até especialistas podem estragar tudo.
Por exemplo, alguém enfatiza a natureza bem digitada como o argumento central contra o tratamento tradicional de continuações não limitadas . Embora a conclusão seja razoavelmente razoável e os insights sobre funções compostas sejam bons ( mas ainda muito ingênuos para a essência ), esse argumento não é sólido porque ignora totalmente a abordagem de "canal lateral" na prática como _Noreturn any_of_returnable_types
(em C11) para codificar Falsum
. E, estritamente falando, uma máquina abstrata com estado imprevisível não é idêntica a "um computador travado".
Em uma linguagem de programação orientada a instruções, um bloco de código é definido como uma lista de instruções. Em outras palavras, uma instrução é uma parte da sintaxe que você pode colocar dentro de um bloco de código sem causar um erro de sintaxe.
A Wikipedia define a declaração de palavras da mesma forma
Na programação de computadores, uma declaração é uma unidade sintática de uma linguagem de programação imperativa que expressa alguma ação a ser realizada. Um programa escrito nesse idioma é formado por uma sequência de uma ou mais instruções
Observe a última declaração. (embora "um programa" neste caso esteja tecnicamente errado, porque C e Java rejeitam um programa que não consiste em nada de instruções.)
Wikipedia define a palavra expressão como
Uma expressão em uma linguagem de programação é uma entidade sintática que pode ser avaliada para determinar seu valor
Isso é, no entanto, falso, porque em Kotlin, throw new Exception("")
é uma expressão, mas quando avaliado, ele simplesmente gera uma exceção, nunca retornando valor algum.
Em uma linguagem de programação estaticamente tipada, toda expressão tem um tipo. Essa definição, no entanto, não funciona em uma linguagem de programação digitada dinamicamente.
Pessoalmente, defino uma expressão como uma parte da sintaxe que pode ser composta por um operador ou função para gerar uma expressão maior. Na verdade, isso é semelhante à explicação da expressão da Wikipedia:
É uma combinação de uma ou mais constantes, variáveis, funções e operadores que a linguagem de programação interpreta (de acordo com suas regras particulares de precedência e associação) e calcula para produzir ("retornar", em um ambiente com estado) outro valor
Mas, o problema está na linguagem de programação C, dada a função executeAlgo como isto:
void executeSomething(void){
return;
}
É executeSomething()
uma expressão ou é uma afirmação? De acordo com minha definição, é uma declaração porque, conforme definido na gramática de referência C da Microsoft,
Você não pode usar o valor (inexistente) de uma expressão que tenha o tipo void de forma alguma, nem converter uma expressão void (por conversão implícita ou explícita) em qualquer tipo, exceto void
Mas a mesma página indica claramente que essa sintaxe é uma expressão.
Para aprimorar e validar minha resposta anterior, as definições dos termos da linguagem de programação devem ser explicadas a partir da teoria dos tipos de ciência da computação, quando aplicável.
Uma expressão tem um tipo diferente do tipo Bottom, ou seja, tem um valor. Uma instrução tem o tipo Unidade ou Parte inferior.
A partir disso, segue-se que uma instrução só pode ter efeito em um programa quando cria um efeito colateral, porque ela não pode retornar um valor ou apenas retorna o valor do tipo de Unidade que não é atribuível (em alguns idiomas, como um C void
) ou (como em Scala) podem ser armazenados para uma avaliação atrasada da declaração.
Obviamente, a @pragma
ou a /*comment*/
não têm tipo e, portanto, são diferenciadas de declarações. Portanto, o único tipo de declaração que não teria efeitos colaterais seria uma não operação. A não operação é útil apenas como espaço reservado para futuros efeitos colaterais. Qualquer outra ação devido a uma declaração seria um efeito colateral. Novamente, uma dica do compilador, por exemplo @pragma
, não é uma declaração porque não tem tipo.
@pragma
ou /*comment*/
são logicamente inconsistentes.
Mais precisamente, uma declaração deve ter um "efeito colateral" (isto é, ser imperativo ) e uma expressão deve ter um tipo de valor (isto é, não o tipo inferior).
O tipo de uma declaração é o tipo de unidade, mas devido ao teorema de Halting a unidade é ficção, digamos que seja o tipo inferior .
Void
não é exatamente o tipo inferior (não é o subtipo de todos os tipos possíveis). Existe em idiomas que não possuem um sistema de tipos completamente sonoros . Pode parecer uma afirmação esnobe, mas completa , como anotações de variação é fundamental para a criação de software extensível.
Vamos ver o que a Wikipedia tem a dizer sobre esse assunto.
https://en.wikipedia.org/wiki/Statement_(computer_science)
Na programação de computadores, uma declaração é o menor elemento independente de uma linguagem de programação imperativa que expressa alguma ação a ser realizada.
Muitos idiomas (por exemplo, C) fazem uma distinção entre instruções e definições, com uma instrução contendo apenas código executável e uma definição declarando um identificador, enquanto uma expressão é avaliada apenas como um valor.
pass
é uma declaração. É um não-op, e não avalia a nada.