How does find by example work in the Pharo Finder

See an English version (via automatic translation with Google Translator) in How does find by example work in the Pharo Finder [Englished].

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.

2 comentários

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