Diferenças entre essas três maneiras de definir uma função em Scala


92

Dadas três maneiras de expressar a mesma função f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

Como essas definições diferem? O REPL não indica quaisquer diferenças óbvias:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>

11
Você deve observar que no segundo bloco acima, a avaliação f1no REPL mostra o valor vinculado estaticamente f1ao avaliar f2e f3mostra o resultado da chamada desses métodos. Em particular, uma nova Function1[Int, Int]instância é produzida toda vez que f2ou f3é invocada, enquanto f1é a mesma Function1[Int, Int]para sempre.
— Randall Schulz,

@RandallSchulz dado que a versão val não requer uma nova instância de função, por que alguém usaria def neste caso?
— virtualeyes

2
@virtualeyes A única situação de que me lembro em que se vê defs produzindo valores FunctionN [...] é na biblioteca do analisador combinador. Não é muito comum escrever métodos que geram funções e virtualmente nunca alguém usaria um def para produzir muitas cópias de uma função semântica / funcionalmente imutável.
— Randall Schulz

Respostas:


112

f1 é uma função que pega um inteiro e retorna um inteiro.

f2é um método com aridade zero que retorna uma função que recebe um inteiro e retorna um inteiro. (Quando você digita f2em REPL mais tarde, torna-se uma chamada para o método f2.)

f3é o mesmo que f2. Você simplesmente não está empregando inferência de tipo aqui.


6
Por que f1é um functione f2é um method?
— Freewind de

17
@Freewind, uma função é um objeto com um método chamado apply. Um método, bem, é um método.
— missingfaktor

Resposta incrível. Pergunta: você diz que f2 tem aridade zero, mas não é unário? en.wikipedia.org/wiki/Arity "Uma função nula não leva argumentos. Uma função unária leva um argumento." Apenas curioso!
— Matthew Cornell

5
@MatthewCornell, por f2si só não aceita argumentos. O objeto de função que ele retorna sim.
— missingfaktor

122

Dentro de uma classe, valé avaliado na inicialização, enquanto defé avaliado apenas quando, e todas as vezes , a função é chamada. No código abaixo, você verá que x é avaliado na primeira vez que o objeto é usado, mas não novamente quando o membro x é acessado. Em contraste, y não é avaliado quando o objeto é instanciado, mas é avaliado toda vez que o membro é acessado.

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

@JacobusR isso é verdade apenas dentro de uma classe?
— Andrew Cassidy

por exemplo: escala> var b = 5 b: Int = 5 escala> val a: (Int => Int) = x => x + ba: Int => Int = <função1> escala> a (5) res48: Int = 10 escala> b = 6 b: Int = 6 escala> a (5) res49: Int = 11 Eu esperava que a (5) retornasse 10 e o valor de b fosse embutido
— Andrew Cassidy

@AndrewCassidy a função aé imutável e avaliada na inicialização, mas bpermanece um valor mutável. Portanto, a referência a bé definida durante a inicialização, mas o valor armazenado por bpermanece mutável. Para se divertir, agora você pode criar um novo val b = 123. Depois disso, você a(5)sempre dará 11, pois bagora é um valor completamente novo.
— Jack

@JacobusR obrigado ... isso faz sentido. Isso coincide com a definição de "escopo léxico", uma vez que a função a carrega uma referência ao "var b" original. Acho que o que me deixou confuso é que dizer: var b = 5; val c = b; b = 6; age de forma diferente. Acho que não devo esperar que uma definição de função que contenha referências ao escopo "léxico" original se comporte da mesma maneira que um Int.
— Andrew Cassidy

3

Executar uma definição como def x = e não avaliará a expressão e . Em vez disso, e é avaliado sempre que x é usado. Alternativamente, Scala oferece uma definição de valor val x = e , que avalia o lado direito e como parte da avaliação da definição. Se x for usado subsequentemente, ele será imediatamente substituído pelo valor pré-calculado de e , de modo que a expressão não precise ser avaliada novamente.

Scala por exemplo de Martin Odersky

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.