
Do básico ao intermediário: Como bolhas de sabão
Introdução
No artigo anterior Do básico ao intermediário: Navegando na SandBox, vimos como poderíamos navegar dentro de uma das SandBox mantidas pelo MetaTrader 5. Foi mostrado que poderíamos fazer isto de duas maneiras totalmente distintas e com diferentes objetivos. Uma das formas de se fazer esta navegação era por meio de uma caixa de diálogo que utilizaria o próprio sistema operacional. Sendo desta forma a maneira mais prática, fácil e simples de navegar dentro da SandBox.
No entanto, também foi mostrada uma segunda forma de navegação. Esta seria implementada dentro do próprio código da aplicação que você estivesse implementando. Sendo uma maneira um tanto quanto mais complicada. Justamente devido ao fato de que, o sistema de navegação, neste caso, não teria como objetivo uma fácil e rápida interação com o usuário da aplicação. Porém, com um objetivo bem distinto, que neste caso seria, o de conhecer que tipo de arquivos e como a estrutura de diretórios estaria organizada dentro da SandBox.
Porém, esta segunda forma de abordar, ou melhor dizendo, efetuar a navegação dentro da SandBox, nos abre algumas portas, na qual podemos dar uma leve explorada neste momento. Isto por que, apesar de tudo parecer muito lindo e maravilhoso, quando estamos testando uma aplicação. Na prática, a coisa toda, em muitas dos casos, acaba sendo bastante caótica. Sendo que podemos ter arquivos e diretórios sendo apresentados de forma totalmente misturada. Assim como também, e neste caso é bem mais provável. Os nomes não estarem em ordem.
Mas, e esta costuma ser a situação mais comum, podemos vir a desejar criar algum tipo de filtro, como por exemplo, nos baseando na data de criação, ou modificação do arquivo ou diretório. Isto para fins de pesquisa, de modo a apresentar as coisas em uma certa ordem. Facilitando assim encontrar o tipo de informação procurada. E da maneira como aquele código visto no artigo anterior foi implementado, não conseguimos de fato, ter uma forma ordenada de apresentação dos resultados, ou se quer uma filtragem, mesmo a mais simples possível de ser implementada.
Para fazer isto, criar uma forma ordenada de apresentar os resultados. Precisamos adicionar algumas coisas ao código. Este tipo de coisa, em muitas das vezes é a parte onde grande parte dos iniciantes acabam ficando completamente desorientados. Isto por conta do tipo de coisa que precisa ser feita. Não por ser difícil, mas sim por existirem diversos meios diferentes de se fazer a mesma coisa. No entanto, pode ser um tanto quanto confuso, fazer isto em uma ou em outra linguagem de programação. Já que muitas das vezes o código precisa ser adaptado de outra linguagem. Coisa que muitos com menos experiência não conseguem fazer.
Entretanto, aqui seremos um tanto quanto diretos. Não irei tentar, e tão pouco irei introduzir um conceito novo. Apenas vamos ver como usar algo, muito comum na programação, mas que muitos iniciantes não fazem ideia de como é que as coisas acontecem por debaixo dos panos. Portanto, vamos a um novo tópico, para começar a parte divertida do artigo. Pois este sim, é tema no qual me divirto bastante, quando preciso de fato utilizar.
Como bolhas de sabão
Muito bem, vamos começar falando sobre uma coisa, que você com toda a certeza já deve ter utilizado. Que seria o de colocar em ordem crescente um array de números qualquer. E por que estou mencionando este fato? O motivo é simples, meu caro leitor. Dentro da biblioteca de funções e procedimentos do MQL5, temos uma função chamada de ArraySort. Esta função tem como objetivo o de classificar em ordem crescente o conteúdo presente dentro do array.
Para quem ainda não viu esta função em funcionamento, podemos visualizar isto, fazendo uso de um pequeno código como mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. uchar info[10]; 07. 08. MathSrand(GetTickCount()); 09. 10. for (uint c = 0; c < info.Size(); c++) 11. info[c] = (uchar) MathRand(); 12. 13. Print("Array content before ordering..."); 14. ArrayPrint(info); 15. 16. ArraySort(info); 17. 18. Print("Array content after sorting..."); 19. ArrayPrint(info); 20. } 21. //+------------------------------------------------------------------+
Código 01
Neste código 01, fazemos uso do gerador de números pseudo aleatórios para inicializar um pequeno array. Este é declarado na linha seis. A inicialização é feita pelo laço da linha dez. Mas o que nos interessa aqui é justamente as linhas que vem a seguir. Nelas imprimimos primeiramente o array antes de seus valores estarem em ordem e depois de eles serem ordenados. Como a geração é baseada em valores aleatórios, o resultado muito provavelmente não irá ser como você pode observar vendo a imagem abaixo.
Imagem 01
Isto por que a cada execução, serão gerados uma nova sequência de valores. Mas o objetivo é justamente o de perceber que os valores foram ordenados de forma crescente. Isto por conta da função da linha 16, vista no código 01.
Ok, mas o que isto tem a ver com a nossa ideia sobre navegação na SandBox? Bem meu caro leitor, esta é a parte interessante. Muitos de vocês, por serem iniciantes, podem não conseguir entender uma coisa aqui. Quando informações são apresentadas em ordem, seja ela crescente, seja ela decrescente. Foi preciso passar a informação por algum mecanismo de classificação. E entender como estes mecanismos funcionam, irá lhe ajudar a resolver diversos problemas. Já que nem sempre, a linguagem, seja ela qual for, contém uma implementação que lhe permita classificar qualquer tipo de dado. Mas se você entender como o mecanismo de classificação funciona, pode conseguir criar uma função ou procedimento que generalize a classificação. Permitindo assim que você classifique tanto dados numéricos, como texto entre outras coisas.
Por exemplo, se você tentar mudar o código 01 para o que é visto logo abaixo, irá notar que não será possível compilar o mesmo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info[10]; 07. 08. MathSrand(GetTickCount()); 09. 10. for (uint c = 0; c < info.Size(); c++) 11. info[c] = (string) MathRand(); 12. 13. Print("Array content before ordering..."); 14. ArrayPrint(info); 15. 16. ArraySort(info); 17. 18. Print("Array content after sorting..."); 19. ArrayPrint(info); 20. } 21. //+------------------------------------------------------------------+
Código 02
Mas por que? O motivo é justamente o fato de o compilador não conseguir encontrar um modelo que permita utilizar dados do tipo string no array. Não por conta do array em si, mas sim por conta da função ArraySort, que não foi implementada, ou projetada para receber strings a fim de as classificar em ordem crescente. Mas espere um pouco. Então você está me dizendo que se removermos a linha 16 do código 02, poderemos ter uma resposta. Isto por que o compilador consegue gerar uma aplicação executável? Sim, é isto mesmo meu caro leitor. Tanto que se isto for feito, você poderá ver algo parecido com a imagem abaixo, quando executar o código 02. Isto é claro, depois de ter removido a linha 16 do código.
Imagem 02
Agora observe o seguinte fato. Nesta imagem 02, temos strings sendo apresentadas, porém elas não estão de fato sendo classificadas em ordem crescente. E o motivo é que não temos um mecanismo que venha a fazer isto para nós. Agora pense no seguinte detalhe: Se você, meu estimado leitor, por um acaso vier a implementar uma forma de classificar estes mesmos dados, que são uma string, mesmo contendo valores numéricos. Poderá utilizar o mesmo mecanismo para poder classificar os arquivos e diretórios a fim de os apresentar em uma certa ordem. Seja ela crescente ou decrescente. E é aqui onde surge a necessidade de se conhecer os mecanismos de classificação que podem vir a ser implementados dentro de uma linguagem de programação.
Para tornar as coisas mais simples, e assim todos consigam entender o que estaremos de fato implementado. Já que o objetivo aqui é ser o mais didático e tornar o material fácil de ser compreendido, mesmo por um iniciante. Vamos fazer as coisas aos poucos. Assim todos conseguiram acompanhar e entender o que está sendo feito. Pois entender as coisas é mais importante do que simplesmente copiar o código quando você precisar fazer algo parecido. Sendo assim vamos começar com o que é visto no código logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. uchar info[10]; 07. 08. MathSrand(512); 09. 10. for (uint c = 0; c < info.Size(); c++) 11. info[c] = (uchar) MathRand(); 12. 13. Print("Array content before ordering..."); 14. ArrayPrint(info); 15. 16. BubbleSort(info); 17. 18. Print("Array content after sorting..."); 19. ArrayPrint(info); 20. } 21. //+------------------------------------------------------------------+ 22. template <typename T> 23. void BubbleSort(T &arr[]) 24. { 25. #define macroSWAP(A, B) { \ 26. temp = arr[j - 1]; \ 27. arr[j - 1] = arr[j]; \ 28. arr[j] = temp; \ 29. } 30. 31. uint nElements = arr.Size(); 32. T temp; 33. 34. for (uint i = 1; i < nElements; i++) 35. for (uint j = nElements - 1; j >= i; j--) 36. if (typename(T) == "string") 37. { 38. }else 39. if (arr[j - 1] > arr[j]) 40. macroSWAP(arr[j - 1], arr[j]) 41. 42. #undef macroSWAP 43. } 44. //+------------------------------------------------------------------+
Código 03
Este código 03 é onde iremos começar. Observe que estamos fazendo algo muito parecido como o que foi visto no código 01. Porém aqui, estamos criando as coisas de forma que você irá obter os mesmos resultados que serão vistos nas próximas imagens. Isto por que, apesar de ainda estarmos utilizando o gerador pseudo aleatório, para inicializar o array. Estamos iniciando o gerador em um determinado valor. Como você pode notar na linha oito.
Agora preste atenção meu caro leitor. Diferente dos códigos 01 e 02, este código 03 tem na linha 16, uma chamada a um procedimento que estamos implementando. Este tem como objetivo classificar de maneira crescente os dados que estão no array. Agora vem a parte importante, em artigos anteriores foi explicado como criar templates de funções e procedimentos. E como podemos tirar proveito deste tipo de modelagem na prática. Pois bem, aqui está um exemplo de utilização daquele conhecimento explicado naqueles artigos. Portanto, diferente da função de biblioteca ArraySort, que não nos permite classificar qualquer tipo de valor. Aqui temos uma implementação que irá nos permitir fazer isto.
Para tornar isto possível, usamos o teste na linha 36, para checar se estamos classificando strings ou outro tipo de dado qualquer. Note o seguinte, ainda NÃO ESTAMOS CLASSIFICANDO TIPOS STRING. Mas já estamos classificando os demais tipos. E podemos comprovar isto, ao observar o resultado apresentado na imagem logo abaixo.
Imagem 03
Apesar deste mecanismo implementado no código não ser o melhor mecanismo que existe, sendo muito lento para grandes arrays. Para nossa demonstração ele é bem rápido e perfeitamente funcional. Como pode ser constatado na imagem 03. Mas é somente isto? Sim meu caro leitor. Mas agora vem a parte divertida. Modificar o código de forma a podermos classificar o tipo string. Mas antes de fazermos isto, vamos dar um pequeno passo. E este é visto no código logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info[10]; 07. 08. MathSrand(512); 09. 10. for (uint c = 0; c < info.Size(); c++) 11. info[c] = (string) (uchar) MathRand(); 12. 13. Print("Array content before ordering..."); 14. ArrayPrint(info); 15. 16. BubbleSort(info); 17. 18. Print("Array content after sorting..."); 19. ArrayPrint(info); 20. } 21. //+------------------------------------------------------------------+ 22. template <typename T> 23. void BubbleSort(T &arr[]) 24. { 25. #define macroSWAP(A, B) { \ 26. temp = arr[j - 1]; \ 27. arr[j - 1] = arr[j]; \ 28. arr[j] = temp; \ 29. } 30. 31. uint nElements = arr.Size(); 32. T temp; 33. 34. for (uint i = 1; i < nElements; i++) 35. for (uint j = nElements - 1; j >= i; j--) 36. if (typename(T) == "string") 37. { 38. }else 39. if (arr[j - 1] > arr[j]) 40. macroSWAP(arr[j - 1], arr[j]) 41. 42. #undef macroSWAP 43. } 44. //+------------------------------------------------------------------+
Código 04
Note o que mudou entre o código 03 e este código 04. Todas as mudanças feitas, visam justamente permitir que os valores utilizados sejam os mesmos em ambos os casos. Apesar disto, ainda não estamos fazendo a classificação das strings. Porém, o resultado da execução deste código 04, pode ser visto logo abaixo.
Imagem 04
Agora quero que você preste muita, mas muita atenção meu caro leitor. Pois o que será feito, pode ser um tanto quanto confuso, para grande parte de vocês. Isto por que, diferente de classificar valores do tipo numérico, a classificação de strings não é algo muito direto. Ela precisa ser feita de uma maneira muito específica para que de fato possa funcionar. No entanto, você pode notar que tanto no código 03, quanto no código 04, existe entre as linhas 37 e 38 um espaço onde podemos implementar o que será o código de classificação em si. E para entender como esta classificação precisa ser feita, preciso que você primeiro entenda, o tipo de resultado que conseguimos ao fazer uso de certas formas de implementação. Primeiro vamos fazer uso da função StringCompare, já que obviamente ela seria o primeiro caminho a ser pensado. Para fazermos isto, usamos o código visto logo na sequência.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info[10]; 07. 08. MathSrand(512); 09. 10. for (uint c = 0; c < info.Size(); c++) 11. info[c] = (string) (uchar) MathRand(); 12. 13. Print("Array content before ordering..."); 14. ArrayPrint(info); 15. 16. BubbleSort(info); 17. 18. Print("Array content after sorting..."); 19. ArrayPrint(info); 20. } 21. //+------------------------------------------------------------------+ 22. template <typename T> 23. void BubbleSort(T &arr[]) 24. { 25. #define macroSWAP(A, B) { \ 26. temp = arr[j - 1]; \ 27. arr[j - 1] = arr[j]; \ 28. arr[j] = temp; \ 29. } 30. 31. uint nElements = arr.Size(); 32. T temp; 33. 34. for (uint i = 1; i < nElements; i++) 35. for (uint j = nElements - 1; j >= i; j--) 36. if (typename(T) == "string") 37. { 38. if (StringCompare(arr[j - 1], arr[j]) > 0) 39. macroSWAP(arr[j - 1], arr[j]) 40. }else 41. if (arr[j - 1] > arr[j]) 42. macroSWAP(arr[j - 1], arr[j]) 43. 44. #undef macroSWAP 45. } 46. //+------------------------------------------------------------------+
Código 05
Quando executamos este código, teremos como resultado o que é visto logo na imagem abaixo.
Imagem 05
Mas que coisa doida é esta? Aparentemente não está acontecendo uma classificação aqui. Apesar de à primeira vista, estamos sim conseguindo classificar as coisas. Porém, olhando com mais atenção, posso notar que existe algo estranho nesta classificação.
Realmente meu caro leitor, você está certo. O código está conseguindo classificar os valores presentes no array de string. Mas existe uma coisa estranha, que é justamente o fato de que 37 e 78 são menores que 113. Porém, estes dois valores estão sendo colocados no final da fila. Porquê? O motivo é que a classificação está levando em conta apenas o conteúdo da string. Por isto, que todos os valores entre 113 e 189 estão sendo colocados na ordem correta. No entanto, quando deixamos de levar em consideração a quantidade de caracteres presentes na string. A classificação acaba ficando um tanto quanto estranha. Apesar de ter funcionado.
A parte interessante é que se você utilizar texto encontrados em um dicionário, no lugar de valores numéricos como estamos fazendo aqui, irá notar que a classificação irá de fato acontecer como esperado. Porém para o que estamos fazendo, ou tentando fazer, a classificação não foi cem por cento perfeita. Ok, para resolver isto, precisamos também incluir a quantidade de caracteres presentes na string, como um dos filtros a ser utilizado. Assim o sistema de bolhas conseguirá, de fato criar uma classificação correta do array de string. A mudança a ser feita é vista no código logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info[10]; 07. 08. MathSrand(512); 09. 10. for (uint c = 0; c < info.Size(); c++) 11. info[c] = (string) (uchar) MathRand(); 12. 13. Print("Array content before ordering..."); 14. ArrayPrint(info); 15. 16. BubbleSort(info); 17. 18. Print("Array content after sorting..."); 19. ArrayPrint(info); 20. } 21. //+------------------------------------------------------------------+ 22. template <typename T> 23. void BubbleSort(T &arr[]) 24. { 25. #define macroSWAP(A, B) { \ 26. temp = arr[j - 1]; \ 27. arr[j - 1] = arr[j]; \ 28. arr[j] = temp; \ 29. } 30. 31. uint nElements = arr.Size(); 32. T temp; 33. 34. for (uint i = 1; i < nElements; i++) 35. for (uint j = nElements - 1; j >= i; j--) 36. if (typename(T) == "string") 37. { 38. if ((StringLen(arr[j - 1]) > StringLen(arr[j]))) 39. macroSWAP(arr[j - 1], arr[j]) 40. if ((StringCompare(arr[j - 1], arr[j]) > 0) && (StringLen(arr[j - 1]) == StringLen(arr[j]))) 41. macroSWAP(arr[j - 1], arr[j]) 42. }else 43. if (arr[j - 1] > arr[j]) 44. macroSWAP(arr[j - 1], arr[j]) 45. 46. #undef macroSWAP 47. } 48. //+------------------------------------------------------------------+
Código 06
Muito bem, então no momento em que executarmos este código 06, iremos ter como resposta o que é visto na imagem logo a seguir.
Imagem 06
Legal, de fato obtivemos o resultado desejado e esperado. Mas de certa maneira não consigo entender uma coisa. Você informou a pouco que o código 05 consegue colocar, ou melhor dizendo, classificar palavras tiradas de um dicionário em uma ordem crescente. Porém, não estávamos conseguindo colocar valores numéricos na ordem correta. Como pode ser claramente notado, ao observarmos o resultado visto na imagem 05. No entanto, ao utilizarmos esta versão modificada, vista no código 06 conseguimos colocar os dados na ordem correta. Mas será que ao fazermos esta modificação vista no código 06, não acabamos por tornar o código incapaz de classificar de forma adequada palavras tiradas de um dicionário? Bem, talvez grande parte daqueles que se imaginam ser programadores, podem dizer: Mas é claro que não. O código sofreu esta mudança, porém continua capaz de classificar as palavras da maneira correta.
No entanto, aqueles que tem dúvidas com relação ao que pode ter sido programado, podem muito bem pensar que o código pode estar incapaz de trabalhar da maneira correta. E ao terem este tipo de dúvida e se questionarem sobre a viabilidade ou não do que foi feito em termos de mudanças na estrutura do código. Acabam por assim dizer, descobrindo que não existe de fato uma solução que consiga cobrir a totalidade dos casos. E que de fato, em algum momento, saber e compreender como as coisas funcionam por debaixo dos panos, pode ser a diferença entre um resultado adequado, e um resultado medíocre.
Portanto, vamos fazer um simples teste, modificando novamente o código 06, para algo, a fim de conseguir testar a hipótese de que podemos ou não usar esta implementação em todo e qualquer caso. Para isto, basta que mudemos o código 06 como mostrado logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info[] = {"value", "data", "time", "assembly", "checking" }; 07. 08. Print("Array content before ordering..."); 09. ArrayPrint(info); 10. 11. BubbleSort(info); 12. 13. Print("Array content after sorting..."); 14. ArrayPrint(info); 15. } 16. //+------------------------------------------------------------------+ 17. template <typename T> 18. void BubbleSort(T &arr[]) 19. { 20. #define macroSWAP(A, B) { \ 21. temp = arr[j - 1]; \ 22. arr[j - 1] = arr[j]; \ 23. arr[j] = temp; \ 24. } 25. 26. uint nElements = arr.Size(); 27. T temp; 28. 29. for (uint i = 1; i < nElements; i++) 30. for (uint j = nElements - 1; j >= i; j--) 31. if (typename(T) == "string") 32. { 33. if ((StringLen(arr[j - 1]) > StringLen(arr[j]))) 34. macroSWAP(arr[j - 1], arr[j]) 35. if ((StringCompare(arr[j - 1], arr[j]) > 0) && (StringLen(arr[j - 1]) == StringLen(arr[j]))) 36. macroSWAP(arr[j - 1], arr[j]) 37. }else 38. if (arr[j - 1] > arr[j]) 39. macroSWAP(arr[j - 1], arr[j]) 40. 41. #undef macroSWAP 42. } 43. //+------------------------------------------------------------------+
Código 07
Aqui agora temos algo um pouco diferente. Porém a única e verdadeira diferença está nos valores presentes no array de string. Neste caso, temos palavras que você, com toda a certeza, sabe qual a ordem em que elas deverão aparecer. Isto considerando que tenhamos uma ordenação de forma crescente. Mas para a sua surpresa, ao executar este código 07, temos como resultado o que é visto na imagem logo abaixo.
Imagem 07
Sem nenhuma sobra de dúvidas, este resultado visto na imagem 07 não condiz com a ordem que estas mesmas palavras serão encontradas em um dicionário. No entanto, ainda assim uma classificação aconteceu aqui, da mesma maneira que a via ocorrido durante a execução do código 05, onde tivemos como resposta a imagem 05. Por conta disto que é importante, que você, meu caro e estimado leitor, entenda que nem sempre a resposta reportada por uma aplicação, estará correta e tão pouco equivocada. O grande detalhe é: Que tipo de resposta era esperada da aplicação? Dependendo do caso, pode ser que a resposta esteja ou não adequada ao objetivo a ser alcançado. Porém este não é o caso aqui. Assim sendo, temos um impasse.
Onde um procedimento criado para fazer algo, que no caso seria a classificação de dados em um array, está nos dando respostas que podem ou não fazer sentido para nós. No entanto, podemos criar uma leve e sútil modificação no código a fim de tornar as coisas um pouco mais controláveis. E esta modificação pode ser vista logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info_01[10]; 07. string info_02[] = {"value", "data", "time", "assembly", "checking" }; 08. //+----------------+ 09. MathSrand(512); 10. 11. for (uint c = 0; c < info_01.Size(); c++) 12. info_01[c] = (string) (uchar) MathRand(); 13. //+----------------+ 14. Print("Number array before ordering..."); 15. ArrayPrint(info_01); 16. 17. BubbleSort(info_01, true); 18. 19. Print("Number array after ordering..."); 20. ArrayPrint(info_01); 21. //+----------------+ 22. Print("Dictionary before ordering..."); 23. ArrayPrint(info_02); 24. 25. BubbleSort(info_02); 26. 27. Print("Dictionary after sorting..."); 28. ArrayPrint(info_02); 29. } 30. //+------------------------------------------------------------------+ 31. template <typename T> 32. void BubbleSort(T &arr[], const bool bUsingLength = false) 33. { 34. #define macroSWAP(A, B) { \ 35. temp = arr[j - 1]; \ 36. arr[j - 1] = arr[j]; \ 37. arr[j] = temp; \ 38. } 39. 40. uint nElements = arr.Size(); 41. T temp; 42. 43. for (uint i = 1; i < nElements; i++) 44. for (uint j = nElements - 1; j >= i; j--) 45. if (typename(T) == "string") 46. { 47. if ((bUsingLength) && ((StringLen(arr[j - 1]) > StringLen(arr[j])))) 48. macroSWAP(arr[j - 1], arr[j]) 49. if ((StringCompare(arr[j - 1], arr[j]) > 0) && ((StringLen(arr[j - 1]) == StringLen(arr[j])) || !bUsingLength)) 50. macroSWAP(arr[j - 1], arr[j]) 51. }else 52. if (arr[j - 1] > arr[j]) 53. macroSWAP(arr[j - 1], arr[j]) 54. 55. #undef macroSWAP 56. } 57. //+------------------------------------------------------------------+
Código 08
E este código 08, é o que torna possível que venhamos a poder testar as mudanças e validade das mesmas. Isto a fim de conseguir criar uma ordenação de um array de strings de maneira esperada. Então vejamos o que acontece quando executamos este código 08 no terminal do MetaTrader 5. Ao fazermos isto, termos como resposta o que pode ser visto na imagem logo abaixo.
Imagem 08
Simplesmente perfeito. Observem que tanto o array contendo strings numéricas como o array contendo palavras de um dicionário, estão sendo ordenadas da maneira correta. Ou melhor dizendo, da maneira como era esperado. O que antes não era possível. Mas a principal questão aqui, é justamente o que foi necessário ter sido criado para que esta ordenação ocorresse da maneira que fosse produzido o resultado estimado. Tudo que foi preciso fazer foi adicionar um teste extra na região onde estaríamos lidando com dados do tipo string.
Apesar de que você pode estar pensando que este tipo de ordenação, mostrada aqui, é um modelo perfeito e que você pode utilizar para todo e qualquer caso. Quero que você repense esta hipótese, meu caro leitor. Este método de ordenação é de longe o pior tipo que existe. Apesar de ser muito simples de entender seu código, ele degrada de maneira muito rápida o desempenho do sistema como um todo. Já que para cada elemento que estiver no array a ser ordenado, precisaremos executar o laço uma quantidade muito grande de vezes.
Para ser exato, o número de vezes que o laço precisa ser executado é o número de elementos elevado ao quadrado, isto no pior dos casos. Ou seja, para ordenar cinco elementos, precisamos executar o laço 25 vezes. Para dez elementos que seria apenas o dobro de elementos para o primeiro caso, o número de execução, pula para 100. Veja que a quantidade de vezes que precisamos executar o laço, cresce de maneira muito rápida a medida em que novos elementos são adicionados.
Por este motivo, alguns programadores costumam chamar este método visto até aqui, como um inferno das bolhas. Já que cada novo elemento adicionado, aumenta potencialmente o número de execuções que precisam ser feitas, para que a ordenação tenha de fato sucesso.
No entanto, mesmo esta metodologia tola e sem muito aperfeiçoamento vista aqui, seja de fato um problema. Existem casos em que não precisamos executar o laço tantas vezes a fim de que tenhamos um array de fato ordenado na saída. Isto por que, talvez venha a ser necessário, apenas uma ou outra interação de troca, para que o array venha a ser totalmente ordenado. E por mais incrível que possa parecer, uma simples e aparentemente pouco eficaz mudança no código, é capaz de tornar este inferno das bolhas algo um tanto quanto aceitável em certas circunstâncias.
Porém, para entender como esta mudança afeta o código, mas principalmente como ela pode tornar o código mais rápido. É preciso que você, meu caro leitor, tenha compreendido de fato, como o processo de ordenação acontece nesta implementação vista até aqui.
Uma vez que você tenha conseguido entender isto, podemos passar para o que seria a melhoria no código. Isto buscando permitir que o sistema de ordenação, venha em algumas situações ser um pouco mais rápido, ou menos dispendioso, isto falando em termos de uso de CPU e tempo de processamento.
Ok, para que possamos entender como esta modificação que iremos implementar, influencia na execução do código. Precisaremos em primeiro lugar, implementar uma forma de visualizar o que aconteceu dentro do algoritmo de ordenação. Para isto, mudaremos o código 08, para algo visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info_01[10]; 07. string info_02[] = {"value", "data", "time", "assembly", "checking" }; 08. //+----------------+ 09. MathSrand(512); 10. 11. for (uint c = 0; c < info_01.Size(); c++) 12. info_01[c] = (string) (uchar) MathRand(); 13. //+----------------+ 14. Print("+----------------+\nNumber array before ordering..."); 15. ArrayPrint(info_01); 16. 17. BubbleSort(info_01, true); 18. 19. Print("Number array after ordering..."); 20. ArrayPrint(info_01); 21. //+----------------+ 22. Print("+----------------+\nDictionary before ordering..."); 23. ArrayPrint(info_02); 24. 25. BubbleSort(info_02); 26. 27. Print("Dictionary after sorting..."); 28. ArrayPrint(info_02); 29. } 30. //+------------------------------------------------------------------+ 31. template <typename T> 32. void BubbleSort(T &arr[], const bool bUsingLength = false) 33. { 34. #define macroSWAP(A, B) { \ 35. temp = arr[j - 1]; \ 36. arr[j - 1] = arr[j]; \ 37. arr[j] = temp; \ 38. } 39. 40. uint nElements = arr.Size(); 41. T temp; 42. uint count = 0; 43. 44. for (uint i = 1; i < nElements; i++) 45. { 46. for (uint j = nElements - 1; j >= i; j--, count++) 47. if (typename(T) == "string") 48. { 49. if ((bUsingLength) && ((StringLen(arr[j - 1]) > StringLen(arr[j])))) 50. macroSWAP(arr[j - 1], arr[j]) 51. if ((StringCompare(arr[j - 1], arr[j]) > 0) && ((StringLen(arr[j - 1]) == StringLen(arr[j])) || !bUsingLength)) 52. macroSWAP(arr[j - 1], arr[j]) 53. }else 54. if (arr[j - 1] > arr[j]) 55. macroSWAP(arr[j - 1], arr[j]) 56. } 57. 58. Print("Number of interactions: ", count); 59. 60. #undef macroSWAP 61. } 62. //+------------------------------------------------------------------+
Código 09
Note que neste código 09, apenas adicionamos uma forma de contar quantas interações aconteceram dentro do ordenador. Isto foi feito adicionando a linha 42, e logo depois, na linha 46 estamos fazendo a contabilidade das interações. No final, na linha 58, imprimimos o resultado, a fim de sabermos quanta interações ocorreram. Agora preste atenção a uma coisa aqui meu caro leitor, esta implementação não irá executar nElements ao quadrado. Isto por que, a cada interação que o laço na linha 44 executar, iremos ter uma interação a menos sendo executada no laço da linha 46. É muito importante que você entenda isto. Para não ficar confuso quanto o valor que será apresentado, como sendo o número de interações executadas.
Muito bem, então quando executarmos este código iremos ter como resposta o que é visto na imagem logo abaixo.
Imagem 09
O que realmente nos interessa aqui e neste momento são as regiões em destaque nesta imagem 09. Note que temos um determinado número de interações ocorrendo. Agora pergunto: Este é um bom número ou um mal número? Bem a resposta para isto é: Depende. No entanto, existe uma questão aqui, que é justamente onde quero chegar. Observe que os valores a serem ordenados, aparentemente estão muito bagunçados. Porém e se eles estivessem menos bagunçados. Será que isto iria tornar o número de interações menor do que o que obtivemos a pouco? Bem, vamos testar isto para ver o que acontece. Para isto, usamos o código visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info_01[] = {"37", "78", "0", "145", "113", "139", "148", "247", "174", "189"}; 07. string info_02[] = {"value", "data", "time", "assembly", "checking" }; 08. //+----------------+ 09. Print("+----------------+\nNumber array before ordering..."); 10. ArrayPrint(info_01); 11. 12. BubbleSort(info_01, true); 13. 14. Print("Number array after ordering..."); 15. ArrayPrint(info_01); 16. //+----------------+ 17. Print("+----------------+\nDictionary before ordering..."); 18. ArrayPrint(info_02); 19. 20. BubbleSort(info_02); 21. 22. Print("Dictionary after sorting..."); 23. ArrayPrint(info_02); 24. } 25. //+------------------------------------------------------------------+ 26. template <typename T> 27. void BubbleSort(T &arr[], const bool bUsingLength = false) 28. { 29. #define macroSWAP(A, B) { \ 30. temp = arr[j - 1]; \ 31. arr[j - 1] = arr[j]; \ 32. arr[j] = temp; \ 33. } 34. 35. uint nElements = arr.Size(); 36. T temp; 37. uint count = 0; 38. 39. for (uint i = 1; i < nElements; i++) 40. { 41. // interact = false; 42. for (uint j = nElements - 1; j >= i; j--, count++) 43. if (typename(T) == "string") 44. { 45. if ((bUsingLength) && ((StringLen(arr[j - 1]) > StringLen(arr[j])))) 46. macroSWAP(arr[j - 1], arr[j]) 47. if ((StringCompare(arr[j - 1], arr[j]) > 0) && ((StringLen(arr[j - 1]) == StringLen(arr[j])) || !bUsingLength)) 48. macroSWAP(arr[j - 1], arr[j]) 49. }else 50. if (arr[j - 1] > arr[j]) 51. macroSWAP(arr[j - 1], arr[j]) 52. } 53. 54. Print("Number of interactions: ", count); 55. 56. #undef macroSWAP 57. } 58. //+------------------------------------------------------------------+
Código 10
Note que na linha seis, deste código 10, estamos definindo os mesmos valores que podem ser vistos na imagem 09. Porém eles estão um pouco menos bagunçados, por assim dizer. O que à primeira vista, você poderia pensar que tornaria o processo de ordenação mais rápido, ou necessitando de menos interações para que ocorresse. Mas para a sua surpresa, quando este código 10 fosse executado, o resultado seria este que você pode observar na imagem logo na sequência.
Imagem 10
Ou seja, apesar de aparentemente precisarmos de menos interações para ordenar o array visto na linha seis, neste código 10. Ainda assim, estamos utilizando o mesmo numérico de interações. O que para muitos pode parecer ser algo estranho. Mas então, o que está errado neste código 10, para que ele não viesse a usar menos interações? Bem, na verdade, meu caro leitor, não existe nada de errado no código. Apenas faltando adicionarmos um teste cujo objetivo seria justamente saber se o array está ou não ordenado.
Certo, mas este tipo de coisa parece ser um tanto quanto complicado. Já que para sabermos se o array está ou não ordenado precisaríamos verificar isto. O que acabaria tomando tempo e necessitando de algum tipo de interação extra. O que não é desejável. Já que estamos buscando justamente reduzir o número de interações. Ok, mas então como poderíamos resolver isto? Está ao meu ver é a parte interessante. Justamente por que para resolver esta questão, precisamos apenas fazer uma pequena mudança no código. Agora observe no código abaixo o tipo de mudança que seria preciso ser feita.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string info_01[] = {"37", "78", "0", "145", "113", "139", "148", "247", "174", "189"}; 07. string info_02[] = {"value", "data", "time", "assembly", "checking" }; 08. //+----------------+ 09. Print("+----------------+\nNumber array before ordering..."); 10. ArrayPrint(info_01); 11. 12. BubbleSort(info_01, true); 13. 14. Print("Number array after ordering..."); 15. ArrayPrint(info_01); 16. //+----------------+ 17. Print("+----------------+\nDictionary before ordering..."); 18. ArrayPrint(info_02); 19. 20. BubbleSort(info_02); 21. 22. Print("Dictionary after sorting..."); 23. ArrayPrint(info_02); 24. } 25. //+------------------------------------------------------------------+ 26. template <typename T> 27. void BubbleSort(T &arr[], const bool bUsingLength = false) 28. { 29. #define macroSWAP(A, B) { \ 30. temp = arr[j - 1]; \ 31. arr[j - 1] = arr[j]; \ 32. arr[j] = temp; \ 33. interact = true; \ 34. } 35. 36. uint nElements = arr.Size(); 37. T temp; 38. bool interact = true; 39. uint count = 0; 40. 41. for (uint i = 1; (i < nElements) && interact; i++) 42. { 43. interact = false; 44. for (uint j = nElements - 1; j >= i; j--, count++) 45. if (typename(T) == "string") 46. { 47. if ((bUsingLength) && ((StringLen(arr[j - 1]) > StringLen(arr[j])))) 48. macroSWAP(arr[j - 1], arr[j]) 49. if ((StringCompare(arr[j - 1], arr[j]) > 0) && ((StringLen(arr[j - 1]) == StringLen(arr[j])) || !bUsingLength)) 50. macroSWAP(arr[j - 1], arr[j]) 51. }else 52. if (arr[j - 1] > arr[j]) 53. macroSWAP(arr[j - 1], arr[j]) 54. } 55. 56. Print("Number of interactions: ", count); 57. 58. #undef macroSWAP 59. } 60. //+------------------------------------------------------------------+
Código 11
Quando executamos este código 11, teremos como resultado o que é visto logo abaixo.
Imagem 11
Note que agora, nesta imagem 11, realmente tivemos uma redução no número de interações, mas sem grandes mudanças no próprio código. Tudo que precisamos fazer foi adicionar a linha 38, e logo depois modificar alguns pontos do código. Como no caso o laço da linha 41, assim como a macro. Agora veja que o próprio código irá verificar se estamos ou não fazendo algum ajuste no array. Caso o laço da linha 44 venha a ser executado, sem que nenhuma das vezes, onde aconteceu a interação a linha 33 não foi executada. Teremos a garantia de que o array já se encontra devidamente ordenado. Podendo assim, na próxima interação do laço da linha 41, encerrarmos o procedimento de ordenação. Legal não é mesmo?
Considerações finais
Neste artigo, foi explicado um mecanismo muito simples e fácil de entender, cujo proposito seria o de gerar a ordenação de uma array, qualquer. Foi mostrado e visto, que nem sempre o resultado apresentado é aquele que realmente esperamos obter. Sendo assim necessário adaptar a própria implementação a fim de conseguir obter os resultados adequado.
Foi mostrado também, que com muito pouco trabalho, podemos tornar uma implementação, que a princípio seria lenta. Em uma implementação um pouco mais ágil. Isto quando a situação vier a permitir que tenhamos uma maior agilidade no processamento de certos array. Já que pode acontecer de que com poucos ajustes, venhamos a ter o resultado esperado sendo montado. O que dispensa o uso de novas interações, já que as mesmas não fariam mais nenhuma diferença no resultado já montado.
Como boa parte do que foi visto aqui, são mudanças para fins de didática. No anexo, você terá apenas os códigos, já no seu estágio finalizado. Então, se desejar entender porque, um ou outro caminho foi seguido. Basta usar os códigos vistos e postados na integra, no texto deste artigo. De demais, é só estudar e praticar o que foi visto, para entender como o mecanismo mostrado aqui funciona.
Arquivo | Descrição |
---|---|
Code 01 | Demonstração de classificação simples |
Code 02 | Demonstração de classificação simples |
Code 03 | Demonstração de classificação simples |
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso