Google Developer Day – Questão 5

E aí galera, tranquilo?

Eu queria postar sobre o DevQuiz do Google faz um tempinho, em particular a questão número 5 que era um pouco mais complexa, mas não achei correto publicar a minha solução enquanto as inscrições ainda rolavam, mas agora que já elvis, gostaria de publicá-la aqui.

A história na verdade começou quando na Agência, ao saber do questionário, a galera de Interface começou a resolver as questões, cada um usando a sua lógica. No final, alguns fizeram e compartilharam suas soluções.

Eu achei interessante isso, porque saiu um pouco da rotina meio “Fábrica de Pastel” e começamos a discutir as soluções propostas de cada um, percebemos que pensamos de forma muito diferente, não pior, não melhor, mas diferente.

Como o tempo contava na hora de responder as perguntas, não se pode criticar o código final seja lá qual for o problema, se de resolução, performance, design ou qualquer outro…

E como defensor ferrenho do Javascript, entusiasta, estudante e escritor do assunto ( já que tenho o blog ) acertei na mosca a resposta, certo?

BÉEEEEEEEEEE! FAIL!

Dog Fail

Meu script foi considerado o mais dificil de se entender ( o que é muito subjetivo ) e no final das contas não funcionou retornando a resposta certa =\.

DAMN!!!

Maaasssss felizmente acabei aprendendo coisas interessantes nos códigos alheios, o que valeu a brincadeira.

Claro, após revisto os erros, reescrevi o código envolvendo as funções soltas em um objeto e preservei TODA a lógica, removendo as redundâncias.

O primeiro código fail foi esse:

var double_n = function(string){
	    return !!string.match(/1{2}|2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|9{2}|0{2}/g)
	}

	var soma_n = function(string){
	    var cont = 0
	    string.replace(/\d/g, function(d){
	        cont += +d
	        return d
	    })
	    return !(!!(!cont % 2))
	}

	var last_first = function(string){
	    if (string.charAt(0) == string.charAt(string.length - 1)) 
	        return false
	    return true
	}

	//Telefone é o textarea com os números
	var array = $('#telefone').val().split(/\n/)
	//O último elemento vinha vazio por causa do último \n
		array.pop()
			
	var contador = 0
	$.each(array, function(i){
	    if (double_n(array[i]) && soma_n(array[i]) && last_first(array[i])) 
	        contador++
	})
	console.log(contador)

Meu erro foi a mania que tenho de querer me certificar de que o retorno de uma condição retorne um valor booleano de fato ao invés de um Truthy ou Falsy. Ou seja, num retorno de string vazia, tenho a mania de transformar antes em um boolean tipo: !!””

Com isso, errei ali no retorno da soma_n, na precedência dos operadores unários.

Mas acabei aprendendo com esse erro e além disso aprendi a melhorar minha RegExp verbosa, por uma bem mais simples na solução do líder técnico da área.

Aí vai o código reescrito com as modificações necessárias, removi algumas redundâncias, mas a lógica se mantém a mesma:

var Resolution = {

	of :function(array, cont){
		var el = null
		var i = 0
			with( this ) while( el = array[i++] )
				cont += if_hasnt_dup(el) && 
					if_sum_isnt_mod2(el) && 
					if_last_isnt_first(el) ? 1:0
		return cont
	},

	if_sum_isnt_mod2 :function(string){
		var cont = 0
		string.replace(/\d/g, function(d){ cont += +d })
		return !(cont % 2)
	},

	if_hasnt_dup :function(string){
		return !(!!string.match(/(\d)\1/g))
	},

	if_last_isnt_first :function(string){
		return !(string.charAt(0) == string.charAt(string.length - 1))
	}
}

alert( Resolution.of(arr, 0) )

Eu usei um with ali, vocês sabem que é horrível e que jamais devemos usar o with, mas eu coloquei ali para dificultar ainda mais o código rsrsrs.

Para quem programa em Javascript, a resolução do problema foi bem rápida, uma porque a própria linguagem permite isso a outra porque era só botar o código no firebug e executar! Que foi o que eu fiz e imagino que muitos outros fizeram também.

Também fiz em Ruby, usando a mesma lógica que usei em Javascript:

