Haskell/Variáveis e funções: diferenças entre revisões

[revisão pendente][revisão pendente]
Conteúdo apagado Conteúdo adicionado
→‎Variáveis em linguagems imperativas: Erros de português - Correção
m <source> -> <syntaxhighlight> (phab:T237267)
 
Linha 34:
Para ajudar na organização, crie também um novo diretório (uma pasta de arquivos) em seu computador para salvar os arquivos Haskell que você cria quando estiver resolvendo os exercícios deste livro. Vamos lá, crie um diretório chamado <tt>HaskellWikibook</tt>, por exemplo, e dentro dele, um arquivo chamado <tt>Varfun.hs</tt> contendo o seguinte código:
 
<sourcesyntaxhighlight lang = "haskell">
r = 5.0
</syntaxhighlight>
</source>
 
Este programa define a variável <code>r</code> como sendo o valor <code>5.0</code>.
Linha 67:
Agora, vamos fazer esse valor ser mais fácil de ser acessado ao definirmos um nome para ele também. Abra o arquivo novamente e mude seu conteúdo para:
 
<sourcesyntaxhighlight lang = "haskell">
r = 5.0
area = pi * r ^ 2
</syntaxhighlight>
</source>
 
Salve-e e recarregue-o no GHCi usando <code>:reload</code>, ou simplesmente <code>:r</code>:
Linha 95:
Além do código em si, arquivos de código fonte podem conter texto conhecidos como ''cometários'', que são ignorados pelo compilador. Em Haskell existem dois tipos de comentários. Primeiro, aqueles que começam com <code>--</code> e que vão até o final da linha:
 
<sourcesyntaxhighlight lang = "haskell">
x = 5 -- x is 5.
y = 6 -- y is 6.
-- z = 7 -- z is not defined.
</syntaxhighlight>
</source>
 
Nestes casos, <code>x</code> e <code>y</code> são realmente definidos no código, mas <code>z</code> não é.
 
O segundo tipo do comentário é deliminado por <code>{- ... -}</code> e podem se abranger mais de uma linha:
<sourcesyntaxhighlight lang = "haskell">
answer = 2 * {-
bloco de cometário de mais de uma linha e...
-} 3 {- comentário no meio da linha -} * 7
</syntaxhighlight>
</source>
 
Comentários são usados para explicar partes de um programa ou para fazer qualquer tipo de anotação acerca de algum contexto. É importante frisar que exagerar na quantidade de comentários pode, na verdade, dificultar a leitura do código em si, e comentários desatualizados podem também gerar confusões.
Linha 120:
Vejamos um exemplo. O código a seguir não funcionaria em Haskell:
 
<sourcesyntaxhighlight lang = "haskell">
r = 5
r = 2
</syntaxhighlight>
</source>
 
Um programador de linguagens imperativas leria isto como sendo: primeiro, definir <code>r = 5</code> e depois redefinir para <code>r = 2</code>. Em Haskell, entretanto, o compilador responderia este código com um erro: "múltiplas declarações de <code>r</code>". Dentro de um escopo definido, uma variável de Haskell é definida uma única vez, e sua definição não pode mudar.
Linha 131:
De forma mais precisa, as variáveis de Haskell são ''imutáveis'': elas variam apenas de acordo com os datos que entramos no programa. Não podemos definir <code>r</code> de dois jeitos diferentes num mesmo código, mas podemos mudar este valor se mudarmos de arquivo. Vamos mudar o código acima para:
 
<sourcesyntaxhighlight lang = "haskell">
r = 2.0
area = pi * r ^ 2
</sourcesyntaxhighlight>
 
Claro que vai funcionar. Mudamos <code>r</code> no único lugar em que ele está definido, e isso muda seu valor em todo o resto do programa.
Linha 142:
Aqui temos mais um exemplo da grande diferença entre Haskell com as linguagens imperativas:
 
<sourcesyntaxhighlight lang = "haskell">
r = r + 1
</syntaxhighlight>
</source>
 
