evitar_entrada_saida_em_funcao_2018_04_07.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <!--
  2. Por que evitar entrada/saida de dados em funções?
  3. http://saw.atp.usp.br/mod/page/view.php?id=
  4. -->
  5. <!DOCTYPE html>
  6. <html itemscope itemtype="http://schema.org/QAPage">
  7. <head>
  8. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  9. <title>Por que evitar entrada/saida de dados em funções?</title>
  10. <style type="text/css">
  11. .tbCodeLinCol { border-collapse:collapse; }
  12. .tbCodeLinCol th { background-color:#8aaada; }
  13. .tbCodeLinCol td, .tbCodeLinCol th { padding:5px;border:1px solid #000; font: 10px/12px Arial, Helvetica, sans-serif; }
  14. .tbCode td, .tb th { border: 0; font: 10px/12px Arial, Helvetica, sans-serif; }
  15. .tableCd { border-collapse:collapse; }
  16. .thBg th { background-color:#8aaada; }
  17. /* .trCd td, .tbCd td, .tbCd th { vertical-align: top; padding:5px; border-right:1px solid #000; font-family: courier } */
  18. .trCd td, .tbCd td, .tbCd th { vertical-align: top; padding:5px; border-right:1px solid #000; border-bottom:1px solid #000; font-family: courier }
  19. .cmd { color: #0000AA; }
  20. .fnc { color: #00AAAA; }
  21. .head { color: #000000; }
  22. .var { color: #009A00; }
  23. .tipo { color: #005A5A; }
  24. .com { color: #AA0000; }
  25. .def { color: #008181; font-weight: bold; } /* definicao */
  26. .versao { font-size: .6em; }
  27. </style>
  28. </head>
  29. <body>
  30. <span style="color: #0055AA;font-size:1.2em"><b>Por que evitar entrada/saida de dados em funções</b></span>
  31. <p>
  32. Para explicar as razões pelas quais deve-se <b>evitar comandos de entrada e de saída dentro da definição de uma função</b>,
  33. devemos primeiro enfatizar o que é a uma função em linguagens como <i>C</i> ou <i>Python</i>:
  34. <ul>
  35. <li>é um código que recebe seus <em>dados de entrada</em> a partir de uma <i>lista de parâmetros</i> (<i class="def">parâmetros formais</i>);</li>
  36. <li>é um código que processa os dados de entrada e geralmente devolve algum valor como resposta (em seu comando <tt class="cmd">return</tt>);</li>
  37. <li>é um código que pode ser invocado a partir de outros trechos do programa, que providenciam os valores para os parâmetros
  38. (<i class="def">parâmetros efetivos</i>).</li>
  39. </ul>
  40. </p>
  41. <p>
  42. Desse modo, usando como exemplo a <i>função fatorial</i> (<i>fat(0)=1</i> e <i>fat(n)=n*fat(n-1), n&gt;0</i>), a razão para
  43. se evitar comandos de entrada e de saída dentro da definição da função pode ser resumida em:
  44. <ul>
  45. <li><i>Facilita o reúso de código</i>, ou seja, sempre que precisar de um algoritmo que compute o fatorial,
  46. basta copiar o código que já implementou (e testou);</li>
  47. <li><i>Facilita o desenvolvimento de programas maiores</i>, pois pode-se agrupar os blocos lógicos que tenham um propósito bem definido como
  48. uma função.</li>
  49. </ul>
  50. </p>
  51. <p>
  52. A fim de ficar mais clara a vantagem de <i>passar os valores via parâmetro</i>, pense na implementação <i>errônea</i> da função
  53. <tt class="var">fat</tt>, suponha que em seu código tenha sido usado um comando para <i>leitura</i> (via teclado) dos dados.
  54. Então, se precisar computar a combinação de <tt>n</tt>, tomados <tt>k</tt> a <tt>k</tt> (<i>C<sub>n,k</sub>=n!/((n-k)!k!)</i>),
  55. esse valor <b>não</b> poderá ser computado usando a expressão seguinte (com chamada à função <tt class="var">fat</tt>):
  56. <tt class="var">fat(n)/(fat(n-k)fat(k))</tt>.
  57. <br/>
  58. Por que mesmo? Porque, os valores a partir dos quais desejamos saber a combinação já são <b>dados</b>, <b>não</b> faz sentido obrigar
  59. o usuário a digitar os valores de <tt>n</tt>, de <tt>n-k</tt> e de <tt>k</tt> (além disso ele pode digitar errado!).
  60. </p>
  61. <p>
  62. Assim, nos exercícios que apresentam o conceito de função (portanto envolvendo um bloco lógico com propósito bem definido),
  63. exigiremos que o aprendiz <b>não</b> use comandos de entrada ou de saída de dados <b>dentro da função</b>.
  64. Com isso esperamos que ele adquira <i>bons hábitos de programação</i>.
  65. </p>
  66. <p>
  67. <!-- F<sub>0</sub>=1, F<sub>1</sub>=1, F<sub>k</sub> = F<sub>k-1</sub> + F<sub>k-2</sub>, k&gt;2 -->
  68. Entretanto, existem as exceções, exemplos nos quais é útil usar comando de entrada ou de saída dentro da função. Ilustraremos
  69. o caso de utilidade de um comando de saída dentro da função, isso ocorre se desejamos computar (e imprimir) os <tt>n</tt> primeiros termos da
  70. <i title="F_{0}=1, F_{1}=1, F_{k} = F_{k-1} + F_{k-2}, k&gt;1">sequência de Fibonacci</i>.
  71. Então, naturalmente precisaremos do comando de impressão/saída e como isso pode ser feito para qualquer
  72. <tt>n</tt> é adequado agrupar esse código na forma de um procedimento separado (no caso uma função <b>sem</b> valor de retorno).
  73. <br/>
  74. Porém se o objetivo fosse conhecer apenas o termo <tt>n</tt> da sequência, a função <b>não deveria ter o comando de saída</b>.
  75. </p>
  76. <p>
  77. Uma última observação sobre os exercícios envolvendo funções. <b>O programa principal sempre deve ter ao menos um comando para entrada
  78. e ao menos um para saída de dados</b>. Sem eles não seria possível testar se a função do exercício foi implementada corretamente ou não.
  79. </p>
  80. <span style="color: #0055AA;font-size:1.1em">Exemplo 1. Dados naturais <tt>n</tt> e <tt>k</tt>, computar a combinação de <tt>n</tt> <tt>k-a-k</tt></span>
  81. <p>
  82. Suponha que precisemos fazer um program para computar
  83. quantas são as combinações de <tt>n</tt> termos combinados <tt>k</tt>-a-<tt>k</tt>, ou seja, tendo <tt>n</tt> elementos,
  84. desejamos saber de quantos modos distintos podemos formar grupos com <tt>k</tt> dos elementos.
  85. </p>
  86. <p>
  87. Esse conceito aparece em <i>probabilidade</i> e em <i>combinatória</i>, sabemos que esta combinação corresponde ao agrupamentos de
  88. elementos nos quais a ordem não importa (tanto faz a ordem dos <tt>k</tt> elementos do tipo <tt>a</tt>) e que a fórmula que fornece
  89. o total desses subconjuntos é a seguinte:
  90. <center><a href="https://en.wikipedia.org/wiki/Binomial_theorem" title="explicacao mais detalhada na WikiPedia">C(n,k) = n! / ((n-k)! k!)</a>.</center>
  91. </p>
  92. <p>
  93. Por exemplo, combinar <tt>6</tt> valores distintos, tomados <tt>2</tt> a <tt>2</tt> resulta <tt>15=6!/((6-2)!2!)=6!/(4!2!)</tt>, que
  94. são os <tt>15</tt> subconjuntos:
  95. <tt>(6,5)</tt>, <tt>(6,4)</tt>, <tt>(6,3)</tt>, <tt>(6,2)</tt>, <tt>(6,1)</tt>,
  96. <tt>(5,4)</tt>, <tt>(5,3)</tt>, <tt>(5,2)</tt>, <tt>(5,1)</tt>,
  97. <tt>(4,3)</tt>, <tt>(4,2)</tt>, <tt>(4,1)</tt>,
  98. <tt>(3,2)</tt>, <tt>(3,1)</tt>,
  99. <tt>(2,1)</tt>.
  100. </p>
  101. <p>
  102. Assim, existem duas possibilidades de implementar esse código, uma implementando a função fatorial (<tt>fat(n)</tt>) que seria
  103. invocada três vezes pelo programa principal (com algo como <tt>imprima(fat(n) / (fat(k) * fat(n-k))</tt>.
  104. Outra possibilidade é implementar também o cômputo de <tt>fat(n) / (fat(k) * fat(n-k))</tt> também como uma função.
  105. Apesar dessa segunda possibilidade não ajudar tanto na organização do código (pois a segunda função (<tt>comb(n,k)</tt>) seria muito
  106. simples, ilustraremos dessa forma por ela usar função chamando função que chama função (a principal chama <tt>comb(n,k)</tt> e essa
  107. chama <tt>fat(n)</tt>).
  108. Veja como ficaria o código em uma <i>pseudo-linguagem</i> de programação:
  109. <pre>int fat(n) { //# estou supondo que o valor digitado pelo usuario seja um natural maior ou igual a 0 (n>=0)
  110. valfat = 1;
  111. cont = 2;
  112. enquanto (cont&lt;=n) { //# continue computando enquanto nao chegar ao valor cont==n
  113. valfat = valfat * cont;
  114. cont = cont + 1;
  115. }
  116. devolva valfat;
  117. }
  118. int comb(n, k) {
  119. devolva fat(n) / (fat(n-k)*fat(k));
  120. }
  121. principal() {
  122. declara_inteiros N, k; //# note que os nomes de variaveis daqui tem relacao NULA com os usados em 'fat'
  123. declara_inteiros a, b, c; //# para uma segunda versao
  124. leia(N, k); //# leia 2 valores naturais e armazene-os respectivamente em N e em k (espera-se que digite N>=k)
  125. //# pode-se invocar seguidamente fat 3 vezes como abaixo:
  126. imprima(comb(N,k);
  127. //# pode-se invocar a funcao fat 3 vezes e armazenar cada resultado e depois "imprimir"
  128. a = fat(N);
  129. b = fat(k);
  130. c = fat(N-k);
  131. imprima(a/( b * c))); //# este exemplo e' ilustrado na figura abaixo
  132. }</pre>
  133. </p>
  134. <center><img src="img/exemplo_exec_funcao.jpg" title="exemplo de execucao da funcao"/>
  135. </center>
  136. <p>
  137. Novamente para enfatizar as desvantagens de usar leitura ou impressão dentro de funções,
  138. procure implementar o código acima, em sua linguagem favorita, e colocar nele comandos para
  139. leitura ou saída dentro da função <tt>fat(n)</tt>, como discutido abaixo.
  140. </p>
  141. <span style="color: #0055AA;font-size:1.1em">Observação 1. Vantagem de uso de função sem comandos de entrada/saída - combinação n, k-a-k</span>
  142. <p>
  143. Note que se dentro da função <tt>fat</tt> houvesse um comando do tipo <tt>leia(n);</tt>
  144. a função NÃO poderia ser usada, pois ao ser invocada as 3 vezes, em cada uma delas exigiria que o
  145. usuário digitasse novamente o valor para <tt>N</tt> inicialmente (estragando o valor de <tt>N</tt> passado para a função),
  146. depois exigiria a digitação novamente do <tt>k</tt> e por último haveria o absurdo de exigir que o usuário
  147. digitasse o valor para <tt>N-k</tt>.
  148. </p>
  149. <p>
  150. De forma análoga, se a função <tt>fat</tt> tivesse algum comando de saída, ao usar a função 3 vezes, o usuário
  151. receberia informações confusas: ele deseja a combinação de <tt>n</tt> combinados <tt>k</tt>-a-<tt>k</tt>, mas
  152. receberia 4 infomações (<tt>n!</tt>, <tt>k!</tt>, <tt>(n-k)!</tt> e <tt>n!/(k! (n-k)!))))</tt>).
  153. </p>
  154. <span style="color: #0055AA;font-size:1.1em">Observação 2. Propriedade interessante de C(n,k) - Triângulo de Pascal</span>
  155. <p>
  156. Se rodarmos este programa seguidamente, na ordem das colunas (primeiro a única entrada de <tt>Entradas 0</tt>, depois a primeira
  157. linha de <tt>Entradas 1</tt>, depois sua segunda linha e assim por diante até a última linha de <tt>Entradas 4</tt>) e
  158. agruparmos as saídas de cada coluna em uma única linha, obteremos a tabela na última coluna abaixo (<tt>Saídas (cada linha um dos blocos de entradas)</tt>).
  159. Destacamos três dos valores para mostrar a relação do
  160. <a href="https://en.wikipedia.org/wiki/Pascal%27s_triangle" title="seguir para WikiPedia">Triângulo de Pascal</a>
  161. <table class="tableCd">
  162. <tr class="trCd" valign="top">
  163. <td>Linhas &nbsp;&nbsp;</td><td>Entradas 0&nbsp;&nbsp;</td><td>Entradas 1&nbsp;&nbsp;</td><td>Entradas 2&nbsp;&nbsp;</td>
  164. <td>Entradas 3&nbsp;&nbsp;</td><td>Entradas 4&nbsp;&nbsp;</td>
  165. <td>Saídas (cada linha um dos blocos de entradas)</td></tr>
  166. <tr class="trCd" valign="top"><td><pre> 0
  167. 1
  168. 2
  169. 3
  170. 4</pre></td><td><pre> 1 0</pre></td> <td><pre> 1 1
  171. 1 0</pre></td><td><pre> 2 0
  172. 2 1
  173. 2 2</pre></td><td><pre> 3 0
  174. 3 1
  175. 3 2
  176. 3 3</pre></td><td><pre> 4 0
  177. 4 1
  178. 4 2
  179. 4 3
  180. 4 4</pre></td><td><pre> 1
  181. 1 1
  182. 1 2 1
  183. [1] [3] 3 1
  184. 1 [4] 6 4 1</pre></td></tr></table></p>
  185. <p>
  186. Examinando as saídas (ũltima coluna) notamos uma propriedade interessante:
  187. o elemento da linha <tt>n+1</tt>, coluna <tt>k+1</tt> = o elemento da linha <tt>n</tt>, coluna <tt>k</tt> + o elemento da linha <tt>n</tt>, coluna </tt>k+1</tt>.
  188. Veja por exemplo, a última linha, linha <tt>4</tt>: o segundo elemento = <tt>4</tt> = <tt>1 + 3</tt> (da linha acima).
  189. </p>
  190. <!--
  191. <pre>1 0
  192. ---
  193. 1 1
  194. 1 0
  195. ---
  196. 2 0
  197. 2 1
  198. 2 2
  199. ---
  200. 3 0
  201. 3 1
  202. 3 2
  203. 3 3
  204. ---
  205. 4 0
  206. 4 1
  207. 4 2
  208. 4 3
  209. 4 4</pre>
  210. -->
  211. <!-- https://pt.wikipedia.org/wiki/Produtos_not%C3%A1veis -->
  212. <!--
  213. <span style="color: #0055AA;font-size:1.1em">Exemplo 2. Coeficientes da expansão do produto notável <tt>(a+b)^n</tt></span>
  214. <p>
  215. Suponhamos que precisemos fazer um program que computa os termos dos coeficientes da expansão de <tt>(a+b)^n</tt>.
  216. Ou seja, desejamos um programa que dado <tt>n</tt> liste quantos termos do tipo <tt>a^i x b^j</tt> existe, para cada
  217. <tt>i</tt> e cada <tt>j</tt> tais que <tt>i+j=n</tt>.
  218. o valor da combinação de <i>N</i> tomado <i>k</i> a <i>k</i>, ou seja,
  219. C(N,k) = N! / ( k! (N-k)!).
  220. Para isso percebe-se que é necessário implementar o cômputo de fatorial que será utilizado 3 vezes.
  221. Para facilitar a compreensão, podemos escrever um código com 3 variáveis auxiliares para armazenar, respectivamente,
  222. <i>N!</i>, <i>k!</i> e <i>(N-k)!</i>.
  223. </p>
  224. <pre>(a+b)^0 = 1 a^0 b^0
  225. (a+b)^1 = (a+b) (0 a^0 b^0 + 0 a^0 b^0) = 1 a^1 b^0 + 1 a^0 b^0
  226. (a+b)^2 = (a+b) (1 a^1 b^0 + 0 a^0 b^1) = 1 a^2 b^0 + 2 a^1 b^1 + 1 a^0 b^2
  227. (a+b)^3 = (a+b) (1 a^2 b^0 + 2 a^1 b^1 + 1 a^0 b^2) = 1 a^3 b^0 + 3 a^2 b^1 + 3 a^1 b^2 + 1 a^0 b^3
  228. (a+b)^4 = (a+b) (1 a^3 b^0 + 3 a^2 b^1 + 3 a^1 b^2 + 1 a^0 b^3) = 1 a^4 b^0 + 4 a^3 b^1 + 6 a^2 b^2 + 4 a^1 b^3 + 1 a^0 b^3a
  229. </pre>
  230. <pre>1
  231. 1 1
  232. 1 2 1
  233. 1 3 3 1
  234. 1 4 6 4 1
  235. </pre>
  236. -->
  237. <p>
  238. Leônidas de Oliveira Brandão<br/>
  239. http://line.ime.usp.br
  240. <!--
  241. 2017/04/06: primeira versao
  242. 2019/04/07: versao 2 (acrescentado novos paragrafos, melhorando explicacoes e frases)
  243. -->
  244. </p>
  245. </body>