require 'jcode'
		def sum_isnt_mod2?(string) 
			cont = 0
			string.each_char { |c| cont+= c.to_i }
			(cont%2).zero?
		end
		
		def hasnt_dup?(string)
			!(string =~ /(\d)\1/) 
		end
		
		def last_isnt_first?(string)
			!(string[0,1] == string[-1,1])
		end
		
		def answer (arr,count)
			arr.each do |v|
				count += 1 if sum_isnt_mod2?(v) && hasnt_dup?(v) && last_isnt_first?(v) 			
			end
			count
		end	
	
	puts answer(array, 0)

Em ambas as linguagens, resolvi considerando os números de telefones como strings ao invés de números, porque achei muito mais fácil.

Você que está lendo o blog agora, resolveu a questão? Acertou ? Errou ?
Posta sua solução nos comentários, acertando ou não tenho certeza que há algo para aprender no código, sempre há…

Se resolveu usando outra linguagem de programação, MELHOR AINDA, poste para mostrarmos as diferenças das linguagens, seria muito interessante. 😉

Obrigado mais uma vez pela atenção galera, um grande abraço!

[Update]
A Carmen Sachetto que faz o Curso de Ciência da Computação comigo no Mackenzie, fez esta solução em Scheme!!

Muito foda, olha só:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 		No peculiar condado de Compiladorshire, todos os telefones têm 6 dígitos.            ; 
; 		A companhia telefônica estabelece as seguintes regras sobre os números:              ;
;		Não pode haver dois dígitos consecutivos idênticos, porque isso é chato;             ;
;		A soma dos dígitos tem que ser par, porque isso é legal;                             ;
;		O último dígito não pode ser igual ao primeiro, porque isso dá azar.                 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   	função principal        ;
; entrada: uma lista com listas ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (Principal lista)
  (if (null? lista) '()
  (if (= (Verifica (car lista)) 0) (cons (car lista) (Principal (cdr lista)))
  (Principal (cdr lista)))))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;    Verifica se um numero de telefone é valido   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (Verifica lista)
  (if (= (Iguais (cdr lista) (car lista)) 
         (Epar (somaElem lista 0)) 
         (PrimUlt (cdr lista) (car lista) (car lista)) 
         0) 0
  (if (not (= (Iguais (cdr lista) (car lista)) 
         (Epar (somaElem lista 0)) 
         (PrimUlt (cdr lista) (car lista) (car lista)) 
         0) ) 1)))    

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   verifica se ultimo elemento é igual a primeiro                         ;
;   se ultimo elemento for igual ao primeiro, retorna 1, senão retorna 0   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (PrimUlt lista primeiro anterior)
  (if (null? lista) (comparaElem primeiro anterior)
  (PrimUlt (cdr lista) primeiro (car lista))))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     compara dois elementos    ;
;   se forem iguais, retorna 1  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (comparaElem a b)
(if ( = a b) 1
(if (not ( = a b)) 0)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  soma elementos de uma lista  ;
;       retorna soma            ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (somaElem lista soma)
(if (null? lista) soma
(somaElem (cdr lista) (+ soma (car lista)))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     verifica se um número é par ou não     ;
;    se for par retorna 0, senão retorna 1   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (EPar numero)
  (if (= (remainder numero 2) 0) 0
  (if (not (= (remainder numero 2) 0)) 1)))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   verifica se há dois dígitos consecutivos idênticos  ;
;                 se houver, retorna 1                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (Iguais lista anterior)
  (if (null? lista) 0
  (if (= anterior (car lista)) 1
  (Iguais (cdr lista) (car lista)))))

Bem legal né ?!?!?!

O Mvc e o Javascript – A parte Server

Eta feriado prolongaaaaaado, acordei no sábado mais cedo só pra ficar mais tempo sem fazer porra nenhuma…

Andei meio sumido mas cá estou eu para mais um post da nossa série sobre MVC em javascript. Demorou mais saiu a segunda parte do post, não será a última, porque é um assunto um pouco complexo, ao meu ver…

Tudo bem, lembram que eu comentei sobre as premissas do nosso mini-framework MVC em javascript? Pois então, deixem elas de lado por enquanto, porque neste post vou querer mostrar um pouco da parte server, que não deve ser ignorada, mesmo que nosso papel seja a Interface.

