Snow Goose
9 desafíos en 9 lenguajes (4 de 9) parte 7
Camel
Camel es una gran banda de rock progresivo inglesa, fundada en 1971 por Andrew Latimer. Su formación original, aparte de Latimer en guitarra, contaría con Andy Ward, como baterista, Doug Ferguson en bajo y Peter Bardens en teclados. Fue en 1974, con su segundo álbum, Mirage, que fueron reconocidos por la crítica, y un moderado éxito que los llevó incluso de gira por el oeste de Estados Unidos de Norte América. Es un grupo que incorpora elementos de Jazz, música clásica y barroca, junto con toques electrónicos.
Quizás una de sus obras más importantes es “The Snow Goose”, un álbum conceptual, inspirado en la novela homónima de Paul Gallico (quién también escribió “La Aventura del Poseidón” que fue llevada posteriormente al cine). Se trata de una hermosa pieza instrumental que habla sobre la amistad con el telón de fondo de la guerra, durante la terrible Evacuación de Dunkerke, en la Segunda Guerra Mundial.
De ahí Camel fue creciendo en fama, llegando a la cima comercial con “I Can See Your House from Here”, de 1979, un álbum que fue polémico por su portada, la que muestra a un astronauta crucificado flotando sobre la tierra.
Pero los ochenta no fueron buenos para Camel, después de grabar Nude, otro álbum conceptual inspirado en la historia del soldado japonés Hiroo Onoda (quien fue encontrado, varios años después de terminada la Segunda Guerra Mundial, aún escondido en una isla, pues no se había enterado de que ésta había acabado), la banda se encontraba en crisis.
El baterista Andy Ward decidió abandonar la banda para rehabilitarse del alcoholismo y las drogas (años después Ward pasó a integrar Marillion). Bardens y Ferguson habían dejado la banda entre 1977 y 1978 y habían sido reemplazado por otros músicos (quienes venían principalmente de Caravan otra famosa banda de la Escena de Canterbury).
Fue así como en 1982 Latimer se encontraba cómo el único miembro fundador del grupo. Pero su casa discográfica le exigía el cumplimiento del contrato y esto implicaba tener al menos un ‘“hit’”. La historia del rock está llena de estas situaciones, en que los artistas graban obligados alguna pieza. En ese entonces Latimer ya había trabajado con su mujer Susan Hoover, en las líricas.
Obligado Latimer se dirigió a los estudios Abbey Road y contrató a varios músicos de sesión, entre los que se encontraban el bajista David Paton y el cantante Chris Rainbow, quienes ya habían trabajado en Alan Parsons Project. El resultado de esta obligación contractual es un álbum de despedida de Camel, titulado '“The Single Factor'", un álbum regular después de todo lo logrado por la banda. Una pieza muy inclinada al pop, con algunas canciones pensadas para agradar al público masivo, y con bastante influencia de Alan Parsons Project.
Así empezó el gran hiato de Camel, hasta que Latimer revive a la banda en 1991, con una nueva encarnación y grabando en USA, a donde se muda después de vender su casa en Inglaterra. El álbum se llama ‘“Dust and Dreams’”, y está inspirado en la novela ‘“Las uvas de la Ira’”, de Steinbeck. Desde entonces Latimer ha seguido activo y sigue siendo venerado dentro del rock progresivo.
Es quizás una de las grandes bandas de las que nunca has escuchado, y ha tenido gran influencia en lo que fue el Neo Progresivo y ha sido citado como inspiración por Opeth, Steven Wilson y Dream Theater, entre otros.
OCaml
Caml es un lenguaje de programación creado en Francia por el INRIA (Institut national de recherche en informatique et en automatique[). Se trata de un lenguaje multiparadigma, descendiente de ML. Caml viene de Categorical Abstract Machine Language.
En 1991 el mismo INRIA amplió el lenguaje, agregando una capa orientada al objeto creando OCaml, Objective Caml.
OCaml es un hito muy importante en el desarrollo de los lenguajes de programación, siendo el inspirador tanto de Scala como de F# y con una fuerte influencia en Rust.
OCaml tiene un sistema de tipos estático, con inferencia de tipos, polimorfismo paramétrico, administración automática de la memoria, tail recursion, pattern matching, functores, y varias de las características que vemos en los lenguajes más modernos.
Veamos un ejemplo de OCaml
let rec qsort = function
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
qsort left @ [pivot] @ qsort right
Este es el clásico algoritmo qsort en OCaml. En F# se escribiría así:
let rec qsort list =
match list with
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
qsort left @ [pivot] @ qsort right
Como se puede apreciar son bastante parecidos. En mi opinión OCaml es mucho mejor que F#. En mi ignorancia pensé que OCaml era un lenguaje extinto (del mismo modo que pensaba que Camel era una banda que ya no estaba activa).
F# es un lenguaje muy interesante, creado por Microsoft Research Labs en Cambridge Inglaterra. Quizás las innovaciones más interesantes de F# sean las unidades de medida, la meta programación (que nos permite crear lenguajes de dominio específico) y el concepto de information rich programming.
Veamos un ejemplo, primero con las unidades de medida:
[<Measure>] type kg
[<Measure>] type s
[<Measure>] type m
Con esto hemos definido el sistema de medidas MKS.
Así que podemos escribir cosas como las siguientes:
let gravedadTerrestre = 9.8<m/s^2>
let alturaDeMiOficina = 32.5<m>
let velocidadDeImpacto = sqrt (2.0 * gravedadTerrestre * alturaDeMiOficina)
Con esto velocidadDeImpacto se expresa como float<m/s>
Si cometo la equivocación de escribir esto:
let velocidadDeImpacto = sqrt (2.0 * gravedadTerrestre + alturaDeMiOficina)
Obtendré un error indicando que la unidad de medida m no calza con m/s^2.
Lo interesante es que podemos derivar unidades, por ejemplo, el Newton:
[<Measure>] type N = kg m/s^2
y podemos hacer:
let masa = 70.0<kg>
let fuerza:float<N> = masa*gravedadTerrestre
Huffman en F#
La solución en F# para nuestro cuarto desafío, en esta serie sobre esos ‘“raros lenguajes nuevos’”, es de las más breves, superada sólo por la solución en Clojure.
Veamos lo más relevante de la solución.
Para modelar el árbol de Huffman usamos un tipo algebraico:
type Tree =
| Leaf of byte * int
| Node of Tree * Tree * int
member this.Freq() =
match this with
| Leaf(_, f) -> f
| Node(_,_,f) -> f
member this.WriteTo(writer:BitOutputStream) =
match this with
| Leaf(s, _) ->
writer.WriteBit(true)
writer.WriteByte(s)
| Node(l, r, _) ->
writer.WriteBit(false)
l.WriteTo(writer)
r.WriteTo(writer)
member this.ReadChar(reader:BitInputStream) =
match this with
| Leaf(s, _) -> s
| Node(left, right, _) ->
if reader.ReadBit() then
right.ReadChar(reader)
else
left.ReadChar(reader)
member this.Parse(reader:BitInputStream) (writer:Stream) =
while not reader.Eof do
let ch = this.ReadChar(reader)
writer.WriteByte(ch)
Con esto decimos que un Arbol (Tree) puede ser una Hoja (Leaf) que corresponde a una tupla de un byte y un entero. Por otro lado, un Nodo (Node) tiene dos árboles y un entero.
La sentencia member nos permite definir ‘“métodos’” para este tipo. Los que usamos posteriormente para persistir el árbol en disco, o para descomprimir (en el método Parser).
La compresión se resuelve con esta función:
let compress (input:string) (output:string) =
let bs = File.ReadAllBytes input
let tree = List.ofArray bs |> calcFreqs |> buildTree
let codes = makeCodes tree
use writer = new BitOutputStream(output)
tree.WriteTo(writer)
for b in bs do
writer.WriteBits(codes.[b])
Noten esta sentencia:
let tree = List.ofArray bs |> calcFreqs |> buildTree
Esto es un pipeline, que permite componer una serie de funciones.
La función buildTree es recursiva y es similar a la que usamos en nuestra solución en Scala
let buildTree (freqs: (byte*int) list) =
let sort (tree: Tree list) =
tree |> List.sortBy(fun i -> i.Freq())
let rec loop (tree: Tree list) =
match sort tree with
| left::right::[] -> Node(left, right, left.Freq() + right.Freq())
| left::right::tail ->
let node = Node(left, right, left.Freq() + right.Freq())
loop (node :: tail)
| [node] -> node
| [] -> failwith "empty tree list!"
freqs |> Seq.map Leaf |> List.ofSeq |> loop
Por último, para descomprimir hacemos:
let decompress (input:string) (output:string) =
use reader = new BitInputStream(input)
let tree = readTree(reader)
let writer = File.OpenWrite(output)
tree.Parse reader writer
La función readTree es interesante, porque en F# las funciones deben estar bien definidas antes de ser usadas, pero readTree usa dos funciones mutuamente recursivas, así que usamos la notación let and
let rec readTree (reader:BitInputStream) =
let b = reader.ReadBit()
if b then
readLeaf(reader)
else
readNode(reader)and readLeaf(reader:BitInputStream) =
let sym = reader.ReadByte()
Leaf(sym, -1)
and readNode(reader:BitInputStream) =
let left = readTree(reader)
let right = readTree(reader)
Node(left, right, -1)
El resto del código lo pueden ver en mi repositorio en Github: https://github.com/lnds/9d9l/tree/master/desafio4/fsharp
Y para finalizar, los dejo con Camel en Vivo, dos videos, uno de 1975 donde la formación original interpreta fragmentos de Snow Goose acompañado de una orquesta de cámara:
Y el segundo video es de 1997, corresponde a Coming of Age: