Qual é a diferença entre:
def even: Int => Boolean = _ % 2 == 0
e
val even: Int => Boolean = _ % 2 == 0
Ambos podem ser chamados como even(10)
.
Qual é a diferença entre:
def even: Int => Boolean = _ % 2 == 0
e
val even: Int => Boolean = _ % 2 == 0
Ambos podem ser chamados como even(10)
.
Respostas:
O método def even
avalia na chamada e cria novas funções sempre (nova instância de Function1
).
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
Com def
você pode obter novas funções em cada chamada:
val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1049057402
test()
// Int = -1049057402 - same result
def test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -240885810
test()
// Int = -1002157461 - new result
val
avalia quando definido, def
- quando chamado:
scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing
scala> def even: Int => Boolean = ???
even: Int => Boolean
scala> even
scala.NotImplementedError: an implementation is missing
Observe que há uma terceira opção: lazy val
.
Ele avalia quando chamado pela primeira vez:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Mas retorna o mesmo resultado (neste caso, a mesma instância de FunctionN
) sempre:
lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
lazy val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1068569869
test()
// Int = -1068569869 - same result
atuação
val
avalia quando definido.
def
avalia em todas as chamadas, portanto, o desempenho pode ser pior do que val
em várias chamadas. Você obterá o mesmo desempenho com uma única chamada. E sem chamadas, você não terá custos indiretos def
, portanto, você pode defini-la mesmo que não a utilize em algumas ramificações.
Com um, lazy val
você obtém uma avaliação preguiçosa: você pode defini-lo mesmo que não o use em alguns ramos, e ele avalia uma vez ou nunca, mas você terá um pouco de sobrecarga com o bloqueio de verificação dupla em todos os acessos ao seu lazy val
.
Como o @SargeBorsch observou, você pode definir o método, e esta é a opção mais rápida:
def even(i: Int): Boolean = i % 2 == 0
Mas se você precisar de uma função (não método) para a composição da função ou para o filter(even)
compilador de funções de ordem superior (como ), gerará uma função do seu método toda vez que você a estiver usando como função, portanto, o desempenho poderá ser um pouco pior do que com val
.
even
é chamada.
def
pode ser usado para definir um método, e essa é a opção mais rápida. @ A.Karimi
even eq even
,.
@inline
atributo para isso. Mas não pode integrar funções porque a chamada de função é uma chamada ao apply
método virtual de um objeto de função. A JVM pode destirtualizar e alinhar essas chamadas em algumas situações, mas não em geral.
Considere isto:
scala> def even: (Int => Boolean) = {
println("def");
(x => x % 2 == 0)
}
even: Int => Boolean
scala> val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>
scala> even(1)
def
res9: Boolean = false
scala> even2(1)
res10: Boolean = false
Você vê a diferença? Em resumo:
def : Para cada chamada para even
, ele chama o corpo do even
método novamente. Mas com even2
ie val , a função é inicializada apenas uma vez durante a declaração (e, portanto, imprime val
na linha 4 e nunca mais) e a mesma saída é usada cada vez que é acessada. Por exemplo, tente fazer isso:
scala> import scala.util.Random
import scala.util.Random
scala> val x = { Random.nextInt }
x: Int = -1307706866
scala> x
res0: Int = -1307706866
scala> x
res1: Int = -1307706866
Quando x
é inicializado, o valor retornado por Random.nextInt
é definido como o valor final de x
. A próxima vez que x
for usada novamente, sempre retornará o mesmo valor.
Você também pode inicializar preguiçosamente x
. ou seja, na primeira vez em que é usado, é inicializado e não durante a declaração. Por exemplo:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2
duas vezes, uma vez com 1
e uma vez com 2
. Você receberá respostas diferentes em cada chamada. Portanto, enquanto o println
não for executado em chamadas subsequentes, você não obtém o mesmo resultado de chamadas diferentes para even2
. Quanto ao motivo de println
não ser executado novamente, essa é uma pergunta diferente.
Veja isso:
var x = 2 // using var as I need to change it to 3 later
val sq = x*x // evaluates right now
x = 3 // no effect! sq is already evaluated
println(sq)
Surpreendentemente, isso imprimirá 4 e não 9! val (even var) é avaliado imediatamente e atribuído.
Agora mude val para def .. ele imprimirá 9! Def é uma chamada de função. Ele avalia cada vez que é chamado.
val ie "sq" é definido pela definição de Scala. É avaliado no momento da declaração, você não pode alterar mais tarde. Em outros exemplos, onde even2 também val, mas declarou com a assinatura da função ou seja "(Int => Boolean)", portanto não é do tipo Int. É uma função e seu valor é definido pela seguinte expressão
{
println("val");
(x => x % 2 == 0)
}
Conforme a propriedade Scala val, você não pode atribuir outra função para even2, mesma regra que sq.
Por que a função eval2 val não está imprimindo "val" repetidamente?
Código Orig:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Sabemos que, em Scala, a última declaração do tipo de expressão acima (dentro de {..}) é realmente retornar ao lado esquerdo. Então você acaba configurando even2 para a função "x => x% 2 == 0", que corresponde ao tipo que você declarou para o tipo even2 val, isto é (Int => Boolean), para que o compilador fique feliz. Agora, even2 apenas aponta para a função "(x => x% 2 == 0)" (nenhuma outra declaração antes, por exemplo, println ("val")) etc. A chamada de event2 com parâmetros diferentes na verdade chama "(x => x% 2 == 0) ", pois somente isso é salvo com o evento2.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Apenas para esclarecer isso, a seguir é apresentada uma versão diferente do código.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
O que vai acontecer ? aqui vemos "inside final fn" impresso repetidamente, quando você chama even2 ().
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
A execução de uma definição como def x = e
não avaliará a expressão e. Em vez disso, é avaliado sempre que x é chamado.
Como alternativa, Scala oferece uma definição de valor
val x = e
, que avalia o lado direito como parte da avaliação da definição. Se x for usado posteriormente, será imediatamente substituído pelo valor pré-calculado de e, para que a expressão não precise ser avaliada novamente.
Além disso, Val é uma avaliação por valor. O que significa que a expressão do lado direito é avaliada durante a definição. Onde Def é por avaliação de nome. Não avaliará até que seja usado.
Além das respostas úteis acima, minhas descobertas são:
def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int
def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int
def test3(): Int = 4
--test3: test3[]() => Int
O exemplo acima mostra que “def” é um método (com zero parâmetros de argumento) que retorna outra função "Int => Int" quando invocada.
A conversão de métodos em funções é bem explicada aqui: https://tpolecat.github.io/2014/06/09/methods-functions.html
No REPL,
scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean
scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8
def significa call-by-name
, avaliado sob demanda
médias val call-by-value
, avaliadas durante a inicialização
Int => Boolean
significa? Eu acho que a sintaxe de definição édef foo(bar: Baz): Bin = expr