Por que temos que tratar da parte Server?

Primeiro porque é importante para qualquer Desenvolvedor, Engenheiro ou Arquiteto de Interface que trabalha efetivamente com Javascript ter conceitos bons em programação e ter noções de como a parte Server funciona, até para pensar numa boa solução para um problema que envolve as duas partes, Server e Client-side.

E segundo, é o Server que é responsável pela indexação do site, uma vez que o Javascript ( por enquanto ) nessa parte falha. Então o site DEVE FUNCIONAR SEM JAVASCRIPT!!!!

É muito importante que se diga isso, então, nada de fazer a validação do seu formulário apenas no Javascript!!!!

Então para que serve o Javascript?
Para otimizar o site e melhorar a experiência do usuário e claro, para viadagens também. Eu tenho visto que ultimamente o Javascript tem outra serventia, fazer propaganda do HTML5. Sim, porque mostram um site com um monte de luzinhas, vacas voando, efeitos 3D e o carayo a quatro e dizem: Olha!!! HTML5!!!

Senhores, aquilo é Javascript né… pode usar as API do HTML5, mas é JAVASCRIPT. Portanto, não fique achando que vai poder fazer todos aqueles efeitos aprendendo apenas HTML5. Mas deixando toda essa papagaiada de HTML5 de lado, vamos falar do que nos interessa.

Então, a idéia é montar nossa aplicação funcionando todinha sem javascript. E o que vai ser nossa aplicação ???

Uma galeria de imagens!!! \o/

Regras de negócio:
– A galeria terá uma área de visualização da imagem.
– Terá também os Thumbnails, que são as versões em miniatura das imagens.
– Ao clicar em um thumbnail, a página exibirá sua versão de tamanho original na área de visualização da imagem.

Um dos motivos que me levou a fazer esse post do lado Server é que eu erroneamente achava que para se fazer uma aplicação que funcionasse com ou sem javascript fosse duas vezes mais demorado de se fazer e que a parte server teria de ser feita duas vezes, uma para funcionar com javascript e outra para funcionar sem.
NOT!

Depende de como programa a sua parte Server. Quem me ajudou a compreender isso foi o William Martins, ( aka Zóio ) que trabalha na agência F.biz comigo, quando me disse que eu estava enganado. Decidi então fazer a parte Server da nossa galeria para provar para mim mesmo de que era possível fazer uma aplicação ajax com retrabalho nulo em Server-side.

Então este post servirá também para mostrar à outros céticos ( como eu era ) de que não é necessário esforço a mais por se usar ajax.

Me perdoem os experientes programadores back-end, vou usar querystrings feias mesmo e vou direto ao assunto, não vou ficar tratando os dados nem exceções, nem tentar proteger a localização das imagens porque minha preocupação é inteiramente a Interface.

Vou fazer a aplicação em php, porque estou engatinhando ainda em rails, e gostaria de fazer tudo em Ruby puro, mas meu server não dá suporte então pela facilidade e velocidade, vai o nosso querido PHPzão.

Eu montei uma Classe no php que pega dois parâmetros, o primeiro é localização da pasta das imagens grandes, e o segundo o nome da pasta que vai conter as versões em miniatura.
A classe assume que você tem uma pasta no seu server contendo as imagens, e dentro dessa pasta uma outra pasta contendo as versões em miniatura com os mesmos nomes das imagens maiores, utilizei esse padrão apenas para facilitar na programação, nenhum motivo especial.

Utilizei então 4 arquivos php, para criar essa aplicação.
wallpaper.class – Classe de paginação
config.php – Arquivo de configuração
index.php e home.php – Index contendo o html geral e o home.php contendo apenas o conteúdo da home.

A classe de paginação ficou assim:

