miércoles, 29 de agosto de 2012

Pattern Matching


Se acuerdan cuando iban al jardín de infantes que había una actividad en la cual de una fila de patitos había que encontrar el que miraba en la dirección contraria. Bueno en programación Pattern Matching no tiene nada que ver con los patitos.

Pattern Matching nace del paradigma funcional aunque hoy en día lenguaje multiparadigma lo implementan como Scala. Pattern Matching permite definir funciones por medio de macheo de parámetros y resultados. Veamos un ejemplo en Haskell de definición de factorial:

factorial 0 = 1
factorial n = n * factorial (n - 1)

A primera vista parece recursividad, pero esto viene del paradigma funcional por lo tanto no existe secuencia. Para ver la diferencia entre la forma de resolver el problema voy a resolver lo mismo en C++

int factorial(int n) {
      if (n == 0) {
             return 1;
      } else {
            return factorial(n-1);
      }
}

Si comparamos nos ahorramos 5 lineas; pero el tema no es solo escribir menos; sino que cual les parece más legible? cual es más simple?

Muchos lenguajes implementan pattern matching, al venir del paradigma funcional normalmente los lenguajes funcionales como Erlang; vamos a hacer la función de factorial en Erlang.

fac(0) -> 1;
fac(N) when N > 0, is_integer(N) -> N * fac(N-1).

Se puede ver que en este caso se indica que debe ser positivo el numero; si se ingresa un valor negativo lanza un error. Ojo esta restricciones también la podíamos hacer en Haskell.

Factorial con ML:

fun fac 0 = 1
     | fac n = n * fac (n - 1)

Otros lenguajes multiparadigma nos permiten jugar con Patter Matching como Scala:

def fact(n: Int): Int = n match {
    case 0 => 1
    case n => n * fact(n - 1)
  }

En este caso se define fact que devolverá un entero, y que el parámetro n debe matchear con 0 y devuelve 1 o con cualquier otro valor y devolverá n * fact(n - 1).

Por ejemplo Kotlin permite hacer pattern Maching por el tipo de la siguiente manera:

when (x) {
  is Int -> print(x)
  is List<Int> -> print(x.sum())
  !is Number -> print("Not even a number")
  else -> print("can't do anything")
}

Donde x es una variable que llega de parámetro.

Por ejemplo con F# una función de figonacci nos queda de la siguiente manera:

let rec fib n =
    match n with
    | 0 -> 0
    | 1 -> 1
    | _ -> fib (n - 1) + fib (n - 2)

Si lo comparamos con Haskell:

fib[0|1]:=1
fib[n_]:= fib[n-1] + fib[n-2]

En Haskell podemos utilizar el | para decir esto o aquello y el _ para decir cualquier cosa que venga.

Las implementaciones son muchas pero la idea en general es la misma; Pattern Matching permite mayor legibilidad y comodidad a programar y va ganando terreno entre los lenguajes más modernos.