Cześć, co to Int => Booleanznaczy? Myślę, że zdefiniowana składnia todef foo(bar: Baz): Bin = expr
Ziu,
@Ziu oznacza, że funkcja „parzysta” przyjmuje wartość Int jako argument i zwraca wartość logiczną jako typ wartości. Możesz więc nazwać „parzystą (3)”, która określa logiczną „fałsz”
Denys Lobur
@DenysLobur dzięki za odpowiedź! Wszelkie odniesienia do tej składni?
Metoda def evenocenia na wywołanie i za każdym razem tworzy nową funkcję (nowe wystąpienie Function1).
def even:Int=>Boolean= _ %2==0
even eq even
//Boolean = falseval even:Int=>Boolean= _ %2==0
even eq even
//Boolean = true
Dzięki defmożesz uzyskać nową funkcję dla każdego połączenia:
val test:()=>Int={val r = util.Random.nextInt
()=> r
}
test()// Int = -1049057402
test()// Int = -1049057402 - same resultdef test:()=>Int={val r = util.Random.nextInt
()=> r
}
test()// Int = -240885810
test()// Int = -1002157461 - new result
valocenia po zdefiniowaniu, def- po wywołaniu:
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
Należy pamiętać, że jest jeszcze trzecia opcja: lazy val.
Ocenia, kiedy wywoływany jest po raz pierwszy:
scala>lazyval even:Int=>Boolean=???
even:Int=>Boolean=<lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Ale zwraca ten sam wynik (w tym przypadku to samo wystąpienie FunctionN) za każdym razem:
lazyval even:Int=>Boolean= _ %2==0
even eq even
//Boolean = truelazyval test:()=>Int={val r = util.Random.nextInt
()=> r
}
test()// Int = -1068569869
test()// Int = -1068569869 - same result
Występ
val ocenia, gdy zdefiniowano.
defocenia przy każdym połączeniu, więc wydajność może być gorsza niż w valprzypadku wielu połączeń. Za jednym razem uzyskasz taką samą wydajność. I bez połączeń nie otrzymasz żadnego obciążenia def, dzięki czemu możesz go zdefiniować, nawet jeśli nie będziesz go używać w niektórych oddziałach.
Z lazy valdostaniesz leniwe ocenę: można zdefiniować go, nawet jeśli nie będzie go używać w niektórych gałęziach, a ocenia się raz lub nigdy, ale dostaniesz trochę narzut od Blokada z podwójnym zatwierdzeniem na każdym dostępu do swoich lazy val.
Jak zauważył @SargeBorsch, możesz zdefiniować metodę, a jest to najszybsza opcja:
def even(i:Int):Boolean= i %2==0
Ale jeśli potrzebujesz funkcji (nie metody) do kompozycji funkcji lub funkcji wyższego rzędu (jak filter(even)), kompilator wygeneruje funkcję z twojej metody za każdym razem, gdy używasz jej jako funkcji, więc wydajność może być nieco gorsza niż z val.
Czy mógłbyś porównać je pod względem wydajności? Nie jest ważne, aby oceniać funkcję przy każdym evenwywołaniu.
Amir Karimi,
2
defmożna użyć do zdefiniowania metody, i jest to najszybsza opcja. @ A.Karimi
Display Name
2
Dla zabawy: 2.12 even eq even.
som-snytt,
Czy istnieje koncepcja funkcji wbudowanych jak w c ++? Pochodzę ze świata c ++, więc wybacz moją ignorancję.
animageofmine
2
Kompilator @animageofmine Scala może próbować wprowadzić metody. Jest do tego @inlineatrybut . Ale nie może wstawiać funkcji, ponieważ wywołanie funkcji jest wywołaniem wirtualnej applymetody obiektu funkcji. JVM może dewirtualizować i włączać takie połączenia w niektórych sytuacjach, ale nie w ogóle.
senia
24
Rozważ to:
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
Czy widzisz różnicę? W skrócie:
def : Przy każdym wywołaniu do evenwywołuje evenponownie treść metody. Ale przy pomocy even2ie val funkcja jest inicjowana tylko raz podczas deklaracji (i dlatego wypisuje się valw linii 4 i nigdy więcej) i za każdym razem jest uzyskiwana ta sama moc wyjściowa. Na przykład spróbuj to zrobić:
scala>import scala.util.Randomimport scala.util.Random
scala>val x ={Random.nextInt }
x:Int=-1307706866
scala> x
res0:Int=-1307706866
scala> x
res1:Int=-1307706866
Po xzainicjowaniu wartość zwracana przez Random.nextIntjest ustawiana jako wartość końcowa x. Następnym razem xzostanie użyte ponownie, zawsze zwróci tę samą wartość.
Możesz także leniwie inicjalizować x. tzn. przy pierwszym użyciu jest inicjowany, a nie podczas deklaracji. Na przykład:
scala>lazyval y ={Random.nextInt }
y:Int=<lazy>
scala> y
res4:Int=323930673
scala> y
res5:Int=323930673
Myślę, że twoje wyjaśnienie może sugerować coś, czego nie zamierzasz. Spróbuj zadzwonić even2dwa razy, raz z 1i raz z 2. Otrzymasz różne odpowiedzi w każdym połączeniu. Tak więc, mimo że printlnnie jest wykonywane w kolejnych połączeniach, nie otrzymujesz tego samego wyniku z różnych wywołań do even2. To, dlaczego printlnnie jest wykonywane ponownie, to inne pytanie.
melston
1
to jest naprawdę bardzo interesujące. Podobnie jak w przypadku val, czyli even2, wartość val jest szacowana do sparametryzowanej wartości. więc tak z val ci ocena funkcji, jej wartości. Println nie jest częścią wartości szacowanej. Jest to część oceny, ale nie szacowana wartość. Sztuczka polega na tym, że oszacowana wartość jest faktycznie sparametryzowaną wartością, która zależy od niektórych danych wejściowych. mądra rzecz
MaatDeamon,
1
@melston dokładnie! właśnie to zrozumiałem, więc dlaczego println nie jest wykonywany ponownie, gdy zmienia się wyjście?
aur
1
@aur to, co jest zwracane przez even2, jest właściwie funkcją (wyrażenie w nawiasach na końcu definicji even2). Ta funkcja jest wywoływana z parametrem, który przekazujesz do even2 za każdym razem, gdy ją wywołujesz.
melston
5
Zobacz:
var x =2// using var as I need to change it to 3 laterval sq = x*x // evaluates right now
x =3// no effect! sq is already evaluated
println(sq)
Zaskakujące, że wydrukuje 4, a nie 9! val (nawet var) jest oceniany natychmiast i przypisywany.
Teraz zmień val na def .. wydrukuje 9! Def to wywołanie funkcji. Będzie ono oceniać przy każdym wywołaniu.
val tzn. „sq” jest według definicji Scali ustalony. Jest oceniany w momencie deklaracji, nie można go później zmienić. W innych przykładach, w których even2 również jest val, ale zadeklarował z podpisem funkcji tj. „(Int => Boolean)”, więc nie jest to typ Int. Jest to funkcja, a jej wartość ustawia się za pomocą następującego wyrażenia
{
println("val");(x => x %2==0)}
Zgodnie z właściwością Scala val, nie można przypisać innej funkcji do even2, ta sama reguła co sq.
O tym, dlaczego wywoływanie funkcji eval2 val nie powoduje ciągłego wypisywania „val”?
Kod źródłowy:
val even2:(Int=>Boolean)={
println("val");(x => x %2==0)}
Wiemy, że w Scali ostatnie wyrażenie powyższego rodzaju wyrażenia (wewnątrz {..}) faktycznie powraca na lewą stronę. Ostatecznie ustawiasz funkcję even2 na „x => x% 2 == 0”, co odpowiada typowi zadeklarowanemu dla typu even2 val, tj. (Int => Boolean), więc kompilator jest szczęśliwy. Teraz even2 wskazuje tylko na funkcję „(x => x% 2 == 0)” (nie jakakolwiek inna instrukcja wcześniej np. Println („val”) itp. Wywołanie event2 z różnymi parametrami faktycznie wywoła ”(x => x% 2 == 0) ”, ponieważ tylko ten zapisywany jest ze zdarzeniem2.
Wykonanie definicji takiej jak def x = enie oceni wyrażenia e. Zamiast e jest oceniana za każdym razem, gdy wywoływane jest x.
Alternatywnie Scala oferuje definicję wartości
val x = e, która dokonuje oceny prawej strony w ramach oceny definicji. Jeśli następnie zostanie użyte x, zostanie ono natychmiast zastąpione przez wstępnie obliczoną wartość e, tak że wyrażenie nie musi być ponownie oceniane.
również Val jest wyceną według wartości. Co oznacza, że wyrażenie po prawej stronie jest oceniane podczas definicji. Gdzie Def jest według oceny nazwy. Nie będzie oceniać, dopóki nie zostanie użyty.
Oprócz powyższych pomocnych odpowiedzi moje ustalenia to:
def test1:Int=>Int={
x => x
}--test1: test1[]=>Int=>Intdef test2():Int=>Int={
x => x+1}--test2: test2[]()=>Int=>Intdef test3():Int=4--test3: test3[]()=>Int
Powyższe pokazuje, że „def” to metoda (z zerowymi parametrami argumentu), która po wywołaniu zwraca inną funkcję „Int => Int”.
Przy tak starym pytaniu i przy tak dużej liczbie odpowiedzi już często pomocne jest wyjaśnienie, w jaki sposób twoja odpowiedź różni się lub uzupełnia informacje zawarte w istniejących odpowiedziach.
Int => Boolean
znaczy? Myślę, że zdefiniowana składnia todef foo(bar: Baz): Bin = expr
Odpowiedzi:
Metoda
def even
ocenia na wywołanie i za każdym razem tworzy nową funkcję (nowe wystąpienieFunction1
).Dzięki
def
możesz uzyskać nową funkcję dla każdego połączenia:val
ocenia po zdefiniowaniu,def
- po wywołaniu:Należy pamiętać, że jest jeszcze trzecia opcja:
lazy val
.Ocenia, kiedy wywoływany jest po raz pierwszy:
Ale zwraca ten sam wynik (w tym przypadku to samo wystąpienie
FunctionN
) za każdym razem:Występ
val
ocenia, gdy zdefiniowano.def
ocenia przy każdym połączeniu, więc wydajność może być gorsza niż wval
przypadku wielu połączeń. Za jednym razem uzyskasz taką samą wydajność. I bez połączeń nie otrzymasz żadnego obciążeniadef
, dzięki czemu możesz go zdefiniować, nawet jeśli nie będziesz go używać w niektórych oddziałach.Z
lazy val
dostaniesz leniwe ocenę: można zdefiniować go, nawet jeśli nie będzie go używać w niektórych gałęziach, a ocenia się raz lub nigdy, ale dostaniesz trochę narzut od Blokada z podwójnym zatwierdzeniem na każdym dostępu do swoichlazy val
.Jak zauważył @SargeBorsch, możesz zdefiniować metodę, a jest to najszybsza opcja:
Ale jeśli potrzebujesz funkcji (nie metody) do kompozycji funkcji lub funkcji wyższego rzędu (jak
filter(even)
), kompilator wygeneruje funkcję z twojej metody za każdym razem, gdy używasz jej jako funkcji, więc wydajność może być nieco gorsza niż zval
.źródło
even
wywołaniu.def
można użyć do zdefiniowania metody, i jest to najszybsza opcja. @ A.Karimieven eq even
.@inline
atrybut . Ale nie może wstawiać funkcji, ponieważ wywołanie funkcji jest wywołaniem wirtualnejapply
metody obiektu funkcji. JVM może dewirtualizować i włączać takie połączenia w niektórych sytuacjach, ale nie w ogóle.Rozważ to:
Czy widzisz różnicę? W skrócie:
def : Przy każdym wywołaniu do
even
wywołujeeven
ponownie treść metody. Ale przy pomocyeven2
ie val funkcja jest inicjowana tylko raz podczas deklaracji (i dlatego wypisuje sięval
w linii 4 i nigdy więcej) i za każdym razem jest uzyskiwana ta sama moc wyjściowa. Na przykład spróbuj to zrobić:Po
x
zainicjowaniu wartość zwracana przezRandom.nextInt
jest ustawiana jako wartość końcowax
. Następnym razemx
zostanie użyte ponownie, zawsze zwróci tę samą wartość.Możesz także leniwie inicjalizować
x
. tzn. przy pierwszym użyciu jest inicjowany, a nie podczas deklaracji. Na przykład:źródło
even2
dwa razy, raz z1
i raz z2
. Otrzymasz różne odpowiedzi w każdym połączeniu. Tak więc, mimo żeprintln
nie jest wykonywane w kolejnych połączeniach, nie otrzymujesz tego samego wyniku z różnych wywołań doeven2
. To, dlaczegoprintln
nie jest wykonywane ponownie, to inne pytanie.Zobacz:
Zaskakujące, że wydrukuje 4, a nie 9! val (nawet var) jest oceniany natychmiast i przypisywany.
Teraz zmień val na def .. wydrukuje 9! Def to wywołanie funkcji. Będzie ono oceniać przy każdym wywołaniu.
źródło
val tzn. „sq” jest według definicji Scali ustalony. Jest oceniany w momencie deklaracji, nie można go później zmienić. W innych przykładach, w których even2 również jest val, ale zadeklarował z podpisem funkcji tj. „(Int => Boolean)”, więc nie jest to typ Int. Jest to funkcja, a jej wartość ustawia się za pomocą następującego wyrażenia
Zgodnie z właściwością Scala val, nie można przypisać innej funkcji do even2, ta sama reguła co sq.
O tym, dlaczego wywoływanie funkcji eval2 val nie powoduje ciągłego wypisywania „val”?
Kod źródłowy:
Wiemy, że w Scali ostatnie wyrażenie powyższego rodzaju wyrażenia (wewnątrz {..}) faktycznie powraca na lewą stronę. Ostatecznie ustawiasz funkcję even2 na „x => x% 2 == 0”, co odpowiada typowi zadeklarowanemu dla typu even2 val, tj. (Int => Boolean), więc kompilator jest szczęśliwy. Teraz even2 wskazuje tylko na funkcję „(x => x% 2 == 0)” (nie jakakolwiek inna instrukcja wcześniej np. Println („val”) itp. Wywołanie event2 z różnymi parametrami faktycznie wywoła ”(x => x% 2 == 0) ”, ponieważ tylko ten zapisywany jest ze zdarzeniem2.
Aby to wyjaśnić, poniżej podano inną wersję kodu.
Co się stanie ? tutaj widzimy „wewnątrz końcowego fn” drukowane wielokrotnie, gdy wywołujesz even2 ().
źródło
Wykonanie definicji takiej jak
def x = e
nie oceni wyrażenia e. Zamiast e jest oceniana za każdym razem, gdy wywoływane jest x.Alternatywnie Scala oferuje definicję wartości
val x = e
, która dokonuje oceny prawej strony w ramach oceny definicji. Jeśli następnie zostanie użyte x, zostanie ono natychmiast zastąpione przez wstępnie obliczoną wartość e, tak że wyrażenie nie musi być ponownie oceniane.źródło
również Val jest wyceną według wartości. Co oznacza, że wyrażenie po prawej stronie jest oceniane podczas definicji. Gdzie Def jest według oceny nazwy. Nie będzie oceniać, dopóki nie zostanie użyty.
źródło
Oprócz powyższych pomocnych odpowiedzi moje ustalenia to:
Powyższe pokazuje, że „def” to metoda (z zerowymi parametrami argumentu), która po wywołaniu zwraca inną funkcję „Int => Int”.
Konwersja metod na funkcje jest dobrze wyjaśniona tutaj: https://tpolecat.github.io/2014/06/09/methods-functions.html
źródło
W REPL
def oznacza
call-by-name
, oceniane na żądanieval oznacza
call-by-value
, oceniane podczas inicjalizacjiźródło