Control de Flujo Expresiones Consideraciones de Eficiencia Lenguajes de Programación I Control de Flujo - Expresiones Ernesto Hernández-Novich <[email protected]> c 2006-2010 Copyright Control de Flujo Expresiones Consideraciones de Eficiencia Definición El Control de Flujo es fundamental para la mayoría de los modelos de cómputo, pues establece el orden en que debe ejecutarse el programa. Control de Flujo Expresiones Consideraciones de Eficiencia Organización de los Mecanismos Secuenciación (Sequencing): orden específico, usualmente el de aparición en el programa. Selección (Selection): se escoje entre dos o más instrucciones según alguna condición a tiempo de ejecución. Iteración (Iteration): un fragmento de código se ejecuta de manera repetida bien sea un número de veces o hasta cumplirse determinada condición a tiempo de ejecución. Abstracción procedimental (Procedural Abstraction): una colección de construcciones de control se encapsula como una unidad, sujeta a parametrización. Control de Flujo Expresiones Consideraciones de Eficiencia Organización de los Mecanismos Recursión (Recursion): una expresión se define en términos de si misma directa o indirectamente. Concurrencia (Concurrency): dos o más fragmentos de programa se ejecutan/evalúan al mismo tiempo. No determinismo (Nondeterminacy): el orden o escogencia de instrucciones y expresiones no es especificado deliberadamente. Dejaremos los Procedimientos y la Concurrencia para más adelante. Control de Flujo Expresiones Consideraciones de Eficiencia Expresiones Una expresión puede ser: Un objeto simple – nombre de variable o constante literal. La aplicación de una función u operador a una colección de argumentos u operandos, cada uno de los cuales es a su vez una expresión. El término operador se usa para referirse a las funciones incluídas en el lenguaje que tienen una sintaxis especial simplificada a propósito – + en lugar de “la función suma”. Control de Flujo Expresiones Consideraciones de Eficiencia Notación Prefija (Scheme, LISP) (+ (* 4 3) (* 0.5 (sin pi))) Infija (casi cualquier lenguaje) 4 * 3 + 0.5 * sin(pi) Postfija (Forth, Postscript) 4 3 * pi sin 0.5 * + Mezclada (mixfix, Smalltalk) msg sendTo: "[email protected]" text: "Hola Mundo" En ocasiones los operadores son adornos (sintactic sugar) sobre funciones concretas – en C++ a + b en realidad es a.operator+(b). Control de Flujo Expresiones Consideraciones de Eficiencia Precedencia y Asociatividad La notación infija es ambigua para las expresiones aritméticas. Utilizar paréntesis para indicar explícitamente el orden de evaluación. Emplear reglas de precedencia y asociatividad para ahorrarse los paréntesis (siempre que se haya leído el manual). Precedencia – en ausencia de paréntesis, ¿cuál operador opera antes que otro? Asociatividad – en ausencia de paréntesis, ¿cómo aplicar los operadores de una secuencia en la cual todos tienen la misma precedencia? Control de Flujo Expresiones Consideraciones de Eficiencia Operadores Inusuales Incremento y decremento pre y postfijos. En vez de a = a + 1, escribir a++. El efecto de borde siempre se efectúa – el valor cambia según la posición del operador. Producto de enteros por colecciones (cadenas, listas, . . . ) $a = $n x "a"; # $a tiene $n aes. @b = (42) x 1; # Una lista con 42 unos. @b = (42) x @b; # Ahora son 42 42. Generadores de listas por enumeración @a = -10..10; Operador ternario if-then-else a = (b > c) ? d : e Control de Flujo Expresiones Consideraciones de Eficiencia Más operadores inusuales Algunos lenguajes (Haskell, Prolog) permiten crear nuevos operadores. Asociados a una función particular. Incorporados a la jerarquía de precedencia y asociatividad para cooperar con el resto del lenguaje. “Suma y producto de tuplas numéricas” en Haskell infixl 5 :+: infixl 7 :*: (:+:), (:*:) :: (Num a,Num b) => (a,b) -> (a,b) -> (a,b) (a1,b1) :+: (a2,b2) = (a1+a2,b1+b2) (a1,b1) :*: (a2,b2) = (a1*a2,b1*b2) Control de Flujo Expresiones Consideraciones de Eficiencia Un lenguaje lleno de operadores inusuales IBM “engendró” APL – lenguaje puramente funcional. Lleno de operadores funcionales aplicativos . . . Generar la lista de primos de 1 hasta N (∼ N ∈ N ◦ . × N)/N ← 1 ↓ ιN Control de Flujo Expresiones Consideraciones de Eficiencia Un lenguaje lleno de operadores inusuales IBM “engendró” APL – lenguaje puramente funcional. Lleno de operadores funcionales aplicativos . . . Generar la lista de primos de 1 hasta N (∼ N ∈ N ◦ . × N)/N ← 1 ↓ ιN . . . pero requiriendo un teclado especial Control de Flujo Expresiones Consideraciones de Eficiencia Asignación Si una instrucción de cómputo influye en los cómputos que le siguen más allá de retornar un valor simple, se dice que tiene un efecto de borde o colateral (side effects). En los lenguajes puramente funcionales (Haskell) no existen los efectos de borde y se dice que tienen transparencia referencial. Asignar un valor a un símbolo sólo lo hace disponible durante la evaluación actual. Solamente hay valores que producen valores por aplicación funcional. En los lenguajes imperativos, los efectos de borde son la norma – modificar el estado es lo que produce resultados. Asignar un valor a un símbolo lo hace disponible durante la evaluación actual y posiblemente más allá de ella. Existen valores y referencias a valores. Control de Flujo Expresiones Consideraciones de Eficiencia Valores vs. Referencias La dualidad existencial de las variables En los lenguajes imperativos los nombres de variable denotan “contenedores” para valores. Los roles del lado derecho e izquierdo son diferentes en una asignación cualquiera: El lado derecho debe producir un valor. El lado izquierdo debe referir una ubicación. Las expresiones que denotan valores se denominan r-values y las expresiones que denotan ubicaciones se denominan l-values. Control de Flujo Expresiones Consideraciones de Eficiencia l-values vs. r-values Algunas expresiones no pueden ser l-values 2 + 3 = a, no tiene sentido. a = 2 + 3, tampoco, cuando a es una constante. Un l-value no tiene que ser necesariamente un nombre ni tampoco simple Aprovechando apuntadores (f(a)+3)->b[c] = 2 El lenguaje permite funciones l-value substr($a,5,4) = "hola mundo!" El mismo objeto puede actuar como r-value o l-value en una misma expresión – depende cuál atributo de la asociación interesa. Control de Flujo Expresiones Consideraciones de Eficiencia Modelo de acceso para variables En el Modelo de Valor, la variable puede considerarse un r-value o l-value según aplique. En el Modelo de Referencia, toda variable siempre es considerada como un l-value. Esto obliga a seguir la referencia (dereferencing) en los casos en que sea necesario. En la mayoría de lenguajes con ese modelo, eso es automático; en algunos es responsabilidad del programador. Acarrea la incomodidad del encajonado (boxing) en algunos lenguajes (Java). Control de Flujo Expresiones Consideraciones de Eficiencia Ortogonalidad Cuando las características del lenguaje pueden ser utilizadas en cualquier combinación de manera consistente, se dice que es ortogonal. Lenguajes orientados a expresiones no distinguen instrucción de expresión (Algol fue el primero). begin a := if b < c then d else e; a := begin f(b); g(c) end; g(d); 2 + 3; end; Lenguajes con una aproximación intermedia (como C, Java, Perl) contemplan instrucciones con valor (expression statements). a = (b < c) ? d : e; Control de Flujo Expresiones Consideraciones de Eficiencia Asignaciones Especiales La asignación como una expresión. a = b = 1; $a = ($b = 5) * ($c = f($d) + 9); Combinar un operador con una asignación. a += 5; b[8].a->b *= 2; $str = "foo"; $str .= " bar"; Asignación múltiple ($a,$b) = (42, "hola"); ($b,$a) = ($a,$b) # Look ma! No temp ($a,$b,$c) = foo(...); Control de Flujo Expresiones Consideraciones de Eficiencia Inicialización de Variables Proveer un valor inicial al momento de la declaración reduce la posibilidad de errores difíciles de identificar. Para variables con almacenamiento estático, el valor inicial puede colocarse directamente a tiempo de compilación ahorrando tiempo de ejecución. Algunos lenguajes no tienen mecanismo de inicialización especial, salvo asignar manualmente los valores. Algunos lenguajes establecen reglas de inicialización automática. Los lenguajes orientados a objetos proveen constructores a ser invocados durante la inicialización. Control de Flujo Expresiones Consideraciones de Eficiencia ¿Cuándo se evalúan los operandos? a - f(b) - c * d f(a,g(b),c) Puede influir (bugs) si hay efectos colaterales. Permite mejorar el código objeto. Es por esto que en la mayoría de los lenguajes el orden de evaluación de operandos no está definido. Algunas excepciones: Java y C# insisten en evaluar de izquierda a derecha. C y C++ evalúan los argumentos para funciones de derecha a izquierda. Algunos compiladores de Fortran reordenar expresiones según las propiedades matemáticas. Control de Flujo Expresiones Consideraciones de Eficiencia Corto circuito – Evaluación de McCarthy Mínimo trabajo necesario para obtener el resultado Las expresiones lógicas (boolean expressions) pueden optimizarse para evitar la evaluación total Una secuencia de or es cierta tan pronto una sea cierta. Una secuencia de and es falsa tan pronto una sea falsa. Algunos lenguajes (Pascal) no ofrecen evaluación con cortocircuito. Algunos lenguajes (Erlang) ofrecen ambas posibilidades, vía operadores diferenciados. and y or – no usan cortocircuito. andalso y orelse – si usan cortocircuito. Al emplear corto-circuito, dejan de ser estrictas – ya no son expresiones sino instrucciones