How does find by example work in the Pharo Finder

Uma das coisas que se parece com mágica é a busca de métodos através de exemplos. Mas veremos que não há nenhuma magia negra nisso se levarmos em conta a fantástica capacidade de reflexão do Pharo Smalltalk. A ferramenta está disponível desde o Squeak, do qual o Pharo descende.

Vamos fazer um experimento com o Finder. Vamos fornecer um array desordenado (shuffled) e o mesmo array ordenado. Veja Como encontrar métodos com base em exemplos no Tutorial de Pharo.

Usando o Finder

Abra o Finder.

Forneça os arrays.

Veja os resultados.

Finder Script

Agora queremos criar um script para fazer o mesmo, de forma aproximada.

Evitaremos a criação de novas classes e métodos usando block closures.

Vamos desenvolver o script aos poucos. Inicialmente verificaremos os resultados parciais da computação.

Começamos obtendo os parâmetros do exemplo e a hierarquia de classes que podem conter os métodos buscados.

O exemplo é passado como um array. O primeiro elemento é o receptor da mensagem. O último é o valor retornado. Os valores intermediários são os argumentos da mensagem.

A closure interna hierarchy recursivamente navega subindo na hierarquia de classes.

No nosso caso são obtidas 4 classes. Excluímos as classes Object e superclasses dela.

Vamos obter todos os métodos na hierarquia de classes.

Usamos flatCollect: em vez de collect: para obter o flatten do array de arrays. Cada classe retornaria um array com os seus métodos dentro de um outro array caso usássemos o collect:.

Vemos que obtivemos muitos métodos que não são de interesse já que o nosso exemplo é um array com dois elementos. Desse array obtemos o receptor e o resultado do método. Não há argumentos. Logo a mensagem é unária, com nenhum argumento. Por isso vamos filtrar mais os métodos.

Filtraremos inicialmente pelo número de argumentos.

Agora só temos métodos unários na lista.

E finalmente vamos acrescentar o filtro para os métodos que retornam o resultado do exemplo.

Tomamos o cuidado de prever a possibilidade de ocorrer erro no método. Neste caso ele é excluido da lista.

Abaixo o código do script:

findMethodByExample := [ :example | | receiver arguments result class hierarchy |
	
	receiver := example first.
	arguments := example allButFirst allButLast.
	result := example last.
	class := receiver class.
	
	hierarchy := [ :clazz | 
		clazz = Object ifTrue: [ {  } ] ifFalse: [  
			{ clazz }, (hierarchy value: clazz superclass)
		].
	].

	((hierarchy value: class) flatCollect: [ :aClass | aClass methods ]) 
		select: [ :method | 
			method numArgs = arguments size 
			and: [ 
				[((Message selector: method selector arguments: arguments) sendTo: receiver deepCopy) = result] 
				on: Error do: [ false ] ]
		].
	
].

findMethodByExample value: { #(1 2 3 4 5 6 7 8 9) shuffled. #(1 2 3 4 5 6 7 8 9) }.

Combinando resultados

Vamos agora experimentar combinar os resultados de vários exemplos simultâneos para reduzir o tamanho das possibilidades.

Vamos imaginar que estamos querendo obter o nome do método para arredondar um número.

Nosso exemplo abaixo traz vários métodos não relacionados ao arredondamento.

O nosso exemplo é um pouco artificial mas servirá para ilustrar a ideia.

Tentamos então outro exemplo. E a coisa melhora.

Resolvemos então combinar os dois exemplos e a resultado fica mais estreito.

Combinando resultados com o Finder

Podemos fazer o mesmo com o Finder? Talvez…

A pergunta How does find-by-example work in the Pharo Finder? no site Stack Overflow sugere que investiguemos a classe MethodFinder.

Nela há dois métodos:

findMethodsByExampleInput:andExpectedResult: e
possibleSolutionsForInput:

O primeiro invoca o segundo e tem argumentos compatíveis com o que precisamos.

Repetimos com o MethodFinder os exemplos { 2.4. 2 } e { 2.5. 3 }.

Podemos ingenuamente tentar obter a combinação dos exemplos e não obter nada!

A razão do fracasso é devida ao fato do método MethodFinder>>findMethodsByExampleInput:andExpectedResult: retornar uma coleção de instâncias da classe MethodFinderSend.

Observamos que há o método MethodFinderSend>>selector que podemos usar para corrigir o problema.

Agora obtivemos a lista, com um só elemento para este caso, de seletores de métodos.

Se clicar na lista vai obter mais informações sobre os métodos que implementam a mensagem representada pelo seletor.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s