<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Superfície Reflexiva &#187; Conceitos</title>
	<atom:link href="http://logbr.reflectivesurface.com/categoria/conceitos/feed/" rel="self" type="application/rss+xml" />
	<link>http://logbr.reflectivesurface.com</link>
	<description>Ainda movido por uma contradição em termos</description>
	<lastBuildDate>Thu, 29 Jul 2010 03:37:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Conceitos de Programação: Complexidade Ciclomática</title>
		<link>http://logbr.reflectivesurface.com/2008/11/12/conceitos-de-programacao-complexidade-ciclomatica/</link>
		<comments>http://logbr.reflectivesurface.com/2008/11/12/conceitos-de-programacao-complexidade-ciclomatica/#comments</comments>
		<pubDate>Thu, 13 Nov 2008 02:23:39 +0000</pubDate>
		<dc:creator>Ronaldo</dc:creator>
				<category><![CDATA[Conceitos]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[Tecnologia]]></category>

		<guid isPermaLink="false">http://logbr.reflectivesurface.com/2008/11/12/conceitos-de-programacao-complexidade-ciclomatica/</guid>
		<description><![CDATA[Este é o quarto artigo em uma série sobre conceitos de programação. Os demais artigos podem ser encontrados na página de resumo sobre a série. Como nos demais artigos, um aviso: o tratamento dado aos assuntos aqui é informativo e podem existir erros nos mesmos. Como nos demais artigos neste blog, comentários e correções são [...]]]></description>
			<content:encoded><![CDATA[<p>Este é o quarto artigo em uma série sobre conceitos de programação. Os demais artigos podem ser encontrados na <a href="http://logbr.reflectivesurface.com/categoria/conceitos/">página de resumo sobre a série</a>. Como nos demais artigos, um aviso: o tratamento dado aos assuntos aqui é informativo e podem existir erros nos mesmos. Como nos demais artigos neste <span class="foreign-word" lang="en">blog</span>, comentários e correções são sempre bem-vindos.</p>

<h3>Complexidade Ciclomática</h3>

<p>Neste quarto artigo, vamos tratar um pouco de <strong>complexidade ciclomática</strong>. Apesar do nome esdrúxulo, complexidade ciclomática (também conhecida como complexidade condicional) é uma métrica simples para determinar, como o próprio nome sugere, a complexidade de um programa estruturado (cíclico). </p>

<p>Essa métrica foi desenvolvida em 1976 por Thomas J. McCabe e reflete diretamente o número de caminhos independentes que um programa pode tomar durante a sua execução. </p>

<p>Qualquer desenvolvedor que já tenha testado código em sua vida, sabe que a quantidade de testes necessária para exercitar um determinado trecho de código é diretamente proporcional à sua árvore decisória. Em outras palavras, quanto mais caminhos o código puder tomar (seja por meios de condicionais ou <span class="foreign-word" lang="en">loops</span>), maior a quantidade de testes necessários. E como veremos abaixo, há realmente uma relação direta entre a complexidade ciclomática e a cobertura de um código.</p>

<h3>Calculando a complexidade ciclomática</h3>

<p>Antes de mostrar exatamente como o cálculo pode ser feito, vamos observar algumas coisas em relação a um programa qualquer. Digamos que você esteja desenvolvendo um programa que lhe dê o maior divisor comum entre dois números. Uma fórmula simples é o <a href="http://en.wikipedia.org/wiki/Euclidean_algorithm">algoritmo de Euclides</a> que pode ser descrito da seguinte forma:</p>

<blockquote>
  <p>Dados dois números naturais <em>a</em> e <em>b</em>, verifique se <em>b</em> é zero. Se sim, <em>a</em> é o maior divisor comum entre os mesmos; caso contrário, repita o processo usando <em>b</em> e o resto da divisão de <em>a</em> por <em>b</em>.</p>
</blockquote>

<p>Esse algoritmo pode ser expresso pelo seguinte programa em Ruby (note que ele não está em Ruby idiomático):</p>

<pre><code>require "test/unit"

def euclid(m, n)
  if n > m
    r = m
    m = n
    n = r
  end
  r = m % n
  while r != 0
    m = n
    n = r
    r = m % n
  end
  n
end

class EuclidTest < Test::Unit::TestCase
  
  SETS = [[5, 10,  5], [2,  6,  2], [11,  7,  1], 
    [80, 64, 16], [2, 2, 2]]
  
  def test_euclid
    SETS.each do |set|
      assert_equal set[2], euclid(set[0], set[1])
    end
  end
  
end</code></pre>

<p>Se o programa acima for executado, ele rodará o caso de teste logo abaixo da função que verificará se a mesma está correta. Você pode adicionar mais casos ao conjunto <code>SETS</code> se desejar.</p>

<p>A função <code>euclid</code> pode ser descrita por um grafo simples que conecta os caminhos entre as várias declarações que a mesma contém. Esse grafo é o mostrado abaixo (clique para expandir):</p>

<p><a href="http://logbr.reflectivesurface.com/wp-content/uploads/2008/11/algoritmo-de-euclides-versao-nao-idiomatica.png"><img src="http://logbr.reflectivesurface.com/wp-content/uploads/2008/11/algoritmo-de-euclides-versao-nao-idiomatica.png" alt="" title="Algoritmo de Euclides (versão não-idiomática)" width="300" height="30" class="alignnone size-full wp-image-1155" /></a></p>

<p>Com base nesse grafo, podemos definir a complexidade ciclomática de um programa da seguinte forma:</p>

<pre><code>CC = A - N + 2C
</code></pre>

<p>Nessa fórmula:</p>

<ul>
<li>CC é a complexidade ciclomática</li>
<li>A é o número de arestas do grafo</li>
<li>N é o número de nós do grafo</li>
<li>C é o número de componentes conectados</li>
</ul>

<p>Como se trata de uma função simples com um único ponto de entrada e saída, o número de componentes é 1 e a fórmula pode ser reduzida para:</p>

<pre><code>CC = A - N + 2
</code></pre>

<p>Se a função possuísse múltiplos pontos de saída, entretanto, a complexidade ciclomática seria definida como:</p>

<pre><code>CC = A - N + C + R
</code></pre>

<p>Nessa fórmula, R é o número de declarações de saída (em Ruby, o número de <code>return</code>s).</p>

<p>Voltando ao grafo mostra na figura, vemos que o mesmo possui 11 nós e 12 arestas, o que nós dá uma complexidade ciclomática de 12 - 11 + 2, ou seja, 3. </p>

<p>Uma outra maneira bem simples de descobrir a complexidade ciclomática é contar o número de <span class="foreign-word" lang="en">loops</span> fechados no grafo (que são formados por condicionais e <span class="foreign-word" lang="en">loops</span>) e somar ao número de pontos de saída. No grafo acima, temos 2 <span class="foreign-word" lang="en">loops</span> fechados (os <code>if</code> e <code>while</code>) e um ponto de saída, resultando no mesmo valor 3 para a complexidade da função.</p>

<p>Uma coisa interessante é que a complexidade permanece a mesma quando a sintaxe de uma linguagem é levada em questão sem alterar a semântica do programa. Tome por exemplo a versão idiomática do algoritmo em Ruby:</p>

<pre><code>def euclid(m, n)
  m, n = n, m if n > m
  m, n = n, m % n while m % n != 0
  n
end</code></pre>

<p>O grafo gerado nesse caso é (clique para expandir):</p>

<p><a href="http://logbr.reflectivesurface.com/wp-content/uploads/2008/11/algoritmo-de-euclides-versao-idiomatica.png"><img src="http://logbr.reflectivesurface.com/wp-content/uploads/2008/11/algoritmo-de-euclides-versao-idiomatica.png" alt="" title="Algoritmo de Euclides (versão idiomática)" width="300" height="43" class="alignnone size-full wp-image-1156" /></a></p>

<p>Node que embora o número de nós e arestas tenha mudado, a relação entre eles não mudou e a complexidade permanece a mesma.</p>

<h3>Testando</h3>

<p>De uma forma geral, o valor da complexidade ciclomática define um limite superior para a quantidade de testes necessários para cobrir todos os caminhos decisórios no código em questão. Esse é um limite superior já que nem todos os caminhos são necessariamente realizáveis.</p>

<p>Disso se infere que quanto menor a complexidade, menor a quantidade de testes necessários <strong>para o método em questão</strong>. Esse fato implica em outro curioso: quebra um método em vários reduz a complexidade dos métodos mas aumenta a complexidade geral do código e, de forma geral, mantém a testabilidade do programa completo no mesmo nível.</p>

<h3>Referências</h3>

<p>Obviamente, já que a complexidade é um valor específico, é possível extrair da mesma uma referência. Baseado no trabalho de McCabe, esses valores de referência são:</p>

<ul>
<li>1-10, métodos simples, sem muito risco</li>
<li>11-20, métodos medianamente complexos, com risco moderado</li>
<li>21-50, métodos complexos, com risco alto</li>
<li>51 ou mais, métodos instáveis de altíssimo risco</li>
</ul>

<h3>Conclusão</h3>

<p>Essa foi uma pequena introdução ao assunto com o objetivo de abrir o caminho para artigos posteriores mostrando ferramentas de apoio ao cálculo e monitoramento da complexidade ciclomática. Como de usual, sugestões e correções são bem vindos.</p>
]]></content:encoded>
			<wfw:commentRss>http://logbr.reflectivesurface.com/2008/11/12/conceitos-de-programacao-complexidade-ciclomatica/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Concurso e Conceitos de Programação</title>
		<link>http://logbr.reflectivesurface.com/2008/03/09/concurso-e-conceitos-de-programacao/</link>
		<comments>http://logbr.reflectivesurface.com/2008/03/09/concurso-e-conceitos-de-programacao/#comments</comments>
		<pubDate>Mon, 10 Mar 2008 00:50:03 +0000</pubDate>
		<dc:creator>Ronaldo</dc:creator>
				<category><![CDATA[Conceitos]]></category>
		<category><![CDATA[Humor]]></category>
		<category><![CDATA[Meta]]></category>
		<category><![CDATA[Tecnologia]]></category>

		<guid isPermaLink="false">http://logbr.reflectivesurface.com/2008/03/09/concurso-e-conceitos-de-programacao/</guid>
		<description><![CDATA[Segundo os logs de acesso deste blog, o dia de hoje representou o momento em que o mesmo ultrapassou os mil leitores conhecidos nos diversos feeds disponíveis. Eu não sei se acredito nas estatísticas ou não, mas vou supor por um momento que sejam verdadeiras e aproveitar para brincar um pouco. Concurso A primeira brincadeira [...]]]></description>
			<content:encoded><![CDATA[<p>Segundo os <span class="foreign-word" lang="en">logs</span> de acesso deste <span class="foreign-word" lang="en">blog</span>, o dia de hoje representou o momento em que o mesmo ultrapassou os mil leitores conhecidos nos diversos <span class="foreign-word" lang="en">feeds</span> disponíveis. Eu não sei se acredito nas estatísticas ou não, mas vou supor por um momento que sejam verdadeiras e aproveitar para brincar um pouco.</p>

<h3>Concurso</h3>

<p>A primeira brincadeira é um concurso. Leitores freqüentes sabem que este ano eu estou pensando bastante sobre a questão de <a href="http://logbr.reflectivesurface.com/index.php?s=paradigmas">paradigmas de programação</a>&#8211;especialmente no que tange ao desenvolvimento Web. O objetivo do concurso é simples: escreva um texto sobre esse assunto. Você pode:</p>

<ul>
<li>Descrever algo que está pensando sobre o assunto</li>
<li>Introduzir algo que aprendeu e que modificou sua forma de pensar sobre desenvolvimento Web</li>
<li>Explicar as falhas do seu <span class="foreign-word" lang="en">framework</span> favorito e como você as corrigiria</li>
<li>Etc</li>
</ul>

<p>De fato, qualquer coisa nesse linha vale. O prêmio vai para o texto que eu pessoalmente considerar mais interessante sobre o assunto. Para participar, basta enviar um e-mail como a URL do texto ou criar um <span class="foreign-word" lang="en">trackback</span> ou <span class="foreign-word" lang="en">pingback</span> para este texto.</p>

<p>E falando no prêmio: eu tenho uma cópia&#8211;nova, intocada&#8211;do <a href="http://www.cc2e.com/">Code Complete</a>, segunda edição, do Steve McConnell, considerado um melhores livros sobre boas práticas em desenvolvimento. Não é autografado, mas é um livro muito bom e acho que cabe bem em qualquer biblioteca de programação.</p>

<h3>Conceitos de Programação</h3>

<p>A segunda brincadeira é tentar continuar a série <a href="http://logbr.reflectivesurface.com/categoria/conceitos/">Conceitos de Programação</a> através de textos de convidados. Se você tem interesse em escrever um texto no estilo dos que eu estava fazendo&#8211;e eu espero poder contribuir novamente a partir da semana que vem&#8211;entre em contato comigo no email mtblog [arroba] reflectivesurface [ponto] com.</p>

<p>O único requisito é que o texto seja similar aos que já foram feitos (balanço de material explicativo e código de exemplo). Obviamente, eu me reservo o direito de adequar o texto ao estilo do Superfície Reflexiva, mas isso não significa mudar o conteúdo do mesmo. Quando muito, inserir alguns sub-títulos, reformatar código para se encaixar na coluna de texto e coisas similares. </p>

<h3>Espalhem</h3>

<p>Agora é hora de testar se existem leitores mesmo. <img src='http://logbr.reflectivesurface.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  Quem sentir vontade, espalhe a notícia do concurso e quem sabe podemos criar um diálogo interessante sobre o tema e presentear alguém.</p>
]]></content:encoded>
			<wfw:commentRss>http://logbr.reflectivesurface.com/2008/03/09/concurso-e-conceitos-de-programacao/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Conceitos de Programação: Big O Notation</title>
		<link>http://logbr.reflectivesurface.com/2008/02/07/conceitos-de-programacao-big-o-notation/</link>
		<comments>http://logbr.reflectivesurface.com/2008/02/07/conceitos-de-programacao-big-o-notation/#comments</comments>
		<pubDate>Thu, 07 Feb 2008 20:04:47 +0000</pubDate>
		<dc:creator>Ronaldo</dc:creator>
				<category><![CDATA[Conceitos]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://logbr.reflectivesurface.com/2008/02/08/conceitos-de-programacao-big-o-notation/</guid>
		<description><![CDATA[Este é o terceiro artigo em uma série sobre conceitos de programação. O primeiro falava sobre Lazy Evaluation e como simular isso em uma linguagem sem suporte nativo ao conceito de uma forma simples mas funcional. O segundo falava sobre Tail Recursion e como aproveitar isso em linguagens que implementam a característica. Antes de começar [...]]]></description>
			<content:encoded><![CDATA[<p>Este é o terceiro artigo em uma <a href="http://logbr.reflectivesurface.com/categoria/conceitos/">série sobre conceitos de programação</a>. O <a href="http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/">primeiro falava</a> sobre <span class= "foreign-word" lang="en"><strong>Lazy Evaluation</strong></span> e como simular isso em uma linguagem sem suporte nativo ao conceito de uma forma simples mas funcional. O <a href="http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/">segundo falava</a> sobre <span class= "foreign-word" lang="en"><strong>Tail Recursion</strong></span> e como aproveitar isso em linguagens que implementam a característica.</p>

<p>Antes de começar o artigo, um aviso: o tratamento que eu estou dando aos assuntos aqui é informativo, com a intenção de incentivar os leitores que desconhecem ou querem aprender sobre o assunto em questão a procurar mais sobre o assunto. Como tal, podem existir erros nos mesmos, derivados da minha própria incompreensão de alguns pontos. Nesse caso, como sempre, a parte de comentários está aberta. Aceito tanto sugestões como correções. Isso vale para todo e qualquer artigo da série. Dito isso, podemos continuar.</p>

<p>Neste terceiro artigo, vamos falar um pouco sobre <span class= "foreign-word" lang="en"><strong>Big O Notation</strong></span>, ou Notação O, que, em teoria da computação é usada para descrever como o tamanho da entrada fornecido a um algoritmo qualquer afeta os recursos que o mesmo utiliza, sejam estes de memória ou tempo de execução. </p>

<p>Conhecer esse tipo de informação sobre um algoritmo é a base de qualquer otimização que possa ser feita sobre o mesmo. Não só para entender como o mesmo opera em termos de eficiência geral mas também para decidir corretamente quando for necessário sacrificar alguma aspecto de performance ou complexidade em favor de outro. Isso vale tanto para uso de CPU, memória como para rede, disco e qualquer outro recurso similar.</p>

<p>A Notação O, na verdade, é uma classe inteira de notações. Nesse artigo vamos cobrir somente uma parte no que tange à análise de complexidade de um algoritmo. </p>

<h3>Complexidade versus performance</h3>

<p>Antes de prosseguirmos, uma distinção deve ser feita aqui. </p>

<p><strong>Performance</strong> é uma medida de quanto dos recursos disponíveis um algoritmo usa. Isso depende tanto do código quanto da máquina onde o mesmo está rodando.</p>

<p><strong>Complexidade</strong>, por outro lado, é como o algoritmo escala, ou seja, o que acontece quando mais e mais dados são passados ao mesmo? Complexidade afeta performance, obviamente, mas o converso não é verdadeiro. </p>

<p>Sendo assim, a Notação O descreve a complexidade do algoritmo e não sua performance. Isso será visto mais adiante com um exemplo.</p>

<h3>Entendendo a notação</h3>

<p>Como mencionado acima, o principal objetivo da notação O, no que tange à descrição de complexidade de um algoritmo é explicar de maneira sucinta como o tamanho do problema afeta o número de operação necessárias para executar o mesmo. Operações podem ser atribuições, comparações, cálculos númericos, leituras, escritas, e assim por diante, tomadas em unidade.</p>

<p>A notação não descreve o número exato de operações, mas está interessada em casos específicos de operação. Em outras palavras, ela é igualmente válida para descrever o melhor caso, o pior caso, ou o caso médio de um algoritmo qualquer. </p>

<p>Para um dado problema, podemos ter as seguintes situações comuns:</p>

<dl>
  <dt>Constante: O(1), ou ordem 1</dt>
  <dd>Isso significa que o uso de recursos é constante seja qual for o tamanho da entrada. Obviamente, esse é o melhor nível de complexidade possível.</dd>
  <dt>Linear: O(n), ou ordem <var>n</var></dt>
  <dd>Isso significa que o uso de recursos é proporcional ao tamanho da entrada.</dd>
  <dt>Logarítmico: O(log n), ou ordem logaritmo <var>n</var></dt>
  <dd>Isso significa que o uso de recursos varia em proporção logarítmico ao tamanho da entrada.</dd>
  <dt>Quadrático: O(n<sup>2</sup>), ou ordem <var>n</var> ao quadrado</dt>
  <dd>Isso significa que o uso de recursos varia em proporção exponencial ao tamanho da entrada.</dd>
</dl>

<p>Para colocar isso em contexto, alguns exemplos:</p>

<ul>
<li>Determinar se um número é par ou ímpar é <strong>O(1)</strong></li>
<li>Encontrar um item em uma lista ordenada por <a href="http://en.wikipedia.org/wiki/Binary_search_algorithm">busca binária</a> é <strong>O(log n)</strong></li>
<li>Resolver o problema do <a href="http://en.wikipedia.org/wiki/Traveling_salesman_problem">caixeiro viajante</a> por força bruta é <strong>O(n!)</strong></li>
</ul>

<h3>A importância da escolha</h3>

<p>Para entender a importância da escolha correta de implementação, podemos usar um exemplo concreto:</p>

<p>O famoso <a href="http://en.wikipedia.org/wiki/Quicksort">Quicksort</a>, em seu caso típico é <strong>O(n log n)</strong>. Em seu pior caso, <strong>O(n<sup>2</sup>)</strong>. Isso para tempo de execução. É claro que isso depende de uma implementação correta do mesmo, mas em qualquer implementação correta, essa é a complexidade.</p>

<p>Em termos de uso de memória, porém, para um implementação correta é sempre <strong>O(n log n)</strong>,  a menos que a execução seja <span class= "foreign-word" lang="en">in-place</span>, caso qual o uso de memória é <strong>O(1)</strong>. Em outras palavras, uma mudança de implementação aqui, mesmo correta, pode ter uma impacto enorme em um dos recursos usados pelo algoritmo.</p>

<p>O caso converso também é verdadeira para a escolha. Há momentos em que você deve saber balancear as expectativas. Se você descobre que o seu algoritmo é <strong>O(n<sup>3</sup>)</strong> e que existe uma forma de transformá-lo em <strong>O(n<sup>2</sup>)</strong> mas que isso vai tomar um ano e tornar o código incompreensível, não faz muito sentido ganhar com a redução por maior que ela <em>aparentemente</em> seja. </p>

<p>Digo aparentemente, porque você pode descobrir que para o tamanho de sua entrada, a complexidade não faz diferença. Se o seu conjunto de entrada é distribuído demais para que o algoritmo caia no melhor caso, faz sentido explorar o pior caso, por exemplo. </p>

<h3>O que não é trivial</h3>

<p>Obviamente, a equação que determinada a eficiência pode expressar qualquer relação. Entretanto, ela não usa constantes ou termos de ordem inferior porque para à medida que <var>n</var> aumenta, o impacto desses termos se torna insignificante. Para entender isso, considere o <a href="http://en.wikipedia.org/wiki/Bubblesort">Bubblesort</a>, um dos piores algoritmos possíveis para ordenação (quase um <a href="http://en.wikipedia.org/wiki/Bogosort">Bogosort</a>):</p>

<p>A implementação trivial em Ruby é a seguinte:</p>

<pre><code>def bubblesort(*items)
  loop do
    swapped = false
    for i in (0..items.size - 2)
      if items[i] > items[i + 1]
        items[i], items[i + 1] = items[i + 1], items[i]
        swapped = true
      end
    end
    break unless swapped
  end
  items
end</code></pre>

<p>Essa implementação é <strong>O(n<sup>2</sup>)</strong>. Você pode reparar que ela gasta <var>n * (n + 1)</var> passagens em média, ou seja, <strong>O(n<sup>2</sup> + n)</strong>, o que é o mesmo que dizer <strong>O(n<sup>2</sup>)</strong>.</p>

<p>Agora, suponha que a implementação seja melhorada:</p>

<pre><code>def bubblesort(*items)
  l = items.size - 1
  loop do
    swapped = false
    l -= 1
    for i in (0..l)
      if items[i] > items[i + 1]
        items[i], items[i + 1] = items[i + 1], items[i]
        swapped = true
      end
    end
    break unless swapped
  end
  items
end</code></pre>

<p>Você verá que agora o algoritmo faz a metade das comparações, porque para antes de chegar aos números que já foram ordenadas. O número de operações agora é <var>n + (n-1) + (n-2) + &#8230; + 1</var>, que dá <var>n(n + 1) / 2</var>. Como demonstrado acima, entretanto, isso ainda é <strong>O(n<sup>2</sup>)</strong>.</p>

<p>A razão é que a entrada ainda influencia exponencialmente a execução. Se você introduzir computar o número de computações feitas verá que esse número cresce exatamente da forma descrita na notação. </p>

<p>Com isso em mãos, uma forma rápida de descobrir quão bem um algoritmo escala é plotar a equação que determina sua complexidade. O crescimento do gráfico mostra isso de forma bem visual. A ordem é:</p>

<h3>Determinando complexidade</h3>

<p>A maneira de determinar complexidade é verificar quantas operações simples o algoritmo precisa fazer antes de terminar sua execução. </p>

<p>Tome os <span class= "foreign-word" lang="en">loops</span> abaixo, por exemplo:</p>

<pre><code>for i in (0..n)
end

for i in (0..m)
end</code></pre>

<p>A complexidade do primeiro é <strong>O(n)</strong> e a do segundo é <strong>O(m)</strong>. A de ambos é <strong>O(n + m)</strong>, que é o mesmo que <strong>O(max(n, m))</strong>. </p>

<p>Veja agora o caso abaixo:</p>

<pre><code>for i in (0..n)
  for j in (0..n)
  end
end</code></pre>

<p>A complexidade agora é <strong>O(n &#42; n)</strong>, ou <strong>O(n<sup>2</sup>)</strong>.</p>

<p>A maioria dos casos pode ser estimada igualmente, verificado as condições que limitam cada caso. Na pior das hipóteses, é possível instrumentar a função e depois lançar os resultados em um programa que gere um gráfico do resultado colhido. Veja o resultado do pior caso da ordenação descrita anteriormente:</p>

<p><img src="http://logbr.reflectivesurface.com/wp-content/uploads/2008/02/cpit.png" alt="Computações versus Itens" align="center"></p>

<h3>Conclusão</h3>

<p>Saber estimar ou pelo menos procurar a complexidade de um algoritmo é fundamental para qualquer programador. Qualquer tipo de otimização mais pesada é irrelevante sem isso. O maior problema está no fato de que certas otimizações são contra-intuitivas como demonstrado acima. Entender as variações se torna, então, a necessidade mais premente para otimizar de maneira adequada.</p>

<p>Obviamente, em muitos casos, uma otimização simples pode ser suficiente para os propósitos do código. Mesmo assim, uma análise um pouco mais profunda pode render benefícios inesperados.</p>
]]></content:encoded>
			<wfw:commentRss>http://logbr.reflectivesurface.com/2008/02/07/conceitos-de-programacao-big-o-notation/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Conceitos de Programação: Tail Recursion</title>
		<link>http://logbr.reflectivesurface.com/2008/02/01/conceitos-de-programacao-tail-recursion/</link>
		<comments>http://logbr.reflectivesurface.com/2008/02/01/conceitos-de-programacao-tail-recursion/#comments</comments>
		<pubDate>Fri, 01 Feb 2008 14:10:15 +0000</pubDate>
		<dc:creator>Ronaldo</dc:creator>
				<category><![CDATA[Conceitos]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://logbr.reflectivesurface.com/2008/02/01/conceitos-de-programacao-tail-recursion/</guid>
		<description><![CDATA[Este é o segundo artigo em uma série sobre conceitos de programação. O primeiro falava sobre Lazy Evaluation e como simular isso em uma linguagem sem suporte nativo ao conceito de uma forma simples mas funcional. Neste segundo artigo, vamos explorar um assunto que possui aplicações bem práticas: Tail Recursion. Como a maioria dos programadores [...]]]></description>
			<content:encoded><![CDATA[<p>Este é o segundo artigo em uma série sobre conceitos de programação. O <a href="http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/">primeiro falava</a> sobre <span class= "foreign-word" lang="en"><strong>Lazy Evaluation</strong></span> e como simular isso em uma linguagem sem suporte nativo ao conceito de uma forma simples mas funcional.</p>

<p>Neste segundo artigo, vamos explorar um assunto que possui aplicações bem práticas: <span class= "foreign-word" lang="en"><strong>Tail Recursion</strong></span>. Como a maioria dos programadores pode atestar, recursividade é uma maneira elegante de resolver várias classes de problemas sem muito esforço. Embora seja um conceito que pode confundir programadores inexperientes, uma marca de um bom programador é saber usar recursividade com eficiência.</p>

<p>Um dos maiores problemas da técnica, é claro, é o consumo espaço na <a href="http://en.wikipedia.org/wiki/Call_stack">pilha</a>. Como cada chamada precisa criar um novo <span class= "foreign-word" lang="en"><a href="http://en.wikipedia.org/wiki/Call_stack#Structure">frame</a></span>, funções profundamente recursivas podem esgotar rapidamente o espaço disponível gerando problemas de difícil depuração. Além disso, há o fato de que é muito fácil escrever uma função recursiva mas não tão fácil escreve uma função que saia rapidamente quando as condições não são apropriadas.</p>

<p>Esses dois problemas dependem muito da capacidade do programador em criar bons algoritmos. Mas o primeiro pode também receber ajuda do compilador em uma otimização conhecida como <span class= "foreign-word" lang="en"><strong>tail call optimization</strong></span>. </p>

<h3>Na prática</h3>

<p>Para mostrar como isso funciona, considere a seguinte função em Ruby:</p>

<pre><code>def factorial(i)
  return 1 if i <= 0
  return i * factorial(i - 1)  
end</code></pre>

<p>Essa é uma implementação trivial de um cálculo de fatorial que sofre do problema mencionado acima. À medida que as chamadas são feitas, o espaço na pilha é eventualmente consumido até que haja um estouro. Na minha máquina isso acontece depois de aproximadamente três mil chamadas para o Ruby 1.8.6 compilado nativamente.</p>

<p>Suponha que você faça a seguinte chamada:</p>

<pre><code>factorial(5)</code></pre>

<p>O resultado é 120 com a seguinte seqüência de chamadas:</p>

<pre><code>factorial(5)
5 * factorial(4)
5 * 4 * factorial(3)
5 * 4 * 3 * factorial(2)
5 * 4 * 3 * 2 * factorial(1)
5 * 4 * 3 * 2 * 1
</code></pre>

<p>Veja que a função é chamada internamente mais quatro vezes e sempre ao final da execução. Apesar disso, esse não é um exemplo de <span class= "foreign-word" lang="en">tail recursion</span>. O motivo é que a multiplicação acontece depois da chamada, ou seja, a invocação é feita ao final mas o retorno só pode ser conhecido com a multiplicação posterior.</p>

<p>Agora, veja a seguinte implementação:</p>

<pre><code>def factorial_accumulate(n, a)
  return a if n <= 1
  return factorial_accumulate(n - 1, n * a)
end

def factorial(n)
  factorial_accumulate(n, 1)
end</code></pre>

<p>Note que agora a chamada vem ao final, mas o resultado não depende de algo posterior. As chamadas seriam assim, para o mesma invocação <code>factorial(5)</code>:</p>

<pre><code>factorial(5)
  factorial_accumulate(5, 1)
    factorial_accumulate(4, 5)
      factorial_accumulate(3, 20)
        factorial_accumulate(2, 60)
          factorial_accumulate(1, 120)
          120
        120
      120
    120
  120
120</code></pre>

<p>É aqui que entra a otimização. </p>

<p>Considerando a forma como funções executam, é fácil perceber que cada chamada poder ser implementada de maneira bem mais eficiente com duas substituições. </p>

<p>Ao invés de criar um novo <span class= "foreign-word" lang="en">frame</span> na pilha, salvar o endereço de retorno, empilhar as parâmetros, executar a função, repetir recursivamente e ao final ir desempilhando com os retornos, é possível substituir os parâmetros na pilha e pular novamente para o início da função. </p>

<p>Em outras palavras, você teria algo assim, em um pseudo-código:</p>

<pre><code>:fa
  
  n <= 1 ?
    goto :fe
  
  n = n - 1
  a = a * n
  
  goto :fa
  
:fe</code></pre>

<p>A otimização está toda centrada no fato de que se a função é chamada de maneira similar, a invocação pode ser substituída por um salto direto precedido por uma troca dos parâmetros de maneira apropriada.</p>

<h3>Um segundo exemplo</h3>

<p>Para ilustrar um pouco mais o tipo de transformação necessária, vamos usar a clássica função de Fibonacci:</p>

<pre><code>def fib(n)
  return 1 if n <= 1
  return fib(n - 1) + fib(n - 2)
end</code></pre>

<p>Essa função também não é <span class= "foreign-word" lang="en">tail recursive</span> mas pode ser transmudada facilmente com a seguinte implementação:</p>

<pre><code>def fib(i, current = 0, next = 1)
  return current if i == 0
  return fib(i - 1, next, current + next)
end</code></pre>

<p>Obviamente, esse é um exemplo bem básico mas ilustra o tipo de mudança necessária para uma otimização.</p>

<h3>Uma pequena distinção</h3>

<p>Na literatura sobre o assunto, dois termos são usados: <span class= "foreign-word" lang="en">tail recursion elimination</span> e <span class= "foreign-word" lang="en">tail call optimization</span>. O primeiro é um caso especial do segundo em que a chamada é para a própria função.</p>

<p>A função <code>factorial_accumulate</code>, mostrada acima, poderia sofrer  <span class= "foreign-word" lang="en">tail recursion elimination</span>, demonstrado no pseudo-código. Já a segunda implementação de <code>factorial</code>, que chama <code>factorial_accumulate</code> poderia sofrer <span class= "foreign-word" lang="en">tail call otimization</span>.</p>

<h3>Implementações</h3>

<p>Esse tipo de otimização é freqüentemente usado linguagens funcionais já que o uso de recursividade declarativa nas mesmas é extremamente comum e não poderia ser implementado da maneira usual sem tornar as mesmas inúteis para qualquer coisa além de operações básicas. De fato, a otimização é necessária para a implementação de certos estilos de programação que eventualmente discutiremos nessa série. </p>

<p>Surpreendentemente, muitas linguagens modernas suportam automaticamente alguma forma dessa otimização. É o caso, por exemplo, da <a href="http://en.wikipedia.org/wiki/Common_Language_Infrastructure">CLI</a>, a infra-estrutura de linguagens por trás do .NET. Embora nem todas as linguagens sob a CLI façam uso da otimização (C# não faz, por exemplo), ela é perfeitamente possível. F#, que é funcional, usa e, ironicamente, <a href="http://blogs.msdn.com/jomo_fisher/archive/2007/09/19/adventures-in-f-tail-recursion-in-three-languages.aspx">C++ dentro do .NET</a> também em certas condições. Linguagens baseadas em CLI podem fazer uso das funcionalidades caso desejem.</p>

<p>Outra linguagem que possui <a href="http://www.lua.org/pil/6.3.html">um suporte muito bom</a> a isso é Lua. O compilador GCC, padrão na maioria das plataformas Unix, também é capaz de otimizar <a href="http://home.in.tum.de/~baueran/thesis/">chamadas recursivas em certos casos</a>.</p>

<p>Erlang, que tem chamada bastante a atenção de desenvolvedores modernos e já está em uso em vários locai como na Amazon, é outra com um bom <a href="http://www.erlang.org/course/advanced.html#space">suporte</a>.</p>

<p>Leitores que usam Ruby ficarão felizes em saber <a href="http://www.trawlr.com/items/1676117">tanto YARV</a> como <a href="http://ola-bini.blogspot.com/2007/01/yarv-tail-call-optimization-in-jruby.html">JRuby</a> já suportam uma forma básica de <span class= "foreign-word" lang="en">tail call optimization</span> e devem eventualmente suportar formas mais complexas.</p>

<h3>O que isso significa</h3>

<p>Conhecer e saber aproveitar as características sutis de uma linguagem é um passo essencial para a maestria na mesma. Muitos problemas podem ser resolvidos com chamadas recursivas próprias que podem acabar sendo otimizadas em muitos compiladores atuais sem que você perceba. </p>

<p>Obviamente, existem casos em que uma implementação recursiva pura é mais interessante para efeitos de performance. Por outro lado, se o problema não exige uma pilha enorme, muitas vezes recursividade é um bom componente para melhorar a legibilidade do código. É necessário saber balancear as duas coisas.</p>

<p>Finalmente, é sempre bom lembrar que, como qualquer técnica, a implementação final depende do compilador ou interpretador. Em muitos casos, recursividade simples pode acabar sendo mais eficiente e se você precisa de máxima performance, é interessante testar variações sobre o tema para realmente identificar o que funciona.</p>

<h3>Conclusão</h3>

<p>Espero que isso tenha ajudado a compreender um pouco do que é <span class= "foreign-word" lang="en">tail recursion</span>. Como sempre, quaisquer dúvidas, correções e sugestões podem ser feitas nos comentários. Na próxima sexta-feira retornamos com mais um artigo.</p>
]]></content:encoded>
			<wfw:commentRss>http://logbr.reflectivesurface.com/2008/02/01/conceitos-de-programacao-tail-recursion/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Conceitos de Programação: Lazy Evaluation</title>
		<link>http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/</link>
		<comments>http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/#comments</comments>
		<pubDate>Fri, 25 Jan 2008 11:15:00 +0000</pubDate>
		<dc:creator>Ronaldo</dc:creator>
				<category><![CDATA[Conceitos]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/</guid>
		<description><![CDATA[Inspirado por alguns pedidos aqui no blog, estou começando uma nova série sobre conceitos de programação. Alguns desses conceitos são úteis como técnicas para melhorar a forma como você resolve problemas em seus programas e outros são úteis como forma de entender o que acontece em determinadas situações quando você está programando de modo a [...]]]></description>
			<content:encoded><![CDATA[<p>Inspirado por alguns pedidos aqui no <span class= "foreign-word" lang="en">blog</span>, estou começando uma nova série sobre conceitos de programação. Alguns desses conceitos são úteis como técnicas para melhorar a forma como você resolve problemas em seus programas e outros são úteis como forma de entender o que acontece em determinadas situações quando você está programando de modo a aproveitar melhor as características das linguagens em questão.</p>

<p>Para começar, vou experimentar com alguns assuntos que me parecem mais interessantes e postar algo sobre os mesmos a cada sexta-feira. Se houver interesse, mantenho o formato. E é claro, estou sempre aberto a quaisquer sugestões de assuntos.</p>

<h3>Lazy Evaluation</h3>

<p>O primeiro tema que vamos tratar aqui é o de <span class= "foreign-word" lang="en"><strong>lazy evaluation</strong></span>. Também conhecida como <span class= "foreign-word" lang="en"><strong>delayed evaluation</strong></span> essa é uma técnica útil para evitar a necessidade de realmente computar um resultado até que o mesmo seja necessário. Isso significa, é claro, que se por algum motivo o resultado não for necessário, o mesmo também não será calculado.</p>

<p>Dois dos efeitos colaterais mais interessantes do uso dessa técnica são a possibilidade de definir estruturas de dados infinitas (como a lista de todos os números primos por exemplo) e definir estruturas de controle sem a necessidade de funções primitivas da própria linguagem. Se feito de maneira correta, o uso de <span class= "foreign-word" lang="en">lazy evaluation</span> é capaz de aumentar consideravelmente a performance de um programa.</p>

<h3>Curto-circuito</h3>

<p>Em linguagens imperativas, principalmente as que não são dinâmicas, um dos usos mais familiares de <span class= "foreign-word" lang="en">lazy evaluation</span> acontece em expressões lógicas. Por exemplo:</p>

<pre><code>rating = (playCount > 10) ? (playCount * 2) : (playCount / 2)</code></pre>

<p>No exemplo acima, dependendo do valor que a variável <code>playCount</code> possuir, somente um dos blocos de código será executado. O outro, compilado ou não, será simplesmente ignorado, embora faça parte da mesma expressão.</p>

<p>Esse é um exemplo bem simples de <span class= "foreign-word" lang="en">lazy evaluation</span> mas ilustra a técnica. Esse, inclusive, é um dos poucos casos em que mesmo linguagens que usam execução estrita de maneira geral (<span class= "foreign-word" lang="en"><a href="http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_reference">call-by-reference</a></span> e <span class= "foreign-word" lang="en"><a href="http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value">call-by-value</a></span>) utilizam esse comportamento para otimizar a execução.</p>

<h3>Estruturas de controle</h3>

<p>Uma função que eu via muito em código ASP era justamente uma forma de criar o operador ternário acima. Aliás, para quem não está familiarizado com esse operador, ele é apenas uma açúcar sintático para a estrutura condicional <code>if</code>. O exemplo usando anteriormente é o mesmo que:</p>

<pre><code>if (playCount > 10)
  rating = playCount * 2;
else
  rating = playCount / 2
end</code></pre>

<p>Em ASP, para emular esse efeito de simplificação, programadores geralmente usavam algo assim:</p>

<pre><code>Function IIf(Expression, TrueValue, FalseValue)
  If Expression Then
    IIf = TrueValue
  Else
    IIf = FalseValue
  End If
End Function</code></pre>

<p>O grande problema desse função é que todos seus parâmetros, por causa da natureza do VBScript, são computados antes da execução da função. Se construirmos um exemplo equivalente ao primeiro, teríamos algo assim:</p>

<pre><code>Rating = IIf(PlayCount > 10, PlayCount * 2, PlayCount / 2)</code></pre>

<p>Ineficientemente, todos os valores acima são computados antes de srem passados adiantw mesmo que somente um deles seja retornado. Em uma linguagem que usasse <span class= "foreign-word" lang="en">lazy evaluation</span>, o usual seria substituir as expressões por uma <em>promessa</em> de execução e só computar o valor quando a mesma fosse necessária. Em uma linguagem assim, a função <code>IIf</code> seria exatamente equivalente à estrutura condicional <code>if</code>.</p>

<h3>Listas infinitas</h3>

<p>Um exemplo muito usado para explicar o assunto, quase que um exemplo canônico, é o cálculo dos números de Fibonacci em uma linguagem <span class= "foreign-word" lang="en">lazy</span> como <a href="http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_value">Haskell</a>. </p>

<p>Para comparar, veja a implementação recursiva abaixo em Ruby:</p>

<pre><code>def fib(n)
  return 1 if n <= 1
  return fib(n - 1) + fib(n - 2)
end</code></pre>

<p>Essa é uma implementação ineficiente, é claro, mas serve para comparar com a implementação similar em Haskell:</p>

<pre><code>fib 0 = 0
fib 1 = 1
fib n = fib(n - 1) + fib(n - 2)</code></pre>

<p>Essa é uma implementação que segue a <a href="http://en.wikipedia.org/wiki/Fibonacci_numbers">definição matemática</a> da função mas que também é bem ineficiente. Essa implementação recursiva geralmente falha por volta do trigésimo quinto item por causa da quantidade de chamadas feitas.</p>

<p>Em Ruby, isso geralmente é resolvido com uma versão iterativa. Em Haskell, podemos resolver da seguinte forma:</p>

<pre><code>fib n = fibs !! n
  where
    fibs = 0 : 1 : zipWith (+) fibs (tail fibs)</code></pre>

<p>Antes de explicar a expressão, é necessário explicar o que <code>zipWith</code> faz. Essencialmente, <code>zipWith</code> pega duas listas e aplica a função às mesmas, elemento por elemento, resultando em uma única lista final. Por exemplo:</p>

<pre><code>*Main> zipWith (*) [1, 2] [3, 4]
[3, 8]</code></pre> 

<p>O uso de <code>(*)</code> tem a ver com outro conceito interessante chamado <span class= "foreign-word" lang="en"><strong>currying</strong></span> que veremos em um artigo futuro.</p>

<p>Essencialmente, então, o que a definição de <code>fib</code> está dizendo é:</p>

<ol>
<li><p>Para o argumento <code>n</code>, <code>fib</code> de <code>n</code> é o elemento <code>n</code> de uma lista chamada <code>fibs</code>. O operador <code>!!</code> é o equivalente Haskell do indexador <code>[]</code> em outras linguagens.</p></li>
<li><p>A lista <code>fibs</code> é definida como a concatenação de 0, 1 e do resultado da concatenação de si mesma com sua cauda com a aplicaçâo de (+). Essa última parte pode soar estranha, mas é exatamente isso o que está acontecendo. Essencialmente, o que você tem aqui é uma lista infinita. Digamos que você queira calcular então o quinto termo da série de Fibonacci. Nesse caso, você teria a seguinte execução:</p></li>
</ol>

<pre><code>0 : 1 : zipWith (+) (0 : 1 : ...) (1 : ...)
0 : 1 : (0 + 1) : zipWith (+) (1 : 1 : ...) (1 : ...)
0 : 1 : (0 + 1) : (1 + 1) : zipWith (+) (1 : 2 : ...) (2 : ...)
0 : 1 : (0 + 1) : (1 + 1) : (1 + 2) : zipWith (+) (2 : 3 : ...) (3 : ...)</code></pre>

<p>O retorno final seria três, que é o quinto item da lista. Se você está curioso, a própria função <code>zipWith</code> pode ser definida como:</p>

<pre><code>zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
zipWith _ _ _ = []
</code></pre>

<p>Em outras palavras, <code>zipWith</code> de uma lista cujo primeiro elemento é <code>x</code> e cujo restante é <code>xs</code> e de uma lista cujo primeiro elemento é <code>y</code> e cujo restante é <code>ys</code> para uma função <code>f</code> é <code>f</code> de <code>x</code> e <code>y</code> concatenado a <code>zipWith</code> de <code>xs</code> e <code>ys</code> sobre <code>f</code>. A segunda linha diz que em qualquer outro caso, o retorno é uma lista vazia. A definição recursiva pode ser vista facilmente na chamada em <code>fibs</code>.</p>

<p>Note que a lista sempre é expressa como uma lista sem término, ou seja, infinita. Para retornar um valor específico, basta pegar o elemento em questão. O cálculo necessário é feito somente até aquele ponto e não mais. O resto da lista continua sendo expresso internamente por uma representação que permite obter o próximo valor sem mais do que uma simples continuação do processamento. A função é bem eficiente e não sofre das penalidades recursivas da versão original. </p>

<p><strong>A beleza da função acima, entretanto, não está em evitar recursão explícita (esse é outro conceito que exploraremos futuramente) ou usar uma lista infinita</strong>. Antes, está no fato de que o cálculo, além de somente ser executado quanto necessário, não é repetido. Ou seja, seu eu pegar o décimo primeiro elemento e depois pegar o quinto, a lista não é recalculada. E quando eu precisar do vigésimo elemento, só serão necessárias mais nove passagens na lista. O cálculo todo acontece sob demanda.</p>

<p>Um outro exemplo interessante disso é o cálculo do <a href="http://en.wikipedia.org/wiki/Memoization">Crivo de Eratóstenes</a> em Haskell:</p>

<pre><code>primes = sieve [2..]
  where
    sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p > 0]</code></pre>

<p>Essencialmente, a função tira todos múltiplos de dois de uma lista infinita de números naturais maiores ou iguais a dois, depois tira todos múltiplos de três, depois todos de cinco, e assim por diante, com cada novo primo descoberto.</p>

<p>Ao contrário da função <code>fib</code>, essa é extremamente ineficiente, o que mostra que o uso de <span class= "foreign-word" lang="en">lazy evaluation</span> não é uma panacéia e que também depende de bons algoritmos.</p>

<h3>O que isso tudo significa</h3>

<p>Em linguagens com suporte direto a <span class= "foreign-word" lang="en">lazy evaluation</span>, o uso da mesma pode facilitar a implementação e aumentar muito a performance de um programa. </p>

<p>Em linguagens que não suportam <span class= "foreign-word" lang="en">lazy evaluation</span>, uma simulação da técnica pode ser bem útil para gerar resultados com maior performance. É um troca, na verdade, já que geralmente isso envolve um consumo maior de memória, mas pode ser bem útil se o cálculo é tão intensivo que exige uma forma melhor de ser tratado. </p>

<p>Uma forma de fazer algo parecido em Ruby é usando a classe <code>Fiber</code>, disponível a partir da versão 1.9. Você teria algo assim:</p>

<pre><code>require "fiber"
  
fib = Fiber.new do 
  x, y = 0, 1
  loop do 
    Fiber.yield y
    x, y = y, x + y
  end
end</code></pre>

<p>Essa computação é infinita, já que não há uma condição de saída, e você pode obter valores chamando o método <code>resume</code> na classe retornada:</p>

<pre><code>20.times { puts fib.resume }</code></pre>

<p>O problema com a chamada acima é que se eu quisesse recalcular uma número de Fibonacci qualquer, eu teria que criar uma nova <code>Fiber</code>. Isso pode ser resolvido com algo um pouco mais complexo:</p>

<pre><code>require "fiber"

class LazyList
  
  def initialize(&#038;block)
    @memo = []
    @fiber = Fiber.new(&#038;block)
  end
  
  def [](value)
    @memo << @fiber.resume while @memo.size <= value
    @memo[value]
  end
  
end

def fib(n)
  (class << self; self; end).instance_eval do
    ((@global_lazy_dict ||= {})[:fib] ||= LazyList.new {
      x, y = 0, 1
      loop do 
        Fiber.yield y
        x, y = y, x + y
      end      
    })[n]
  end
end

[12, 10, 17].each { |i| puts fib(i) }</code></pre>

<p>O código acima usa uma forma bem básica de <a href="http://en.wikipedia.org/wiki/Memoization">memoização</a> para manter a lista em memória mas a idéia é essa. Se você rodar o código, verá que a lista somente é calculada até onde necessário e que valores anteriores são retornados do <span class= "foreign-word" lang="en">cache</span>. </p>

<p>Uma aplicação interessante desse tipo de técnica seria executar cálculos pesados em uma <span class= "foreign-word" lang="en">thread</span> separada e retornar somente o necessário para a exibição. Se está fosse feita em páginas, por exemplo, você poderia requisitar somente os valores daquela página enquanto os das demais páginas estão sendo calculados. Para quem, inclusive, quer experimentar com isso em Ruby, há justamente uma biblioteca chamada <a href="http://moonbase.rydia.net/software/lazy.rb/">lazy.rb</a> para auxiliar. Veja o método <code>future</code>.</p>

<h3>Conclusão</h3>

<p>Espero que isso tenha ajudado a entender um pouco o que é <span class= "foreign-word" lang="en">lazy evaluation</span>. Qualquer dúvida posterior, coloque nos comentários aqui. Da mesma forma, sugestões e críticas são bem-vindas. Na próxima sexta-feira, continuaremos com mais um artigo.</p>
]]></content:encoded>
			<wfw:commentRss>http://logbr.reflectivesurface.com/2008/01/25/conceitos-de-programacao-lazy-evaluation/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
