A classificação é um conceito simples, mas poderoso. Ao pegar uma lista desorganizada de itens, você pode usar um algoritmo para classificá-los em uma ordem específica. Embora essa ideia possa parecer básica, a classificação aparece em toda a ciência da computação em muitos aspectos e formas diferentes. Uma das aplicações mais importantes é a aceleração das pesquisas. Por exemplo, seu computador classifica os arquivos por nome e tamanho para que você possa encontrar rapidamente o que precisa. A UEFN permite que você classifique objetos no Organizador por Tipo para agrupar coisas e organizar seu projeto. No Fortnite, os jogadores no placar do jogo são classificados por várias estatísticas diferentes, como eliminações e tempo de volta, para que todos saibam quem está no topo.
A aplicação da classificação ao código do Verse pode ajudar você a melhorar sua jogabilidade e aprimorar aspectos do seu desenvolvimento. Por exemplo, que tal ordenar uma matriz de jogadores por sua pontuação em um torneio, no qual os jogadores que tiveram a menor pontuação em cada rodada são eliminados? Ao classificar essa matriz no final de cada rodada, você pode encontrar rapidamente os menores pontuadores e defini-los como espectadores. Por outro lado, é possível obter rapidamente os jogadores com a melhor pontuação e conceder a eles um bônus para a próxima rodada. Você pode até mesmo usar essa lista ordenada para criar tabelas de liderança locais persistentes, para que os jogadores saibam em quem prestar atenção durante o jogo. E, se você tiver uma lista de pessoas que gostaria de realçar na sua ilha, poderá classificar os nomes em ordem alfabética para facilitar a exibição.
Cada um desses exemplos usa a classificação de maneiras diferentes, mas, em essência, todos eles usam um algoritmo de classificação para atingir seu objetivo. Os algoritmos de classificação implementam a lógica que classifica suas listas, e há muitos tipos diferentes para escolher de acordo com suas necessidades. Embora a classificação seja uma ferramenta avançada, é importante entender como ela funciona se você quiser integrá-la aos seus projetos. Essa página aborda as principais ideias por trás da classificação e como escolher o algoritmo que funciona melhor para o seu projeto. Para obter implementações de algoritmos de classificação específicos, consulte o link no final desta página.
Classificação por mesclagem
A classificação por mesclagem é um algoritmo de classificação do tipo dividir e conquistar. Isso significa que ele divide o grupo de elementos que precisa ser classificado em grupos menores e classifica os grupos menores. Em seguida, ele combina os grupos menores com o conhecimento de que eles já estão classificados.
Como implementar a classificação por mesclagem
A implementação a seguir chama a si mesma para mesclar a classificação das matrizes divididas, o que significa que é uma função recursiva. Quando você tem uma função recursiva, precisa especificar um caso base para evitar a recursão infinita. Como esse algoritmo divide os vetores pela metade todas as vezes, o caso base é o vetor com apenas um elemento, de modo que o vetor é considerado classificado para começar com um elemento e pode ser mesclado com outro vetor de elemento único e assim por diante até o vetor completo de elementos.
A implementação a seguir é genérica, pois usa tipos paramétricos para seus parâmetros. Isso significa que você pode usar o tipo que quiser para os elementos e sua própria função de comparação como argumentos. Portanto, você pode ter uma matriz de números inteiros que você classifica ou uma matriz de um tipo mais complexo, como uma classe, que você pode classificar. A função Compare
é outro parâmetro para o qual você pode passar suas próprias funções personalizadas. Se quiser classificar do menor para o maior, ou por determinados campos de uma classe, você pode especificar isso em sua função personalizada sem modificar essa implementação de função genérica.
Siga estas etapas para implementar a classificação por mesclagem no Verse:
-
Vamos começar escrevendo o caso básico: apenas um elemento na matriz. Se houver um elemento ou menos, retorne o argumento Array que foi passado para a função.
MergeSort<public>(Array:[]t, Compare(L:t, R:t)<decides><transacts>:t where t:type)<transacts>:[]t= Length:int = Array.Length if: Length > 1 # Verifique se há mais de um elemento na matriz, caso contrário, atingimos o caso base. else: # Retorne a matriz passada porque atingimos o caso base. Matriz
-
Em seguida, divida a matriz ao meio.
~~~(verse) MergeSort
(Array:[]t, Compare(L:t, R:t) :t where t:type) :[]t= Length:int = Array.Length if: Length > 1 # Verifique se há mais de um elemento na matriz, caso contrário, atingimos o caso base. Mid:int = Floor(Length / 2) # Obtenha o índice do meio da matriz. Left:[]t = Array.Slice[0, Mid] # Divida a matriz pela metade. Isso mantém os elementos do início até o índice Mid - 1. Right:[]t = Array.Slice[Mid] # Divida a matriz pela metade. Isso mantém os elementos do índice médio até o final da matriz. else: # Retorne a matriz passada porque atingimos o caso base. Matriz ~~~ -
Chame
MergeSort
em cada metade da matriz inicial e, em seguida, façaMerge
nas duas metades juntas.~~~(verse) MergeSort
(Array:[]t, Compare(L:t, R:t) :t where t:type) :[]t= Length:int = Array.Length if: Length > 1 # Verifique se há mais de um elemento na matriz, caso contrário, atingimos o caso base. Mid:int = Floor(Length / 2) # Obtenha o índice do meio da matriz. Left:[]t = Array.Slice[0, Mid] # Divida a matriz pela metade. Isso mantém os elementos do início até o índice Mid - 1. Right:[]t = Array.Slice[Mid] # Divida a matriz pela metade. Isso mantém os elementos do índice médio até o final da matriz. then: # Chame MergeSort na metade esquerda da matriz. LeftSorted:[]t = MergeSort(Left, Compare) # Chame MergeSort na metade direita da matriz. RightSorted:[]t = MergeSort(Right, Compare)
# Combine as duas matrizes e retorne o resultado. Merge(LeftSorted, RightSorted, Compare) else: # Retorne a matriz passada porque atingimos o caso base. Matriz ~~~
-
Ao mesclar as duas metades novamente, compare os elementos de cada matriz e adicione-os à matriz resultante até que todos os elementos de ambas as matrizes tenham sido adicionados. Para fazer isso, tenha uma variável de índice para rastrear as posições em cada matriz. Toda vez que a função
Compare
for bem-sucedida, o elemento de matriz esquerda deverá ser adicionado e sua variável de índice deverá avançar para a próxima posição na matriz; caso contrário, faça o mesmo com o elemento de matriz direita. Depois que todos os elementos de uma das matrizes tiverem sido adicionados, adicione os elementos restantes da outra matriz, pois ela já foi classificada.~~~(verse) # Uma função auxiliar para MergeSort que combina as matrizes divididas em uma ordem baseada na função Compare. Merge(Left:[]t, Right:[]t, Compare(L:t, R:t)
:t where t:type) :[]t= var LeftIndex:int = 0 var RightIndex:int = 0 var MergedArray:[]t = array{} # Percorra todos os elementos das matrizes para adicioná-los à variável MergedArray. loop: if (LeftElement := Left[LeftIndex], RightElement := Right[RightIndex]): # Verifique o elemento na metade esquerda da matriz com o elemento na metade direita da matriz. # Usa a função Compare passada como um argumento if (Compare[LeftElement, RightElement]): set MergedArray += array{LeftElement} set LeftIndex += 1 else: set MergedArray += array{RightElement} set RightIndex += 1 else: # Neste ponto, adicionamos todos os elementos de uma das matrizes. # Agora, verifique qual matriz ainda tem elementos a serem mesclados e adicione todos os elementos restantes. if (LeftIndex >= Left.Length): option{set MergedArray += Right.Slice[RightIndex]} else: option{set MergedArray += Left.Slice[LeftIndex]} # Saia do loop porque já terminamos de adicionar todos os elementos. break
# Retorne a matriz mesclada. MergedArray ~~~
Script completo
# Um algoritmo de classificação que atua por divisão e conquista, dividindo um arranjo em dois, ordena cada arranjo separadamente e depois junta os dois.
# Essa é uma implementação recursiva, em que a função chama a si mesma para fazer a classificação por mesclagem das matrizes divididas.
# O caso base (a condição para interromper a recursão) é que a matriz tem apenas um elemento.
# Essa é uma implementação genérica que usa tipos paramétricos e, portanto, você pode fornecer seu próprio tipo e sua própria função de comparação como argumentos.
MergeSort<public>(Array:[]t, Compare(L:t, R:t)<decides><transacts>:t where t:type)<transacts>:[]t=
Length:int = Array.Length
if:
Length > 1 # Verifique se há mais de um elemento na matriz, caso contrário, atingimos o caso base.
Mid:int = Floor(Length / 2) # Obtenha o índice do meio da matriz.
Left:[]t = Array.Slice[0, Mid] # Divida a matriz pela metade. Isso mantém os elementos do início até o índice Mid - 1.
Right:[]t = Array.Slice[Mid] # Divida a matriz pela metade. Isso mantém os elementos do índice médio até o final da matriz.
then:
# Chame MergeSort na metade esquerda da matriz.
LeftSorted:[]t = MergeSort(Left, Compare)
# Chame MergeSort na metade direita da matriz.
RightSorted:[]t = MergeSort(Right, Compare)
# Combine as duas matrizes e retorne o resultado.
Merge(LeftSorted, RightSorted, Compare)
else:
# Retorne a matriz passada porque atingimos o caso base.
Matriz
# Uma função auxiliar para MergeSort que combina as matrizes divididas em uma ordem baseada na função Compare.
Merge(Left:[]t, Right:[]t, Compare(L:t, R:t)<decides><transacts>:t where t:type)<transacts>:[]t=
var LeftIndex:int = 0
var RightIndex:int = 0
var MergedArray:[]t = array{}
# Percorra todos os elementos das matrizes para adicioná-los à variável MergedArray.
loop:
if (LeftElement := Left[LeftIndex], RightElement := Right[RightIndex]):
# Verifique o elemento na metade esquerda da matriz com o elemento na metade direita da matriz.
# Usa a função Compare passada como um argumento
if (Compare[LeftElement, RightElement]):
set MergedArray += array{LeftElement}
set LeftIndex += 1
else:
set MergedArray += array{RightElement}
set RightIndex += 1
else:
# Neste ponto, adicionamos todos os elementos de uma das matrizes.
# Agora, verifique qual matriz ainda tem elementos a serem mesclados e adicione todos os elementos restantes.
if (LeftIndex >= Left.Length):
option{set MergedArray += Right.Slice[RightIndex]}
else:
option{set MergedArray += Left.Slice[LeftIndex]}
# Saia do loop porque já terminamos de adicionar todos os elementos.
break
# Retorne a matriz mesclada.
MergedArray
~~~
## Como escolher um algoritmo
Embora muitos algoritmos de classificação sigam ideias semelhantes, alguns aspectos diferenciam cada um deles e podem ter um grande impacto no desempenho do algoritmo.
### Complexidade de tempo
Os algoritmos levam tempo, e quanto mais coisas eles tiverem que fazer, mais tempo levará. Vários fatores influenciam a complexidade do problema que o algoritmo está tentando resolver, e a quantidade de tempo necessária para lidar com ele é chamada de **complexidade de tempo**.
Como calcular o tempo exato que um algoritmo levará para ser concluído é quase impossível devido a vários fatores, como o hardware do computador, outros códigos em execução ou o tipo de dados com os quais se está trabalhando, a complexidade do tempo é medida em [operações](), ou seja, o número de ações que um algoritmo precisa executar para ser concluído. Como cada algoritmo é diferente, a quantidade de tempo necessária para processar um conjunto de dados é representada usando a notação **big O**. A notação Big O não descreve o número exato de operações necessárias, mas descreve como esse número muda à medida que o tamanho da lista inserida no algoritmo aumenta.
Por exemplo, digamos que você escreva um algoritmo para encontrar o menor número em uma lista. Como você não sabe se a lista está ordenada, o algoritmo precisa verificar cada número para saber se ele é o menor. Se houver três itens na lista, seu algoritmo fará três verificações, e se houver dez itens, seu algoritmo fará dez. Como o número de operações aumenta linearmente com o tamanho da entrada da lista, a complexidade de tempo é linear, ou **O(n)**, em que n é o número de itens na lista. Se o algoritmo tivesse que fazer duas operações por item, como copiar cada item em uma lista diferente após cada verificação, a complexidade de tempo seria **O(2n)**, pois o número de operações é 2 * NumberOfItems.
E se o algoritmo quisesse apenas verificar se há algum item na lista? Como o algoritmo faz apenas uma operação, independentemente do tamanho da entrada, a complexidade de tempo seria constante, ou **O(1)**. Há muitos outros exemplos, como tempo quadrático (**O(n^2)**) ou tempo fatorial (**O(n!)**), mas o que importa é que cada um deles descreve como o número de operações realizadas cresce com o tamanho da entrada.
### Complexidade na classificação
A medição da complexidade do tempo para algoritmos de classificação fica muito mais complicada devido aos diferentes estados em que uma lista pode estar antes da classificação. E se ela já tiver sido resolvida? E se ela for classificada de forma inversa? Como o desempenho dos algoritmos de classificação pode variar drasticamente dependendo desses fatores, eles são normalmente medidos por suas complexidades de tempo no caso melhor, pior e médio.
Por exemplo, vejamos o [algoritmo de classificação por bolhas](https://dev.epicgames.com/community/snippets/zbLB/fortnite-bubble-sort). Na melhor das hipóteses, com uma lista já ordenada, a classificação por bolhas só precisa verificar cada par de elementos, mas não precisará fazer nenhuma troca! Isso significa que a complexidade de tempo no melhor caso será **O(n)**, o que é ótimo! No entanto, o que dizer do cenário oposto, em que a lista é classificada de trás para frente? Nessa situação, a classificação por bolhas terá que fazer _n _número de verificações e trocar cada elemento da lista _n_ vezes, o que significa que a complexidade de tempo no pior caso será n * n ou **O(n^2)**. Isso é considerado lento em comparação com outros algoritmos de classificação e pode ficar fora de controle muito rapidamente, pois o algoritmo precisaria de pelo menos cem operações para processar uma lista de dez elementos. Você consegue imaginar quantas operações seriam necessárias para cem elementos? Que tal mil?
Em vez do melhor e do pior caso, os algoritmos de classificação geralmente têm um desempenho mais próximo de seus casos médios. Por exemplo, a complexidade média de casos de classificação de mesclagem é **O(nlogn)**. Essa é considerada uma boa referência, pois muitos outros algoritmos de classificação comuns têm complexidades de casos melhores, piores e médios nesse nível ou muito próximas a ele. A classificação por bolhas de antes tem uma complexidade média de **O(n^2)** e, portanto, mesmo no caso médio, ainda é relativamente lenta.
Conhecer a complexidade de tempo é importante ao escolher um algoritmo, pois você deseja que seu código seja executado rapidamente e tenha um desempenho semelhante, independentemente do tipo de entrada. Para ver alguns exemplos de algoritmos de classificação eficientes comuns, consulte [Classificação por Mesclagem](https://dev.epicgames.com/community/snippets/LNY1/fortnite-merge-sort) e [Classificação rápida](https://dev.epicgames.com/community/snippets/90Da/fortnite-quicksort). Esses dois algoritmos têm as mesmas complexidades de caso melhor, pior e médio de **O(nlogn)** e são um bom ponto de partida para qualquer situação de classificação.
### Complexidade espacial
Além da complexidade de tempo, também é importante considerar a quantidade de material que o algoritmo precisa criar para concluir a classificação. Essa é a **complexidade de espaço**, ou a quantidade de dados extras criados ao executar o algoritmo. Assim como a complexidade de tempo, a complexidade de espaço é medida com a notação Big _O _, que varia entre algoritmos. Por exemplo, como a classificação por mesclagem divide uma lista em _n_ sublistas, ela tem uma complexidade de espaço de **O(n)**. Os algoritmos que classificam os objetos no local (ou seja, apenas modificam os dados existentes, em vez de criar novos) têm uma complexidade espacial de **O(1)**. Se você estiver trabalhando com listas muito grandes, a complexidade do espaço pode ser algo a ser observado ao selecionar o algoritmo.
Como o Verse é uma [linguagem de programação funcional](), as operações da API de estrutura de dados retornam versões modificadas do item original passado como entrada. Por exemplo, as funções de matriz `Insert()` e `RemoveElement()` não modificam o array original, mas criam e retornam uma nova matriz após a execução da função. Isso pode levar a requisitos mais altos de complexidade de tempo e espaço para levar em conta a criação desses novos elementos.
### Estabilidade
Muitas vezes, ao classificar, seu algoritmo se depara com empates. Por exemplo, se forem dados dois jogadores, cada um com uma pontuação de cinco, como o algoritmo deve classificá-los? Nesse cenário, um **algoritmo estável** preservará a ordem em que os elementos iguais aparecem na lista após a classificação. Portanto, se o jogador 1 estava antes do jogador 2 na lista antes da classificação, a lista classificada ainda terá o jogador 1 antes do jogador 2.
No entanto, um **algoritmo instável** não preservará essa ordem, o que significa que o jogador 2 pode aparecer antes do jogador 1. Isso é importante se o seu código se preocupar com os primeiros elementos de uma lista, por exemplo, se você quiser obter os três melhores jogadores por pontuação para exibir em uma tabela de liderança. Nessa situação, você gostaria de ter um algoritmo estável para garantir que quem atingisse essa pontuação primeiro permanecesse na tabela de liderança.
## Como testar e traçar o perfil dos seus algoritmos
Embora seja bom saber como se espera que seu algoritmo seja executado, considerando a complexidade de tempo e espaço, é ainda mais importante medir o desempenho real do algoritmo em entradas reais. O desenvolvimento de algoritmos de classificação pode ser difícil, e pode ser difícil identificar se o seu código funciona de forma consistente para todas as entradas. É aí que entram os testes. Ao configurar uma função de teste, você pode avaliar a precisão do código e identificar como ele se comporta à medida que o desenvolve. Também é possível verificar se há alguma regressão no desempenho se você alterar a implementação do que está testando. Siga estas etapas para configurar um arquivo de teste para seus algoritmos de classificação e gerar dados sobre o desempenho deles:
1. Crie uma função passível de falha chamada `RunArrayTest`.
~~~(verse)
# Função de teste para classificar matrizes e criar um perfil do código.
RunArrayTest()<decides><transacts>:void=
-
Adicione o argumento para a matriz na qual você deseja executar a classificação e a função de comparação a ser usada.
~~~(verse) # Função de teste para classificar matrizes e criar um perfil do código. RunArrayTest(ActualArray:[]t, Compare(L:t, R:t)
:t where t:subtype(comparable)) :void= # Execute a classificação por mesclagem ResultArray := SortingAlgorithms.MergeSort(ActualArray, Compare) ~~~ -
Agora, você precisará comparar a matriz classificada com o que é esperado. Adicione o argumento para que a matriz esperada seja comparada com a matriz real classificada para verificar se o resultado está correto.
Compare os elementos do
ResultArray
com os elementos doExpectedArray
. A funçãoRunArrayTest
é passível de falha e, portanto, assim que um elemento não corresponder, a função de teste falhará.# Função de teste para classificar matrizes e criar um perfil do código. RunArrayTest(ActualArray:[]t, ExpectedArray:[]t, Compare(L:t, R:t)<decides><transacts>:t where t:subtype(comparable))<decides><transacts>:void= # Execute a classificação por mesclagem ResultArray := SortingAlgorithms.MergeSort(ActualArray, Compare) # Verifique se o teste foi aprovado e classificou a matriz conforme esperado. for (Index -> Result : ResultArray, Expected := ExpectedArray[Index]): Resultado = Esperado
-
Adicione uma função de argumento para imprimir as matrizes em cada etapa, para que você possa compará-las se o teste falhar e entender como corrigir o código que você está testando.
~~~(verse) # Função de teste para classificar matrizes e criar um perfil do código. RunArrayTest(ActualArray:[]t, ExpectedArray:[]t, Compare(L:t, R:t)
:t, ArrayToString(:[]t) :string where t:subtype(comparable)) :void= # Execute a classificação por mesclagem ResultArray := SortingAlgorithms.MergeSort(ActualArray, Compare) # Imprima as matrizes Real, Esperada e Resultado para solução de problemas. ProjectLog("Actual: {ArrayToString(ActualArray)}") ProjectLog("Expected: {ArrayToString(ExpectedArray)}") ProjectLog("Result: {ArrayToString(ResultArray)}")
# Verifique se o teste foi aprovado e classificou a matriz conforme esperado. for (Index -> Result : ResultArray, Expected := ExpectedArray[Index]): Resultado = Esperado ~~~
-
Sua função de teste verifica a precisão do resultado, mas você também pode obter mais informações do seu teste. Usando a [expressão de perfil] (profile-in-verse), você pode medir o desempenho dos seus algoritmos registrando o tempo que o algoritmo leva para ser concluído.
Isso é útil se você quiser comparar o desempenho de diferentes algoritmos de classificação ou testar como os seus algoritmos são executados com diferentes entradas. Os algoritmos individuais podem ter um desempenho muito diferente com base nas entradas de melhor e pior caso, e a criação de perfis permite que você encontre e teste esses casos para saber o que deve ser observado. Em vários testes, você pode avaliar o desempenho médio do seu algoritmo e estimar o desempenho dele em entradas de um determinado tamanho.
-
Adicione um argumento de cadeia de caracteres chamado
Description
que explique o foco do teste. Você pode adicionar isso à expressãoprofile
para registrar a descrição com o valor cronometrado. ~~~(verse) # Função de teste para classificar matrizes e criar um perfil do código. RunArrayTest(Description:string, ActualArray:[]t, ExpectedArray:[]t, Compare(L:t, R:t):t, ArrayToString(:[]t) :string where t:subtype(comparable)) :void= # Execute a classificação por mesclagem e traçar o perfil do código. ResultArray := profile(Description): SortingAlgorithms.MergeSort(ActualArray, Compare)
# Imprima as matrizes Real, Esperada e Resultado para solução de problemas. ProjectLog("Actual: {ArrayToString(ActualArray)}") ProjectLog("Expected: {ArrayToString(ExpectedArray)}") ProjectLog("Result: {ArrayToString(ResultArray)}")
# Verifique se o teste foi aprovado e classificou a matriz conforme esperado. for (Index -> Result : ResultArray, Expected := ExpectedArray[Index]): Resultado = Esperado ~~~
Como escrever casos de teste
Comece de forma simples. Isole o que está sendo testado. Teste uma matriz de números inteiros para classificar do menor para o maior valor.
-
Crie uma função de comparação para números inteiros.
~~~(verse) # Função de comparação a ser usada como argumento para classificar os inteiros do menor para o maior número. IsIntSmallerThan(Left:int, Right:int)
:int= Left < Right ~~~ -
Crie uma função auxiliar para converter uma matriz de números inteiros em uma string.
~~~(verse) # Função auxiliar para converter uma matriz de números inteiros em uma string. IntArrayToString(Array:[]int)
:string= var ConvertedToString:string = "[" for (Index -> Element : Array): set ConvertedToString += "{Element}" if (Index < Array.Length - 1): set ConvertedToString += ", " set ConvertedToString += "]" ~~~ -
Crie um dispositivo Verse para executar o teste.
# Esse arquivo contém testes para verificar se o algoritmo de classificação se comporta conforme o esperado. # Os testes também incluem a criação de perfil do código para ver o desempenho do algoritmo. using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/Diagnostics } using { /Verse.org/Simulation } using { /Verse.org/Random } my_log_channel<public> := class(log_channel): # Um "Logger" no âmbito do projeto para imprimir mensagens de funções que não estão em uma classe com log. # A impressão que não é do Logger é <no_rollback> e, portanto, não pode ser usada em uma função <transacts>. ProjectLog<public>(Message:[]char, ?Level:log_level = log_level.Normal)<transacts>:void= Logger := log{Channel := my_log_channel} Logger.Print(Message, ?Level := Level) # Um dispositivo do Modo Criativo criado em Verse que pode ser colocado em um nível. # Coloque no nível para executar testes e o código de perfil. test_sorting := class(creative_device): # É executado quando o dispositivo é iniciado em um jogo em execução OnBegin<override>()<suspends>:void= # Exemplo de matrizes a serem usadas no teste. Test1ExpectedIntArray:[]int = array{} Test1ActualIntArray:[]int = array{} # Execute os testes e informe se eles falharam ou foram bem-sucedidos. if: RunArrayTest["Matriz de números inteiros em ordem do menor para o maior", Test1ActualIntArray, Test1ExpectedIntArray, IsIntSmallerThan, IntArrayToString] then: ProjectLog("Todos os testes foram aprovados.") else: ProjectLog("Um ou mais testes falharam.")
-
Agora, preencha as matrizes de teste com valores.
var ArrayLength:int = GetRandomInt(10, 100) Test1ExpectedIntArray:[]int = for (Count := 0..ArrayLength): Contagem Test1ActualIntArray:[]int = Shuffle(Test1ExpectedIntArray)
-
Execute o teste arrastando o dispositivo Verse até o seu nível
A seguir, há mais ideias de testes que você pode executar:
* Classifica uma matriz de números inteiros do maior para o menor.
# Função de comparação a ser usada como argumento para classificar os inteiros do maior para o menor número.
IsIntGreaterThan(Left:int, Right:int)<decides><transacts>:int=
Left > Right
~~~
* Classifica uma matriz de números inteiros que tem valores repetidos.
~~~(verse)
Test3ExpectedIntArray:[]int = array{0, 1, 1, 2, 3, 3}
Test3ActualIntArray:[]int = Shuffle(Test3ExpectedIntArray)
* Classifique uma classe por um dos seus campos.
# Uma classe de exemplo para testar a classificação.
player_stats := class<unique>:
Wins:int = 0
RoundsPlayed:int = 0
# Função de comparação a ser usada como argumento para classificar os ganhos do menor para o maior.
IsWinsSmallerThan(Left:player_stats, Right:player_stats)<decides><transacts>:player_stats=
Left.Wins < Right.Wins
Esquerda.
# Função auxiliar para converter os valores de comparação das estatísticas do jogador em uma string.
PlayerStatsWinsArrayToString(Array:[]player_stats)<transacts>:string=
var ConvertedToString:string = "["
for (Index -> Element : Array):
set ConvertedToString += "{Element.Wins}"
if (Index < Array.Length - 1):
set ConvertedToString += ", "
set ConvertedToString += "]"
~~~
### Script completo
~~~(verse)
# Esse arquivo contém testes para verificar se o algoritmo de classificação se comporta conforme o esperado.
# Os testes também incluem a criação de perfil do código para ver o desempenho do algoritmo.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
my_log_channel<public> := class(log_channel):
# Um "Logger" no âmbito do projeto para imprimir mensagens de funções que não estão em uma classe com log.
# A impressão que não é do Logger é <no_rollback> e, portanto, não pode ser usada em uma função <transacts>.
ProjectLog<public>(Message:[]char, ?Level:log_level = log_level.Normal)<transacts>:void=
Logger := log{Channel := my_log_channel}
Logger.Print(Message, ?Level := Level)
# Um dispositivo do Modo Criativo criado em Verse que pode ser colocado em um nível.
# Coloque no nível para executar testes e o código de perfil.
test_sorting := class(creative_device):
# É executado quando o dispositivo é iniciado em um jogo em execução
OnBegin<override>()<suspends>:void=
# Exemplo de matrizes a serem usadas no teste.
var ArrayLength:int = GetRandomInt(10, 100)
Test1ExpectedIntArray:[]int =
for (Count := 0..ArrayLength):
Contagem
Test1ActualIntArray:[]int = Shuffle(Test1ExpectedIntArray)
set ArrayLength = GetRandomInt(10, 100)
Test2ExpectedIntArray:[]int = for (Count := 0..ArrayLength):
ArrayLength - Count
Test2ActualIntArray:[]int = Shuffle(Test2ExpectedIntArray)
Test3ExpectedIntArray:[]int = array{0, 1, 1, 2, 3, 3}
Test3ActualIntArray:[]int = Shuffle(Test3ExpectedIntArray)
set ArrayLength = GetRandomInt(10, 100)
Test4ExpectedPlayerStats:[]player_stats = for (Count := 0..ArrayLength):
player_stats:
Wins := ArrayLength - Count
RoundsPlayed := GetRandomInt(Count, Count * 30)
Test4ActualPlayerStats:[]player_stats = Shuffle(Test4ExpectedPlayerStats)
# Execute os testes e informe se eles falharam ou foram bem-sucedidos.
if:
RunArrayTest["Matriz de números inteiros em ordem do menor para o maior", Test1ActualIntArray, Test1ExpectedIntArray, IsIntSmallerThan, IntArrayToString]
RunArrayTest["Matriz de números inteiros em ordem do maior para o menor", Test2ActualIntArray, Test2ExpectedIntArray, IsIntGreaterThan, IntArrayToString]
RunArrayTest["Matriz de números inteiros em ordem do menor para o maior com repetições", Test3ActualIntArray, Test3ExpectedIntArray, IsIntSmallerThan, IntArrayToString]
RunArrayTest["Matriz de estatísticas do jogador em ordem do maior número de vitórias para o menor", Test4ActualPlayerStats, Test4ExpectedPlayerStats, IsWinsGreaterThan, PlayerStatsWinsArrayToString]
then:
ProjectLog("Todos os testes foram aprovados.")
else:
ProjectLog("Um ou mais testes falharam.")
# Função de teste para classificar matrizes e criar um perfil do código.
RunArrayTest(Description:string, ActualArray:[]t, ExpectedArray:[]t, Compare(L:t, R:t)<decides><transacts>:t, ArrayToString(:[]t)<transacts>:string where t:subtype(comparable))<decides><transacts>:void=
# Execute a classificação por mesclagem e traçar o perfil do código.
ResultArray := profile(Description):
SortingAlgorithms.MergeSort(ActualArray, Compare)
# Imprima as matrizes Real, Esperada e Resultado para solução de problemas.
ProjectLog("Actual: {ArrayToString(ActualArray)}")
ProjectLog("Expected: {ArrayToString(ExpectedArray)}")
ProjectLog("Result: {ArrayToString(ResultArray)}")
# Verifique se o teste foi aprovado e classificou a matriz conforme esperado.
for (Index -> Result : ResultArray, Expected := ExpectedArray[Index]):
Resultado = Esperado
# Função auxiliar para converter uma matriz de números inteiros em uma string.
IntArrayToString(Array:[]int)<transacts>:string=
var ConvertedToString:string = "["
for (Index -> Element : Array):
set ConvertedToString += "{Element}"
if (Index < Array.Length - 1):
set ConvertedToString += ", "
set ConvertedToString += "]"
# Função auxiliar para converter os valores de comparação das estatísticas do jogador em uma string.
PlayerStatsWinsArrayToString(Array:[]player_stats)<transacts>:string=
var ConvertedToString:string = "["
for (Index -> Element : Array):
set ConvertedToString += "{Element.Wins}"
if (Index < Array.Length - 1):
set ConvertedToString += ", "
set ConvertedToString += "]"
# Função de comparação a ser usada como argumento para classificar os inteiros do menor para o maior número.
IsIntSmallerThan(Left:int, Right:int)<decides><transacts>:int=
Left < Right
# Função de comparação a ser usada como argumento para classificar os inteiros do maior para o menor número.
IsIntGreaterThan(Left:int, Right:int)<decides><transacts>:int=
Left > Right
# Uma classe de exemplo para testar a classificação.
player_stats := class<unique>:
Wins:int = 0
RoundsPlayed:int = 0
# Função de comparação a ser usada como argumento para classificar os ganhos do menor para o maior.
IsWinsSmallerThan(Left:player_stats, Right:player_stats)<decides><transacts>:player_stats=
Left.Wins < Right.Wins
Esquerda.
# Função de comparação a ser usada como argumento para classificar os ganhos do maior para o menor.
IsWinsGreaterThan(Left:player_stats, Right:player_stats)<decides><transacts>:player_stats=
Left.Wins > Right.Wins
Esquerda.
Por conta própria
-
Implemente um algoritmo de classificação diferente. Você pode encontrar as seguintes outras implementações de algoritmos:
-
Adicionar mais casos de teste para aumentar a cobertura do testador. Você consegue pensar em casos extremos que o testador não cobre atualmente? Ou o que dizer de tipos de dados mais complexos?