<?php
	
	class Pagination {
		
		private 
			$q, $pg, $n, $url, $thumbnail,
			$images, $size, $pages, $html;
				
		public $page;
		
		function __construct( $folder, $thumb_folder ){
		
				$this->url = $folder;
				$this->q = $_GET["q"];
				$this->pg = $_GET["pg"];
				$this->n = $_GET["n"];
				
				$this->thumb_folder = $thumb_folder;
				$this->thumb_name = array_pop( explode('/', $thumb_folder) );
				
				$this->images = $this->rearray( scandir( $this->n ) );	
				$this->size = sizeof( $this->images );
				$this->pages = floor( $this->size / $this->q ) + ( ($this->size % $this->q) ? 1:0 );
				
				$this->start();
			
		}
		
		private function rearray($nArray){
			$aux = array();
				foreach( $nArray as $v )
					if( !( $v==".." || $v=="." || $v==$this->thumb_name ))
						array_push($aux, $v);
			return $aux;
		}
		
		private function start(){
			
			$this->page->name = $this->n;
			$this->page->current = $this->pg;	
			$this->page->image = array();
			$this->page->list = array();
						
				for($i=1; $i<=$this->pages; $i++)
					array_push($this->page->list, "q=" . $this->q . "&pg=" . ($i) . "&n=".$this->n );
						
				for( $i=( ( $this->pg-1) * $this->q ); $i < (($this->pg-1)*$this->q)+1+($this->q-1); $i++)
					
					if($i>$this->size-1) 
						break;
					
					else
						array_push(
							$this->page->image, 
							array(
								"link" => $this->url. $this->n ."/". $this->images[$i],
								"url"  => $this->url. $this->n . $this->thumb_folder .'/'. $this->images[$i]
							) 
						);
						
			$this->page->preview = $this->page->image[0]['link'];
		}
	}
?>


Ela pega parâmetros via Query String:
q : Quantidade de thumbnails que a aplicação deve mostrar
n : Nome da psta contendo as imagens de tamanho original
pg: Página que se deseja visualizar

E constrói listas que contém a localização das imagens em miniatura, e das originais, com os parâmetros de acordo com sua página dinâmicamente.

Não sei se perceberam, mas roubei a lógica de paginação que fiz em Javascript. =D

Bom, a classe ainda guarda algumas informações importantes que serão úteis na hora da renderização, mas não quero ficar esmiuçando este código.

Tem a parte da home.php, que é onde eu faço a iteração na lista e gero o html final:




<div id="vitral">
	<img src="<?php echo $html->preview ?>" />
</div>

<div id="jsPagination">
	
	<ul id="thumbnails" class="hoverbox jsPagination">			
		<?php foreach($html->image as $image):?>
			<li>	
				<a href="?<?php echo $html->list[$html->current-1] . '&preview='. $image['link'];?>" class="thumb-link" >
					<img src="<?php echo $image['url']?>" class="preview" />
					<img src="<?php echo $image['url']?>" />
				</a>
			</li>
		<?php endforeach ?>			
	</ul>

	<ul id="pages" class="jsP-pages">								
		<?php foreach($html->list as $key => $list):?>
			<li>
				<a href="?<?php echo $list?>" class="<?php echo $key+1 == $html->current? 'current' : '' ?>">
					<?php echo $key+1?>
				</a>
			</li>
		<?php endforeach?>							
	</ul>
	
</div>

E agora a parte onde eu quero realmente chegar com este post. Que é a prova de que não é necessário refazer toda a lógica usada para fazer a parte Ajax depois de feita a aplicação sem javascript. Como eu armazenei os dados dos links, dos thumbnails e das imagens de tamanho original em listas, como fica a resposta Ajax?

Simples… assim:

config.php

if(isset($_GET['ajax'])){		
	echo json_encode( array($html->list, $html->image) );
	exit();
}

Simples assim…verifico se existe uma query string ajax na url e retorno apenas o JSON.
Uma maneira mais elegante seria verificar se a requisição que está sendo feita é uma requisição Ajax ao invés de usar QueryStrings, mas isso não é importante pra gente.

O importante é mostrar que o trabalho de se fazer duas versões do site não gera retrabalho, não é preciso refazer toda a lógica ou fazê-la duas vezes.

Então nossa aplicação está pronta, e funcionando sem Javascript =D.

Agora sim, podemos pensar no Javascript e como usar o Pattern MVC através dele.

Mas isso é assunto para o próximo post.

Aqui está o link com a aplicação funcionando e aqui está o link para o código fonte para quem quiser usar a galeria ou ver como fiz a paginação. Aceito sugestões, caso tenha algum programador back-end lendo o blog =).

Prometo que o próximo post desta série não vai demorar a sair…rsrs

Um grande abraço.