Em vez de "incrementar o valor de <code>r</code>", isto é, atualizar o valor já armazenado na memória, este código em Haskell é a definição recursiva e <code>r</code>, ou seja, ele foi definido em termos de si mesmo. Não se preocupe, pois explicaremos [[Haskell/Recurssão|recurssão]] mais tarde. Neste caso específico, se <code>r</code> tiver sido definido com algum valor anterior, o compilador retornaria uma mensagem de erro, como explicamos anteriormente. Se <code>r</code> tiver sido definido com um valor de 5, fazer <code>r = r + 1</code> é o mesmo que tentar dizer que <math>5 = 5 + 1</math> na Matemática, o que está obviamente errado.
Linha 152:
{|border="0"
|-
||<sourcesyntaxhighlight lang = "haskell">
y = x * 2
x = 3
</syntaxhighlight>
</source>
||<sourcesyntaxhighlight lang = "haskell">
x = 3
y = x * 2
</syntaxhighlight>
</source>
|}
 
Linha 167:
Mudar o programa todas as vezes em que quisermos calcular a área de um círculo diferente é tedioso e nos limita a um círculo por vez. Poderíamos calcular duas áreas duplicando todas as contas em nosso código, definindo <code>r2</code> e <code>area2</code><ref group=nota>Podemos ver aqui que os nomes de variáveis podem contar números e letras. Entretanto, toda variável ''deve'' começar com um letra minúscula, que pode ser seguida por outras letras (também maiúsculas), números, traço (_) ou apóstrofe (').</ref>
 
<sourcesyntaxhighlight lang = "haskell">
r = 5
area = pi * r ^ 2
r2 = 3
area2 = pi * r2 ^ 2
</syntaxhighlight>
</source>
 
Para evitarmos essa repetição incessante, é preferível simplesmente adicionar uma ''função'' para calcular a área e aplicá-la a diferentes raios.
Linha 178:
Uma ''função'' recebe um ''argumento'' (ou ''parâmetro'') e retorna um resultado, exatamente como na Matemática. Em Haskell, as funções são definidas da mesma forma que as variáveis, exceto que temos que dizer quais são seus argumentos do lado esquerdo da expressão. Por exemplo, o código a seguir define <code>area</code>, cujo argumento é <code>r</code>:
 
<sourcesyntaxhighlight lang = "haskell">
area r = pi * r ^ 2
</syntaxhighlight>
</source>
 
Observe a sintáxe: o nome da função vem primeiro (<code>area</code>), seguido de um espaço em branco e de seu argumento (<code>r</code>). Depois vem o sinal <code>=</code>, e a definição da função, que usa o argumento de entrada em seu cálculo.
Linha 201:
Mesmo assim, parênteses ainda são usados para agrupar ''expressões'' (qualquer código que retorne algum valor) que devem ser calculadas todas juntas. Veja como as seguintes expressões são interpretadas de formas diferentes:
 
<sourcesyntaxhighlight lang = "haskell">
5 * 3 + 2 -- 15 + 2 = 17 (multiplicação feita antes da adição)
5 * (3 + 2) -- 5 * 5 = 25 (parenteses forçam a adição a ser computada primeiro)
area 5 * 3 -- (area 5) * 3
area (5 * 3) -- area 15
</syntaxhighlight>
</source>
 
Da mesma forma que a multiplicação acontece antes que a adição, perceba que as funções de Haskell tem hierarquia de ''precedência'' maior que qualquer outro operador, seja <code>+</code> ou <code>*</code>, por exemplo.
Linha 213:
O que exatamente acontece quando entramos uma expressão no GHCi? Depois que pressionamos "enter", ela é avaliada. Isso significa que cada função vai ser substituída por sua definição e as contas serão realizadas até que um único valor final reste. Por exemplo, ao executarmos <code>area 5</code> o seguinte acontece:
 
<sourcesyntaxhighlight lang=haskell>
area 5 -- todos 'r' do lado direito serão substituidos por '5'
pi * 5 ^ 2 -- 'pi' será substituido por um valor aproximado
Linha 219:
3.141592653589793 * 25 -- a operação de multiplicação será realizada
78.53981633974483 -- resultado final
</syntaxhighlight>
</source>
 
Quando usamos o GHCi, o resultado de ''aplicar'' ou ''chamar'' a função, que segue o procedimento acima, vai aparecer na tela.
Linha 225:
Agora, mais algumas funções:
 
<sourcesyntaxhighlight lang = "haskell">
dobrar x = 2 * x
quadruplicar x = double (double x)
quadrado x = x * x
metade x = x / 2
</syntaxhighlight>
</source>
 
{{HaskellExercícios|1=
Linha 239:
Funções também podem ter mais que um argumento. Por exemplo, para calcular a área de um retângulo precisamos de seu comprimento e de sua largura:
 
<sourcesyntaxhighlight lang = "haskell">
areaRetang a b = a * b
</syntaxhighlight>
</source>
 
{{HaskellGHCi|1=
Linha 250:
Outro exemplo é a área de um triângulo, <math>A = \frac{bh}{2}</math>:
 
<sourcesyntaxhighlight lang="haskell">
areaTriang b h = (b * h) / 2
</syntaxhighlight>
</source>
 
{{HaskellGHCi|1=
Linha 261:
Como você pode ver, os argumentos são separados por espaços. É por isso que as vezes você precisa usar parênteses para agrupar expressões. Por exemplo, não se pode escrever o quádruplo de um valor como sendo
 
<sourcesyntaxhighlight lang = "haskell">
quadruple x = double double x -- error
</syntaxhighlight>
</source>
 
Isso aplicaria a função <code>double</code> sobre dois argumentos: <code>double</code> e <code>x</code>. É possível, sim, que ''funções sejam argumentos de outras funções'', como veremos mais tarde, mas isso não é o que queremos aqui. Para fazer esta função funcionar precisamo suar parênteses:
 
<sourcesyntaxhighlight lang = "haskell">
quadruple x = double (double x)
</syntaxhighlight>
</source>
 
Os argumento são sempre passados na ordem em que aparecem. Por exemplo:
 
<sourcesyntaxhighlight lang = "haskell">
menos x y = x - y
</syntaxhighlight>
</source>
 
{{HaskellGHCi|1=
Linha 295:
Claro que você pode usar funções outras funções para definir suas próprias, bem como você já o fez usando adição, <code>+</code>, ou multiplicação, <code>*</code>. Na verdade, operadores também são definidos como funções em Haskell. Por exemplo, para calcular a área de um quadrado, podemos reutlizar a função que calcula a área de um retângulo:
 
<sourcesyntaxhighlight lang = "haskell">
areaRetang a b = a b
areaQuad l = areaRetang l l
</syntaxhighlight>
</source>
 
{{HaskellGHCi|1=
Linha 314:
Quando definimos uma função, às vezes precisamos calcular valores intermediários, mas que são ''locais'' e usados apenas dentro daquela função. Considere a [[w:teorema de Herão|fórmula de Herão]]: <math>A = \sqrt{p(p-a)(p-b)(p-c)}</math>, sendo <math>p</math> o metade do perímetro do triângulo. Este é o cálculo da área de um triângulo de lados <code>a</code>, <code>b</code> e <code>c</code>:
 
<sourcesyntaxhighlight lang = "haskell">
herao a b c = sqrt (s * (s - a) * (s - b) * (s - c))
where
s = (a + b + c) / 2
</syntaxhighlight>
</source>
 
A variável <code>s</code> representa a metade do perímetro. Seria tedioso e propenso a erros se tivéssemos que escrever <code>(a + b + c)/2</code> toda vez que precisássemos deste valor dentro de <code>herao</code>. <code>sqrt</code> calcula a raiz quadrada do seu argumento (lembre-se de ''square root'', no inglês).
Linha 324:
Simplesmente escrever as definições separadamente não funcionaria. Veja:
 
<sourcesyntaxhighlight lang = "haskell">
herao a b c = sqrt (p * (p - a) * (p - b) * (p - c))
p = (a + b + c) / 2 -- a, b, and c are not defined here
</syntaxhighlight>
</source>
 
O compilador reclamaria que <code>a</code>, <code>b</code> e <code>c</code> só estão disponíveis no lado direito de <code>herao</code>, enquanto que a definição de <code>s</code> não faz parte do lado direito de <code>herao</code>. Para que ela seja embutida lá, usamos a palavra-chave <code>where</code>.
Linha 333:
Perceba que tanto <code>where</code> quanto a definição de <code>s</code> estão ''indentadas'' por 4 espaços em branco. Isso é importante para delimitar o que está dentro do que. Vejamos outros exemplos do uso de <code>where</code>:
 
<sourcesyntaxhighlight lang = "haskell">
areaTriangTrig a b c = c * altura / 2 -- usando trigonometria
where
Linha 343:
resulto = sqrt (p * (p - a) * (p - b) * (p - c))
p = (a + b + c) / 2
</syntaxhighlight>
</source>
 
== Escopo ==