quinta-feira, 20 de outubro de 2011

Randômicos

Gerar valores randômicos sempre foi uma necessidade do desenvolvimento de sistemas, principalmente com a garantia de unicidade, ou seja, valores que nunca se repetem, únicos e exclusivos.

Entre as muitas aplicações tive algumas recentes, relativamente simples, que era a geração de tokens randômicos para a transferência da sessão web de um sistema a outro e outra que necessitava gerar chaves na sessão. Rotinas que já se encontravam desenvolvidas e operantes com C# a um bom tempo.

O contratempo surgiu ao tentar migrar a aplicação para Ruby.
  • Allow non-standard functions on saving, mais especificamente getsitekey() não rodam em Ruby.
Enfim, essas limitações me levaram a estudar um pouco dos recursos atuais de geração randômica do Genexus, e em especial o tipo GUID, atualmente disponível na versão EV1, e o resultado compartilho com vocês.

Números Randômicos Tradicionais
Uma função geradora de randômicos programada anteriormente em C# dava muito bem conta do recado. Nessa tínhamos uma seqüência de letras e números formando uma string e o gerador de randômicos do C# se encarregava de obter um elemento dessa string até um determinado Size.

string[] chars = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","x","z"  };
    Random rnd = new Random();
    string random = string.Empty;
    for (int i = 0; i < Size; i++) {
           random += chars[rnd.Next(0, 33)];
    }

Esse pequeno programa gerava números randômicos de tamanho Size, muito úteis para gerar chaves.

Em Genexus, essa mesma rotina foi programada com a função Random()para gerar números aleatórios. Essa função gera randomicos no formato Numeric(11.9), em intervalos de 0.1, portanto para ter um numero de um digito inteiro é necessário multiplicar o resultado por 10, dois dígitos por 100, e assim por diante, ficando:


&String    = 'ABCDEFGHIJKLMNOPQRSTUVXWYZ0123456789abcdefghijklmnopqrstuvxwyz'
     &Randomic = ''
     for &n= 1 to &Size
           &i = Random()*100
           &i = iif(&i > 62, &i-37, &i)
           &Randomic += &String.Substring(&i, 1)
     endfor

Essa lógica em Genexus é muito útil porque se trabalhássemos apenas com números inteiros o limite seria 18 dígitos, visto que esta é a maior dimensão possível de um tipo Numeric(18.0). Portanto, faz-se necessário executar mais de uma vez as chamadas ao Random(), por isso da necessidade do for &n= 1 to &Size.

(Uma pergunta aqui: porque preciso da expressão &i-37 no iif? )

Em alguns ambientes, o Random() gera a mesma seqüência de números, no Ruby deu certo de primeira, mas se tiver problemas com isso pode utilizar o RSeed().


Maiores informações:

Até aqui o problema havia sido resolvido com grande facilidade, mas por acaso me deparei com algo novo na Evolution 1.


Randômicos com GUID
Esse sujeito estava meio escondido nas definições de tipos de dados Genexus, e não lhe foi imputado o devido respeito, pois se trata de um tipo string que gera números pseudo-randômicos hexadecimais, creio haver muitas aplicações possíveis, inclusive no meu caso daria conta sem problema algum, apesar de hexadecimal significar digitos de 0..9 e de A..F, portanto, teriamos ai uma pequena restrição.

O mais impressionante é a capacidade de geração,  2128  que equivale a um número igual a 3,4028236692093846346337460743177e+38, segundo os cálculos da Artech, algo como um trilhão de GUID´s a cada nanossegudo durante 10 bilhões de anos seriam suficientes para gerar uma repetição, portanto, acho que dá pra usar.

Quanto ao uso, o Wikipedia oferece alguns bons exemplos, e a própria Artech o utiliza para identificar cada um dos objetos Genexus em sua KB, e garante que cada um será único em qualquer Kb que venha a ser criada no mundo todo.  Um dos exemplos propõe ainda a utilização de uma NotaFiscalId do tipo GUID, ou seja, dispensa-se o tão utilizado e querido e usado Autonumber=true.

A string GUID gerada possui o seguinte formato (fonte: Wikipédia):
Hex digits
Description
8
Data1
4
Data2
4
Data3
4
Initial two bytes from Data4
12
Remaining six bytes from Data4


Em termos práticos, são 30 caracteres em 5 regiões separadas por -, como no exemplo a seguir.

f5a88c30-db5b-012e-3dd2-1231380f2e23

Então como Usar isso?

O tipo GUID pode ser aplicado a atributos e variáveis e de quebra alguns métodos ficam disponibilizados, inclusive um que gera um novo valor randômico.

Para criar um novo GUID, por exemplo, pode-se programar em uma variável:


&guid = GUID.NewGuid()

Para se comparar com um GUID existente, que pode ser recebido por parâmetro, por exemplo, teriamos.


if &guid <> GUID.FromString("f5a88c30-db5b-012e-3dd2-1231380f2e23")
     msg("Bad Site Key")
endif

Dessa forma poderiamos também transformar um GUID em string.


msg(&guid.ToString())

Maiores informações:

Bom, fica ai mais uma opção para randômicos já em um formato de chave, só resta aplicar a um campo chave e ver no que dá.