Control de Flujo

  • if
  • cond
  • Lógica booleana

Qué es el Control de Flujo?

Decidir y reaccionar

“Control de Flujo” es el término usado en programación para decidir cómo reaccionar ante una dada circunstancia. Tomamos decisiones de esta manera todo el tiempo. Si (if) es un lindo día, entonces (then) deberíamos ir la parque. Si no (else) deberíamos quedarnos y jugar juegos de mesa. Si el tanque está vacío, entonces deberías ir a cargar nafta, si no deberías continuar hasta tu lugar de destino.

Testeando condiciones para reaccionar

El software está lleno de este tipo de decisiones. Si el input del usuario es válido, entonces guardamos sus datos; si no le mostramos un mensaje de error. El patrón común es que verificás una condición y reaccionás de forma diferente basado en si la condición da verdadero (true) o falso (false).

if

En Clojure, la herramienta más básica que tenemos para Control de Flujo es el operador if. Acá hay un ejemplo de cómo podrías programar un escenario de validación de ciertos datos de entrada:

Ejemplo: Si luego de sumar 40 a y da menos de 150, entonces se debe retornar (+ y 40); si no, retornar -150. (esto es para la app de la tortuga, el borde superior del marco está en 150 en y, y el inferior en -150 en y.)

Referencia: Condicional if

(if (< (+ y 40) 150)
  (+ y 40)
  -150))

Forma general del operador if

(if conditional-expression
  expression-to-evaluate-when-true
  expression-to-evaluate-when-false)

Ejemplos de if

(if (> 3 1)
  "3 es mayor que 1"
  "3 no es mayor que 1")
;=> "3 es mayor que 1"

(if (> 1 3)
  "1 es mayor que 3"
  "1 no es mayor que 3")
  ;=> "1 no es mayor que 3"

Valor de verdad (Truthiness?)

Al testear el valor de verdad de una expresión, Clojure considera los valores nil y false como falso y cualquier otro valor como verdadero. Acá hay algunos ejemplos:

Referencia: Truthiness/Valor de verdad

(if "cualquier valor aparte de nil y false es considerado verdadero"
  "Un string es considerado verdadero"
  "Un string no es considerado verdadero")
;=> "Un string es considerado verdadero"

(if nil
  "nil es considerado verdadero"
  "nil no es considerado verdadero")
;=> "nil no es considerado verdadero"

(if (get {:a 1} :b)
  "las expresiones que evalúan a nil son consideradas verdaderas"
  "las expresiones que evalúan a nil no son consideradas verdaderas")
;=> "las expresiones que evalúan a nil no son consideradas verdaderas"

Ejercicio 1: Valor de y dentro de un marco

  • Escribir una función y-within-frame que recibe y (posición vertical) como argumento.
  • Podés usar alguno de los ejemplos de if del slide.
  • La función debe devolver un valor de y que no sea mayor a 150.

;; ejemplo de if
(if (< (+ y 40) 150)
  (+ y 40)
  -150))
;; uso de la función y-within-frame
(y-within-frame 80)    ;=> 120
(y-within-frame 180)   ;=> -150

cond

El operador if toma un sólo predicado. Cuando queremos usar múltiples predicados, if no es una buena opción. Tenemos que anidar, anidar… y anidar condiciones if. Para contemplar múltiples situaciones, el operador cond funciona bien.

Acá va el ejemplo. Si sumando 40 a y da más que 150, evaluar la primer forma. En este caso, devuelve -150. Si sumando 40 a y da menos que -150, evaluar la segunda forma. En este caso, devuelve 150. Si los dos predicados retornan falso, evaluar la forma :else. In este caso, retorna y más 40. Si usamos esta función en la app de la tortuga, podemos mantener a nuestra tortuga entre el borde superior e inferior del marco.

Referencia: Condicional cond

(cond
  (> (+ y 40) 150) -150
  (< (+ y 40) -150) 150
  :else (+ y 40)))

Forma general del operador cond

(cond
  predicate1 expression-to-evaluate-when-predicate1-is-true
  predicate2 expression-to-evaluate-when-predicate2-is-true
  ...
  :else expression-to-evaluate-when-all-above-are-false)

Ejercicio 2: Valor de y dentro del marco - parte 2

La función que escribimos en el ejercicio anterior, y-within-frame, tiene un problema. Si el valor de y es -1000, la función retorna -960. Dado que el valor de y del borde inferior del marco es -150, -960 queda afuera del marco. Tu tortuga va a quedar en un área invisible. Hagamos que la función sea realmente within-frame usando cond.

  • Escribir una función y-within-frame-cond que recibe y (posición vertical) como argumento.
  • Podés usar el ejemplo de cond del slide.
  • La función debe retornar un valor de y entre -150 y 150.

;; uso de la función y-within-frame-cond
(y-within-frame-cond 200)    ;=> -150
(y-within-frame-cond -200)   ;=> 150
(y-within-frame-cond 0)      ;=> 40

Lógica booleana con and, or, y not

Las sentencias if no están limitadas a verificar sólo una cosa. Podés verificar múltiples condiciones usando lógica booleana. La Lógica booleana se refiere a combinar y cambiar el resultado de predicados usando and, or, y not.

Si nunca antes viste este concepto en programación, recordá que sigue el sentido común de cómo ves las cosas normalmente. Es esto y aquello verdadero? Sólo si ambos son verdaderos. Es esto o aquello verdadero? Sí, si cualquiera de ellos – o ambos! – lo son. Es esto no verdadero? Sí, si es falso.

Tabla de verdad

and, or, y not funcionan como otras funciones (no son exactamente funciones, pero funcionan como si lo fueran), entonces se usan con notación prefija, tal como vimos con los operadores aritméticos.

x y (and x y) (or x y) (not x) (not y)
false false false false true true
true false false true false true
true true true true false false
false true false true true false

Combinaciones de and, or, y not

and, or, y not pueden ser combinados. Esto a veces puede ser difícil de leer. Veamos un ejemplo:

(defn año-bisiesto?
  "Cada cuatro años, excepto en años divisibles por 100,
  pero sí para años divisibles por 400."
  [year]
  (and (zero? (mod year 4))
       (or (zero? (mod year 400))
           (not (zero? (mod year 100))))))

[Bonus] Combinando cond, and, or, y not

Aprendimos sobre cond, and, or, y not. Pensemos qué función podemos escribir combinando todos. Un ejemplo:

(defn true-or-false?
  "Dado op, retorna true o false"
  [op]
  (let [x true
        y false]
    (cond
      (= op :and) (and x y)
      :else false)))

(defn correct?
  "Dados op y ans, retorna un mensaje indicando si ans fue correcta o no"
  [op ans]
  (if (= ans (true-or-false? op))
      "Ganaste"
      "Perdiste"))

Ejercicio 3: [Bonus] Completar la función true-or-false?

La función true-or-false? en el slide anterior sólo “ve” la operación :and. Agregá las operaciones :or y :not.

  • Usá core.clj de myproject e InstaREPL
  • Agregá (= op :or) y (= op :not) en el cond
  • Para :not, elegí x o y como argumento
;; uso de la función correct?
(correct? :and false)   ;=> "Ganaste"
(correct? :or false)    ;=> "Perdiste"
(correct? :not true)    ;=> "Ganaste"  (aunque podría ser "Perdiste")

Volvé al primer slide, o andá al outline del curriculum.