• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Manifest0

Currying

9 mensagens neste tópico

Viva!

Comecei anteontem a tentar aprender haskell, e já tou com problemas no capitulo 1.4 (vai ser um livro longo!).

Será que alguém me poderia ajudar a perceber melhor o currying?

P.ex.:

plus :: (Integer, Integer) -> Integer
plus(x,y) = x+y

Neste exemplo eu «leio» a linha: "plus :: (Integer, Integer) -> Integer", da seguinte maneira: "A função plus vai receber 2 integers e vai devolver um integer."

No entanto eu posso definir a função plus da seguinte maneira:

plus :: Integer -> (Integer -> Integer)
plus(x,y) = x + y

Apesar da função fazer exactamente a mesma coisa, como é que isto se «lê»? A função plus vai receber um inteiro e vai devolver uma função que devolve um inteiro? Não faz sentido, pois a função plus não vai receber um inteiro, vai receber 2! Para além de não ir devolver nenhuma função vai devolver um inteiro!

Com os melhores cumprimentos,

Manifest0

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Deves estar a ler o livro do JBB.

Os parêntesis nos tipos na 2ª versão podem ser omitidos. Quando o fizeres vais perceber que, na versão 1, para obteres um resultado precisas de passar um par de inteiros, enquanto que na versão 2, passas os inteiros um de cada vez. Ou seja, na versão 1, só quando tiveres plus (x,y) é que sabes que vai dar um Integer (plus (x,y) :: Integer), enquanto que na 2ª versão, quando já tens apenas plus x, já sabes que vai ser do tipo Integer -> Integer (plus x :: Integer -> Integer).

Para usares a 1ª versão tens de fornecer simultaneamente dois números.

Para usares a 2ª versão podes fornecê-los um a um (os parêntesis estão lá apenas para salientar isso).

Só se pode falar no resultado da versão 1 depois de lhe forneceres os seus 2 argumentos, por ex: plus (3,5). No entanto a 2ª versão dá um resultado logo que lhe seja fornecido o primeiro número.

Se, para a 2ª versão, tiveres: plus 3, sabes que

plus :: Integer -> (Integer,Integer)

3 :: Integer

logo  plus 3 :: Integer -> Integer

Isto é, plus 3 é uma função que tem um argumento do tipo Integer e dá um resultado do tipo Integer. Pode-se concluir que plus1 (3,5) = (plus2 3) 5) admitindo que plus2 3 é a função que dá como resultado o valor do seu argumento somado a 3.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Bem, Currying é o que se chama ao transformar uma função que recebe múltiplos argumentos para uma função que recebe um argumento.

É bastante simples, se perceberes o conceito das funções de Haskell. Todas mas mesmo todas as funções de Haskell só recebem 1 e só 1 argumento. Se tiveres uma função com a assinatura fun :: a -> b -> c na realidade é fun :: a -> (b -> c), ou seja, uma função que recebe um elemento do tipo a e devolve uma função que recebe um elemento do tipo b que devolve um elemento do tipo c. E isto para todos os casos. fun :: a -> b -> c -> d -> e é o mesmo que tiveres fun :: a -> (b -> (c -> (d -> e))). Como são casos similares não é obrigatório por os parêntesis tornado a leitura da função mais simples como também a escrita da mesma.

Mas vamos ver os casos práticos, usando a tua função plus:

Prelude> :t plus
plus :: (Integer, Integer) -> Integer

Se uma pessoa passar aplicar o currying

Prelude> :t curry plus
curry plus ::  Integer -> Integer -> Integer

Que é o mesmo que tivermos Integer -> (Integer -> Integer)

Agora vamos ver a diferença de passar os argumentos. A primeira versão obriga a passar os argumentos todos juntos ao mesmo tempo, como um único argumento através de um tuplo.

Prelude> plus (4,3)
7

Se tentar-mos passar apenas 1 dos argumentos (não tem lógica porque a função só recebe 1 argumetno que é o tuplo).

Prelude> plus 4

<interactive>:1:5:
    No instance for (Num (Integer, Integer))
      arising from the literal `4' at <interactive>:1:5
    Possible fix:
      add an instance declaration for (Num (Integer, Integer))
    In the first argument of `plus', namely `4'
    In the expression: plus 4
    In the definition of `it': it = plus 4

Ou seja, um erro todo bonito. Ele está a espera de um tuplo e encontra um 4.

Agora para a versão curried.

Prelude> curry plus 4 3
7

Tudo bem. E se passarmos 1 argumento.

Prelude> curry plus 4

<interactive>:1:0:
    No instance for (Show (Integer -> Integer))
      arising from a use of `print' at <interactive>:1:0-11
    Possible fix:
      add an instance declaration for (Show (Integer -> Integer))
    In the expression: print it
    In a 'do' expression: print it

Ele dá erro na mesma. Mas este erro é diferente. Ele aqui queixa-se que não consegue arranjar uma representação gráfica (Show) para uma função do tipo Integer -> Integer.

Se formos ver o tipo da função

Prelude> :t curry plus 4
curry plus 4 :: Integer -> Integer

Ou seja, exactamente o que estávamos à espera. A função curried após receber o 1º argumento devolveu uma função.

E tarahhh com isto vem de arrasto muitos goodies, entre os quais, partial application. Podes fazer na boa algo tipo:

Prelude> map (curry plus 4) [1..5]
[5,6,7,8,9]

EDIT: Bahh o Baderous antecipou-me  :down:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Viva!

Obrigado pelas respostas!

O livro que estou a ler é o: Introduction to functional programming using Haskell [AMAZON].

Pelo que percebi o currying é uma maneira de fixar o primeiro argumento, ou seja, é como se encapsulasse a função original numa outra função. Exemplo (java - o meu haskell ainda não dá para muito.... :) )

função original:

public int plus(int x, int y)
{
  return x+y;
}

função curryied(?):

public int plus(int y)
{
return X + y; //o X já foi definido, na função "original"
}

E assim já podemos chamar a função da seguinte maneira: (plus 4) 3

Estou certo?

Com os melhores cumprimentos,

Manifest0

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Betovsky, tem como você explica melhor como isso acontece:

map (curry plus 4) [1..5]

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Betovsky, tem como você explica melhor como isso acontece:

map (curry plus 4) [1..5]

Isso é equivalente a

[curry plus 4 1, curry plus 4 2, ..., curry plus 4 5]

<=> [plus (4, 1), plus (4, 2), ..., plus (4, 5)]

<=> [5, 6, 7, 8, 9]

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

@Manifest0

É algo tipo isso. Não é bem a mesma coisa, para simular mesmo o esquema teria-se de usar closures, algo que Java só irá ter no 1.7.

É uma das grandes falhas de Java, acho que é a única linguagem (juntamente com C++, acho eu) que não permite essa funcionalidade.

@eMineiro

Exactamente como o Rui Carlos demonstrou.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

E assim já podemos chamar a função da seguinte maneira: (plus 4) 3

Pensa assim:

Versão curried

plus :: Integer -> Integer -> Integer //não precisas dos parêntesis
plus x y = x+y

plus 3 4 = 7

Versão uncurried

plus :: (Integer,Integer) -> Integer
plus (x,y) = x+y

plus (3,4) = 7

Para usares a 1ª passas os argumentos um a um. Para a 2ª tens de os passar num par. Os erros que ocorrem em cada situação já foram explicados pelo Betovsky.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ok.

Acho que já percebi.

Obrigado pelas respostas.

Com os melhores cumprimentos,

Manifest0

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora