2010-05-05 16 views
28
def foo(x:Int, f:Unit=>Int) = println(f()) 

foo(2, {Unit => 3+4} 

//case1 
def loop:Int = 7 
foo(2, loop) //does not compile 

changing loop to 
//case 2 
def loop():Int = 7 
foo(2, loop) // does not compile 

changing loop to 
//case 3 
def loop(x:Unit): Int = 7 //changing according to Don's Comments 
foo(2,loop) // compiles and works fine 

should'nt case 1 and case 2 also work? dlaczego oni nie działają?Funkcje bez argumentów, z jednostką jako argumentem w scala

definiowania foo jako

def foo(x:Int, y:()=>Int) 

wówczas przypadek 2 działa, ale nie Przypadek 1.

Arent wszyscy powinni pracować, definiowanie funkcji w obu kierunkach.

// również myślę() => Int in foo to zły styl, y: => Int nie działa, komentarze ??

+2

def definiuje metody (które nie należą do pierwszej klasy), a nie funkcje (które * są * pierwszą klasą). Częściowa aplikacja przenosi metody do funkcji, gdy ich potrzebujesz. –

+1

Czy możesz proszę rozwinąć .. Nie dostałem. Czy miałeś na myśli to, że defa nie mogą zostać przekazane. tj. powyższa "pętla" nie może być przekazana innym funkcjom? – scout

+2

Prawidłowo. Kiedy mówimy "pierwsza klasa", mamy na myśli taki sam status, jak każda inna wartość, na przykład Int, String lub instancja klasy zdefiniowanej przez użytkownika. Metody nie mogą być traktowane w ten sposób (można je tylko wywoływać). Z drugiej strony funkcje (które są instancjami (podklasami) FunctionN (gdzie N jest liczbą argumentów)) są pierwiastkami pierwszej klasy z oczywistego powodu, że są instancjami klasy. –

Odpowiedz

74

Scala rozróżnia następujące rzeczy:

  • Funkcje/metod z żadne listy parametrów ("przez -name parametr”jeśli funkcja)
  • Funkcje z jeden pustej listy parametrów
  • Funkcje z jeden parametr typu jednostki

Żaden z nich nie są równoważne, chociaż jako udogodnienie pozwala Scala aby wymazać puste listy parametrów. (Nawiasem mówiąc, dwie listy pusty parametrów nie są również takie same.)

Więc chociaż Unit napisano (), to nie samo jako argument funkcji parens () dla funkcji lub metody. Zamiast tego pomyśl o () jako o Tuple0.

Więc, jeśli powiesz f: Unit => Int, co masz na myśli to „f przyjmuje jeden parametr, ale jest to naprawdę nudny parametr ponieważ jest Unit, które zawsze muszą być takie same nudne Tuple0 wartość ()”. To, co piszesz, jest naprawdę krótkie dla f: (Unit) => Int.

Jeśli powiesz f:() => Int, to znaczy, że „f przyjmuje żadnych parametrów i wytwarza Int”.

Jeśli powiesz f: => Int, to znaczy, że „opóźnić wykonanie cokolwiek oświadczenie generuje wartość Int aż używamy go w tym kodzie (i ponownie ocenić go za każdym razem).” Funkcjonalnie kończy się to w zasadzie takim samym, jak f:() => Int (i wewnętrznie jest konwertowany na tę samą klasę Function0), ale ma inne zastosowanie, prawdopodobnie w celu uzyskania bardziej zwartej formy zamknięć (zawsze pomijasz kod => w kodzie wywołującym).

+0

Więc jeśli mam funkcję def pętli() = 7 To naprawdę nie ma sensu wstawiać jednostki, jak w def pętli (x: Jednostka) = 7, jeśli chcę przekazać go do innej funkcji, jak foo powyżej. – scout

+2

@Scout: Nie ma powodu, aby dodawać w tym przypadku zbędną "Jednostkę". Przydaje się do abstrakcji. Załóżmy, że masz funkcję składu: 'def comp [A, B, C] (f: A => B, g: B => C, a: A) = g (f (a))' i próbujesz przekazać to 'f', który nie zwraca wartości. Co teraz robisz? Okazuje się, że Scala zawsze "zwraca" coś - przynajmniej '()' (jedyną możliwą wartością typu 'Unit'). Teraz możesz napisać 'g', który pobiera dane wejściowe typu' Unit' i użyć powyższej funkcji ogólnej kompozycji. Ale w większości sytuacji po prostu użyj 'def f() = ...' lub 'def f = ...'. –

+1

Dzięki za wyjaśnienia. Próbowałem wymyślić, jak leniwy evaulation. Zwróć uwagę, że spacja pomiędzy ':' i '=' w 'f: => Int' jest ważna. – Jus12

14

() => int Function0 [Int] Chociaż jednostka => int Function1 [jednostka, Int]

scala> val function0:() => Int =() => 5 
function0:() => Int = <function0> 

scala> val function1: Unit => Int = u => 5 
function1: (Unit) => Int = <function1> 

scala> function0() 
res0: Int = 5 

scala> function1("anything") 
res1: Int = 5 

scala> function1(100) 
res2: Int = 5 

scala> 

Należy również zauważyć, że() jest przedmiotem jednostki

scala> function1(()) 
res11: Int = 5 

scala> function1() 
res12: Int = 5 

scala> function1() 
res13: Int = 5 

scala> val unit =() 
unit: Unit =() 


scala> function1(unit) 
res15: Int = 5 

scala> function1 apply unit 
res16: Int = 5 

scala> 
+0

scala> val function1: Unit => Int = Unit => 5 Zauważ, że w tym wierszu ostatnia "Jednostka" nie jest identyfikatorem typu, ale tylko nazwą parametru (można ją zmienić na "x" na przykład). –

+0

@Dimitris Andreou: Tak, masz rację. – Eastsun

+0

Dostaję dlaczego 'function1 (())' i 'function1()' działa, ale nie rozumiem, dlaczego 'function1()' działa. Spodziewam się, że będzie to zgodne z typowymi regułami wywoływania metod w Scali, to znaczy, że potrzebujesz spacji po nazwie funkcji lub wartości jednostki między parametrem parens ... – ThaDon

2

W przypadku 1 i 2 powyżej, wartość zwracana pętla zamiast pętli sam jest typem sprawdzana dla drugiego argumentu foo a nie: Int = jednostka => Int

Zmiana na pętla ma błąd literowy.

Powiązane problemy