Pregunto con respecto a c #, pero supongo que es lo mismo en la mayoría de los otros idiomas.
¿Alguien tiene una buena definición de expresiones y declaraciones y cuáles son las diferencias?
Pregunto con respecto a c #, pero supongo que es lo mismo en la mayoría de los otros idiomas.
¿Alguien tiene una buena definición de expresiones y declaraciones y cuáles son las diferencias?
Respuestas:
Expresión: algo que se evalúa como un valor. Ejemplo: 1 + 2 / x
Declaración: Una línea de código que hace algo. Ejemplo: GOTO 100
En los primeros lenguajes de programación de propósito general, como FORTRAN, la distinción era muy clara. En FORTRAN, una declaración era una unidad de ejecución, algo que usted hizo. La única razón por la que no se llamaba "línea" era porque a veces abarcaba varias líneas. Una expresión por sí sola no podía hacer nada ... tenía que asignarla a una variable.
1 + 2 / X
es un error en FORTRAN, porque no hace nada. Tenías que hacer algo con esa expresión:
X = 1 + 2 / X
FORTRAN no tenía una gramática tal como la conocemos hoy en día, esa idea fue inventada, junto con Backus-Naur Form (BNF), como parte de la definición de Algol-60. En ese momento, la distinción semántica ("tener un valor" versus "hacer algo") estaba consagrada en la sintaxis : un tipo de frase era una expresión, y otra era una declaración, y el analizador podía distinguirlas.
Los diseñadores de lenguajes posteriores desdibujaron la distinción: permitieron que las expresiones sintácticas hicieran cosas, y permitieron declaraciones sintácticas que tenían valores. El primer ejemplo de lenguaje popular que aún sobrevive es C. Los diseñadores de C se dieron cuenta de que no se hacía daño si se le permitía evaluar una expresión y desechar el resultado. En C, cada expresión sintáctica se puede convertir en una declaración simplemente agregando un punto y coma a lo largo del final:
1 + 2 / x;
Es una declaración totalmente legítima, aunque absolutamente nada sucederá. De manera similar, en C, una expresión puede tener efectos secundarios: puede cambiar algo.
1 + 2 / callfunc(12);
porque callfunc
podría hacer algo útil
Una vez que permita que cualquier expresión sea una declaración, también podría permitir el operador de asignación (=) dentro de las expresiones. Es por eso que C te permite hacer cosas como
callfunc(x = 2);
Esto evalúa la expresión x = 2 (asignando el valor de 2 a x) y luego pasa eso (el 2) a la función callfunc
.
Este desenfoque de expresiones y declaraciones ocurre en todos los derivados C (C, C ++, C # y Java), que todavía tienen algunas declaraciones (como while
) pero que permiten que casi cualquier expresión se use como una declaración (en la asignación de C # solamente, las expresiones call, increment y decrement pueden usarse como declaraciones; ver la respuesta de Scott Wisniewski ).
Tener dos "categorías sintácticas" (que es el nombre técnico para el tipo de cosas que son las declaraciones y expresiones) puede conducir a la duplicación de esfuerzos. Por ejemplo, C tiene dos formas de condicional, la forma de declaración
if (E) S1; else S2;
y la forma de expresión
E ? E1 : E2
Y a veces la gente quiere una duplicación que no existe: en el estándar C, por ejemplo, solo una declaración puede declarar una nueva variable local, pero esta capacidad es lo suficientemente útil como para que el compilador GNU C proporcione una extensión GNU que permite que una expresión declare un variable local también.
A los diseñadores de otros lenguajes no les gustó este tipo de duplicación, y vieron desde el principio que si las expresiones pueden tener efectos secundarios y valores, entonces la distinción sintáctica entre declaraciones y expresiones no es tan útil, por lo que se deshicieron de ella. . Haskell, Icon, Lisp y ML son todos lenguajes que no tienen enunciados sintácticos, solo tienen expresiones. Incluso los bucles estructurados de clase y las formas condicionales se consideran expresiones y tienen valores, pero no muy interesantes.
callfunc(x = 2);
pasa x
a callfunc
, no 2
. Si x
es un flotador, callfunc(float)
se llamará, no callfunc(int)
. Y en C ++, si pasa x=y
a func
, y func
toma una referencia y la cambia, cambia x
, no y
.
where
cláusula en Haskell se considera una expresión y no una declaración. learnyouahaskell.com/syntax-in-functions#where
where
es en realidad una parte de la declaración de función, no una expresión o declaración.
Tenga en cuenta que en C, "=" es en realidad un operador, que hace dos cosas:
Aquí hay un extracto de la gramática ANSI C. Puede ver que C no tiene muchos tipos diferentes de declaraciones ... la mayoría de las declaraciones en un programa son declaraciones de expresión, es decir, una expresión con un punto y coma al final.
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
Una expresión es algo que devuelve un valor, mientras que una declaración no lo hace.
Por ejemplo:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
El gran problema entre los dos es que puedes encadenar expresiones, mientras que las declaraciones no se pueden encadenar.
foo.voidFunc(1);
es una expresión con un valor nulo. while
y if
son declaraciones.
return
se considera una subestimación.
Puede encontrar esto en wikipedia , pero las expresiones se evalúan con algún valor, mientras que las declaraciones no tienen valor evaluado.
Por lo tanto, las expresiones se pueden usar en declaraciones, pero no al revés.
Tenga en cuenta que algunos lenguajes (como Lisp, y creo que Ruby, y muchos otros) no diferencian la declaración frente a la expresión ... en tales lenguajes, todo es una expresión y puede encadenarse con otras expresiones.
Para una explicación de las diferencias importantes en la componibilidad (posibilidad de encadenamiento) de las expresiones frente a las declaraciones, mi referencia favorita es el artículo del premio Turing de John Backus, ¿Se puede liberar la programación del estilo de von Neumann? .
Los lenguajes imperativos (Fortran, C, Java, ...) enfatizan las declaraciones para estructurar programas y tienen expresiones como una especie de pensamiento posterior. Los lenguajes funcionales enfatizan las expresiones. Los lenguajes puramente funcionales tienen expresiones tan poderosas que las declaraciones pueden eliminarse por completo.
Las expresiones se pueden evaluar para obtener un valor, mientras que las declaraciones no devuelven un valor (son de tipo nulo ).
Las expresiones de llamada a funciones también pueden considerarse declaraciones, por supuesto, pero a menos que el entorno de ejecución tenga una variable incorporada especial para contener el valor devuelto, no hay forma de recuperarlo.
Los lenguajes orientados a declaraciones requieren que todos los procedimientos sean una lista de declaraciones. Los lenguajes orientados a la expresión, que probablemente sean todos lenguajes funcionales, son listas de expresiones o, en el caso de LISP, una expresión S larga que representa una lista de expresiones.
Aunque ambos tipos pueden estar compuestos, la mayoría de las expresiones pueden estar compuestas arbitrariamente siempre que los tipos coincidan. Cada tipo de declaración tiene su propia forma de componer otras declaraciones, si pueden hacerlo todo. Foreach y si las declaraciones requieren una sola declaración o que todas las declaraciones subordinadas van en un bloque de declaración, una tras otra, a menos que las subestimaciones permitan sus propias subenunciaciones.
Las declaraciones también pueden incluir expresiones, donde una expresión realmente no incluye ninguna declaración. Sin embargo, una excepción sería una expresión lambda, que representa una función, por lo que puede incluir cualquier cosa que una función pueda incluir a menos que el lenguaje solo permita lambdas limitadas, como las lambdas de expresión única de Python.
En un lenguaje basado en expresiones, todo lo que necesita es una sola expresión para una función, ya que todas las estructuras de control devuelven un valor (muchas de ellas devuelven NIL). No hay necesidad de una declaración de retorno ya que la última expresión evaluada en la función es el valor de retorno.
Void
No es el tipo de fondo. Mira mi respuesta .
null
)? ¿No void
sería más como el tipo de unidad (pero con su valor único inaccesible)?
void
es el tipo de retorno de una función que nunca regresa (por ejemplo, una función que throw
es un error), es el tipo inferior . De void
lo contrario, es el tipo de unidad . Tiene razón en que una declaración que no puede divergir tiene el tipo de unidad. Pero una declaración que puede divergir es el tipo inferior. Debido al Teorema de detención, generalmente no podemos probar que una función no diverja, por lo que creo que la unidad es ficción. El tipo inferior no puede tener un valor, por lo que no puede tener un solo valor de null
.
null
valor es realmente un pseudovalor que denota que una referencia se refiere a algo que no existe.
Simplemente: una expresión se evalúa como un valor, una declaración no.
{}
es una declaración. Poner la palabra entre comillas de miedo no cambia eso. Las declaraciones son construcciones sintácticas con semántica. No existe tal cosa como "la capa semántica": parece que se refiere a la ejecución . Dices que estás tratando de ser preciso, pero has fallado en eso. Su queja acerca de "la ignorancia de los votantes negativos" es puramente ad hominem; no tienes información sobre los estados mentales de los votantes negativos.
{}
se define como una declaración en la especificación del lenguaje C #.
Algunas cosas sobre los lenguajes basados en expresiones:
Lo más importante: todo devuelve un valor
No hay diferencia entre llaves y llaves para delimitar bloques de código y expresiones, ya que todo es una expresión. Sin embargo, esto no impide el alcance léxico: se podría definir una variable local para la expresión en la que está contenida su definición y todas las declaraciones contenidas en ella, por ejemplo.
En un lenguaje basado en expresiones, todo devuelve un valor. Esto puede ser un poco extraño al principio: ¿qué (FOR i = 1 TO 10 DO (print i))
devuelve?
Algunos ejemplos simples:
(1)
devoluciones 1
(1 + 1)
devoluciones 2
(1 == 1)
devoluciones TRUE
(1 == 2)
devoluciones FALSE
(IF 1 == 1 THEN 10 ELSE 5)
devoluciones 10
(IF 1 == 2 THEN 10 ELSE 5)
devoluciones 5
Un par de ejemplos más complejos:
OpenADoor(), FlushTheToilet()
o TwiddleYourThumbs()
devolverá algún tipo de valor mundano, como Aceptar, Listo o Éxito.(FOR i = 1 TO 10 DO (print i))
, el valor del bucle for es "10", hace que la (print i)
expresión se evalúe 10 veces, cada vez que devuelve i como una cadena. El tiempo final a través de devoluciones 10
, nuestra respuesta finalA menudo se requiere un ligero cambio de mentalidad para sacar el máximo provecho de un lenguaje basado en expresiones, ya que el hecho de que todo sea una expresión permite 'alinear' muchas cosas
Como un ejemplo rápido:
FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO ( LotsOfCode )
es un reemplazo perfectamente válido para el no basado en expresiones
IF MyString == "Hello, World!" THEN TempVar = 10 ELSE TempVar = 5 FOR i = 1 TO TempVar DO ( LotsOfCode )
En algunos casos, el diseño que permite el código basado en expresiones me parece mucho más natural.
Por supuesto, esto puede conducir a la locura. Como parte de un proyecto de pasatiempo en un lenguaje de scripting basado en expresiones llamado MaxScript, logré crear esta línea 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
)
Una declaración es un caso especial de una expresión, una con void
tipo. La tendencia de los lenguajes a tratar las declaraciones de manera diferente a menudo causa problemas, y sería mejor si se generalizaran adecuadamente.
Por ejemplo, en C # tenemos el Func<T1, T2, T3, TResult>
conjunto sobrecargado muy útil de delegados genéricos. Pero también tenemos que tener un Action<T1, T2, T3>
conjunto correspondiente , y la programación de orden superior de propósito general debe duplicarse constantemente para lidiar con esta desafortunada bifurcación.
Ejemplo trivial: una función que verifica si una referencia es nula antes de llamar a otra función:
TResult IfNotNull<TValue, TResult>(TValue value, Func<TValue, TResult> func)
where TValue : class
{
return (value == null) ? default(TValue) : func(value);
}
¿Podría el compilador lidiar con la posibilidad de TResult
ser void
? Si. Todo lo que tiene que hacer es requerir que el retorno sea seguido por una expresión de tipo void
. El resultado de default(void)
sería de tipo void
, y el func que se pasaría tendría que ser de la forma Func<TValue, void>
(que sería equivalente a Action<TValue>
).
Varias otras respuestas implican que no puede encadenar declaraciones como puede con expresiones, pero no estoy seguro de dónde viene esta idea. Podemos pensar en lo ;
que aparece después de las declaraciones como un operador infijo binario, tomando dos expresiones de tipo void
y combinándolas en una sola expresión de tipo void
.
Declaraciones -> Instrucciones para seguir secuencialmente
Expresiones -> Evaluación que devuelve un valor
Los enunciados son básicamente pasos o instrucciones en un algoritmo, el resultado de la ejecución de un enunciado es la actualización del puntero de instrucción (denominado ensamblador)
Las expresiones no implican y orden de ejecución a primera vista, su propósito es evaluar y devolver un valor. En los lenguajes de programación imperativos, la evaluación de una expresión tiene un orden, pero es solo por el modelo imperativo, pero no es su esencia.
Ejemplos de declaraciones:
for
goto
return
if
(todos ellos implican el avance de la línea (declaración) de ejecución a otra línea)
Ejemplo de expresiones:
2+2
(no implica la idea de ejecución, sino de la evaluación)
Una declaración es un componente de procedimiento a partir del cual se construyen todos los programas de C #. Una declaración puede declarar una variable local o constante, llamar a un método, crear un objeto o asignar un valor a una variable, propiedad o campo.
Una serie de declaraciones rodeadas de llaves forman un bloque de código. Un cuerpo de método es un ejemplo de un bloque de código.
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
Las declaraciones en C # a menudo contienen expresiones. Una expresión en C # es un fragmento de código que contiene un valor literal, un nombre simple o un operador y sus operandos.
Una expresión es un fragmento de código que se puede evaluar en un solo valor, objeto, método o espacio de nombres. Los dos tipos más simples de expresiones son literales y nombres simples. Un literal es un valor constante que no tiene nombre.
int i = 5;
string s = "Hello World";
Tanto i como s son nombres simples que identifican variables locales. Cuando esas variables se usan en una expresión, el valor de la variable se recupera y se usa para la expresión.
if(number >= 0) return true; else return false;
o incluso mejor bool? IsPositive(int number) { if(number > 0) return true; else if(number < 0) return false; else return null;}
:)
Prefiero el significado statement
en el sentido lógico formal de la palabra. Es uno que cambia el estado de una o más de las variables en el cálculo, permitiendo que se haga una declaración verdadera o falsa sobre su (s) valor (es).
Supongo que siempre habrá confusión en el mundo de la informática y la ciencia en general cuando se introducen nuevas terminologías o palabras, las palabras existentes se 'reutilizan' o los usuarios ignoran la terminología existente, establecida o 'adecuada' para lo que están describiendo.
No estoy realmente satisfecho con ninguna de las respuestas aquí. Miré la gramática de C ++ (ISO 2008) . Sin embargo, tal vez por el bien de la didáctica y la programación, las respuestas podrían ser suficientes para distinguir los dos elementos (aunque la realidad parece más complicada).
Una declaración consta de cero o más expresiones, pero también puede ser otros conceptos del lenguaje. Esta es la forma extendida de Backus Naur para la gramática (extracto de la declaración):
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 los otros conceptos que se consideran declaraciones en C ++.
case
por ejemplo es una declaración etiquetadaif
if/else
,case
while
, do...while
,for (...)
break
, continue
, return
(se puede volver expresión),goto
try/catch
bloquesEste es un extracto que muestra la parte de expresiones:
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
+
, -
, *
, /
, &
, |
, &&
, ||
, ...)throw
cláusula también es una expresiónLas declaraciones son oraciones gramaticalmente completas. Las expresiones no son. Por ejemplo
x = 5
se lee como "x obtiene 5." Esta es una oración completa. El código
(x + 5)/9.0
lee, "x más 5 todo dividido por 9.0". Esta no es una oración completa. La declaración
while k < 10:
print k
k += 1
Es una oración completa. Observe que el encabezado del bucle no es; "while k <10" es una cláusula subordinante.
while
Es una expresión de algunos lenguajes como Scala. Estás combinando gramática con mecanografía. Mira mi respuesta .
while
con cuerpo sigue siendo una expresión en Scala. También puede ser una declaración si crea efectos secundarios, lo que permite mi respuesta fuertemente rechazada (una expresión también puede ser una declaración). Mi respuesta es la única correcta. Perdón por todos esos lectores que no pueden entender.
(x + 5)/9.0
definitivamente puede estar solo como una declaración. Además, si por gramaticalmente completo se refiere a un programa válido, C no permite que las declaraciones se mantengan solas como un solo programa.
Aquí está el resumen de una de las respuestas más simples que encontré.
originalmente respondida por Anders Kaseorg
Una declaración es una línea completa de código que realiza alguna acción, mientras que una expresión es cualquier sección del código que se evalúa como un valor.
Las expresiones se pueden combinar "horizontalmente" en expresiones más grandes utilizando operadores, mientras que las declaraciones solo se pueden combinar "verticalmente" escribiendo una tras otra, o con construcciones de bloques.
Cada expresión se puede usar como una declaración (cuyo efecto es evaluar la expresión e ignorar el valor resultante), pero la mayoría de las declaraciones no se pueden usar como expresiones.
La base de facto de estos conceptos es:
Expresiones : una categoría sintáctica cuya instancia se puede evaluar a un valor.
Declaración : Una categoría sintáctica cuya instancia puede estar involucrada con las evaluaciones de una expresión y el valor resultante de la evaluación (si corresponde) no está garantizado.
Además del contexto inicial de FORTRAN en las primeras décadas, ambas definiciones de expresiones y declaraciones en la respuesta aceptada son obviamente erróneas:
sizeof
operador nunca se evalúa.(Por cierto, quiero agregar [cita requerida] a esa respuesta con respecto a los materiales sobre C porque no puedo recordar si DMR tiene tales opiniones. Parece que no, de lo contrario no debería haber razones para preservar la duplicación de funcionalidad en el diseño de C : en particular, el operador de coma frente a las declaraciones.)
(La siguiente justificación no es la respuesta directa a la pregunta original, pero creo que es necesario aclarar algo que ya se respondió aquí).
Sin embargo, es dudoso que necesitemos una categoría específica de "declaraciones" en lenguajes de programación de propósito general:
begin
en el esquema) o el azúcar sintáctico de estructuras monádicas.++i + ++i
tiene sentido en C.)Entonces, ¿por qué las declaraciones? De todos modos, la historia ya es un desastre. Parece que la mayoría de los diseñadores de idiomas no toman su decisión con cuidado.
Peor aún, incluso les da a algunos entusiastas del sistema de tipos (que no están lo suficientemente familiarizados con la historia de PL) algunas ideas erróneas de que los sistemas de tipos deben tener cosas importantes que hacer con los diseños más esenciales de las reglas sobre la semántica operativa.
En serio, el razonamiento dependiendo de los tipos no es tan malo en muchos casos, pero particularmente no es constructivo en este caso especial. Incluso los expertos pueden arruinar las cosas.
Por ejemplo, alguien enfatiza la naturaleza de escribir bien como el argumento central contra el tratamiento tradicional de las continuaciones no delimitadas . Aunque la conclusión es algo razonable y las ideas sobre las funciones compuestas están bien ( pero aún son demasiado ingenuas para el sentido ), este argumento no es sólido porque ignora totalmente el enfoque de "canal lateral" en la práctica como _Noreturn any_of_returnable_types
(en C11) para codificar Falsum
. Y estrictamente hablando, una máquina abstracta con un estado impredecible no es idéntica a "una computadora averiada".
En un lenguaje de programación orientado a sentencias, un bloque de código se define como una lista de sentencias. En otras palabras, una declaración es una sintaxis que puede poner dentro de un bloque de código sin causar un error de sintaxis.
Wikipedia define la declaración de la palabra de manera similar
En la programación de computadoras, una declaración es una unidad sintáctica de un lenguaje de programación imperativo que expresa alguna acción a realizar. Un programa escrito en dicho lenguaje está formado por una secuencia de una o más declaraciones
Observe la última declaración. (aunque "un programa" en este caso es técnicamente incorrecto porque C y Java rechazan un programa que no consiste en nada de declaraciones).
Wikipedia define la expresión de la palabra como
Una expresión en un lenguaje de programación es una entidad sintáctica que puede evaluarse para determinar su valor.
Sin embargo, esto es falso, porque en Kotlin throw new Exception("")
es una expresión, pero cuando se evalúa, simplemente arroja una excepción, sin devolver ningún valor.
En un lenguaje de programación estáticamente tipado, cada expresión tiene un tipo. Sin embargo, esta definición no funciona en un lenguaje de programación de tipo dinámico.
Personalmente, defino una expresión como una sintaxis que se puede componer con un operador o llamadas a funciones para producir una expresión más grande. Esto es realmente similar a la explicación de la expresión de Wikipedia:
Es una combinación de una o más constantes, variables, funciones y operadores que el lenguaje de programación interpreta (de acuerdo con sus reglas particulares de precedencia y asociación) y calcula para producir ("devolver", en un entorno con estado) otro valor
Pero, el problema está en el lenguaje de programación C, dada una función execute, algo como esto:
void executeSomething(void){
return;
}
¿Es executeSomething()
una expresión o es una declaración? Según mi definición, es una declaración porque, tal como se define en la gramática de referencia C de Microsoft,
No puede usar el valor (inexistente) de una expresión que tenga el tipo void de ninguna manera, ni puede convertir una expresión vacía (por conversión implícita o explícita) a cualquier tipo excepto void
Pero la misma página indica claramente que dicha sintaxis es una expresión.
Para mejorar y validar mi respuesta anterior, las definiciones de los términos del lenguaje de programación deben explicarse a partir de la teoría del tipo de informática cuando corresponda.
Una expresión tiene un tipo distinto del tipo Inferior, es decir, tiene un valor. Una declaración tiene el tipo Unidad o Fondo.
De esto se deduce que una declaración solo puede tener algún efecto en un programa cuando crea un efecto secundario, porque no puede devolver un valor o solo devuelve el valor del tipo de Unidad que no es asignable (en algunos idiomas, por ejemplo, una C void
) o (como en Scala) se pueden almacenar para una evaluación retrasada de la declaración.
Obviamente, a @pragma
o a /*comment*/
no tienen tipo y, por lo tanto, se diferencian de las declaraciones. Por lo tanto, el único tipo de declaración que no tendría efectos secundarios sería una no operación. La no operación solo es útil como marcador de posición para futuros efectos secundarios. Cualquier otra acción debido a una declaración sería un efecto secundario. Una vez más, una sugerencia del compilador, por ejemplo @pragma
, no es una declaración porque no tiene tipo.
@pragma
o /*comment*/
son lógicamente inconsistentes.
Más precisamente, una declaración debe tener un "efecto secundario" (es decir, ser imperativo ) y una expresión debe tener un tipo de valor (es decir, no el tipo inferior).
El tipo de enunciado es el tipo de unidad, pero debido a que la unidad del teorema de detención es ficción, digamos el tipo de fondo .
Void
no es precisamente el tipo inferior (no es el subtipo de todos los tipos posibles). Existe en idiomas que no tienen un sistema de tipo de sonido completo . Eso puede sonar como una declaración snob, pero la integridad , como las anotaciones de variación, son críticas para escribir software extensible.
Veamos qué dice Wikipedia sobre este asunto.
https://en.wikipedia.org/wiki/Statement_(computer_science)
En la programación de computadoras, una declaración es el elemento independiente más pequeño de un lenguaje de programación imperativo que expresa alguna acción a realizar.
Muchos lenguajes (por ejemplo, C) hacen una distinción entre declaraciones y definiciones, con una declaración que solo contiene código ejecutable y una definición que declara un identificador, mientras que una expresión evalúa solo un valor.
pass
es una declaración. Es un no-op, y no evalúa nada.