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)))