Páginas

Showing posts with label pattern matching. Show all posts
Showing posts with label pattern matching. Show all posts

Sunday, February 5, 2012

Conversão de unidades com Racket

Um rápido post para compartilhar um simples conversor de unidades escrito em Racket.
Resolvi escrever depois de ver a ideia de usar unidades junto dos valores no tutorial oficial do Erlang.

No exemplo, vamos converter centímetros em polegadas ou vice-versa. É fácil estender a ideia para converter outras unidades, como temperatura, pressão, moeda, etc.

O principal é só isso aqui:

(define (convert-length value)
  (match value
    [`(,(? number? v) centimeter) `(,(* v 2.54) inch)]
    [`(,(? number? v) inch) `(,(/ v 2.54) centimeter)]))

Ou seja, criamos uma função chamada convert-length que recebe um valor. O interessante é que esse valor não é um tipo numérico, mas sim uma estrutura de dados que contém um valor numérico e uma unidade.
Com isso, podemos ter uma única função de conversão, e sabemos sempre exatamente com que unidade estamos trabalhando.

O que é feito na função é usar casamento de padrões (pattern matching), uma funcionalidade padrão do Racket e bastante poderosa para trabalhar com estruturas de dados, para realizar a conversão apropriada de acordo com o valor de entrada.

[`(,(? number? v) centimeter) ; padrão
 `(,(* v 2.54) inch)] ; valor retornado

O primeiro padrão casa com uma lista formada por um número, armazenado na variável v, e o símbolo literal centimeter. A segunda parte da cláusula computa a conversão e cria uma estrutura com a nova unidade de medida.

O código completo:

#lang racket
(require rackunit)

;; Idea from the Erlang Tutorial
;; http://www.erlang.org/doc/getting_started/seq_prog.html#id64621
(define (convert-length value)
  (match value
    [`(,(? number? v) centimeter) `(,(* v 2.54) inch)]
    [`(,(? number? v) inch) `(,(/ v 2.54) centimeter)]
    [_ (error "Wrong value format. Value must be a list of two elements: a number and an unit, inch or centimeter.")]))

;; From Matthias Felleisein
;; http://lists.racket-lang.org/users/archive/2011-November/049154.html
(define (tee tag v)
  (displayln `(,tag ,v))
  v)

;;----------------------------------------------------------------------
;; Tests
(check-equal?
 (tee 'test-1 (convert-length '(1 centimeter)))
 '(2.54 inch))

(check-equal?
 (tee 'test-2 (convert-length '(2.54 inch)))
 '(1.0 centimeter))

(check-equal?
 (tee 'test-3 (convert-length (convert-length '(1 centimeter))))
 '(1.0 centimeter))

(check-exn exn:fail?
 (λ ()
   (convert-length 3.4)))