11.4. Przekształcanie metod w funkcje
Metody nie są wartościami i nie są obiektami. Nie można ich wobec tego przypisać do zmiennej lub przekazać jako argumentu innej metodzie. Z kolei funkcje są wartościami i można z nimi takie operacje przeprowadzać. Choć metody nie są funkcjami, to można je przekształcić w funkcje. W pliku Functions6.scala zdefiniowana jest metoda greet.
Plik Functions6.scala: def greet(greeting: String, name: String, surname: String) = greeting + " " + name + " " + surname + "!"
Metodę greet można przekształcić na funkcję w następujący sposób.
scala> :load Functions6.scala Loading Functions6.scala... greet: (greeting: String, name: String, surname: String)String scala> val g = greet _ g: (String, String, String) => String = <function3> scala> g("Hello","John","Brown") res0: String = Hello John Brown!
Po nazwie metody został dodany znak _. Nazwa metody i znak pokreślenia są w powyższym wyrażeniu rozdzielone spacją. Gdyby tej spacji nie było, to cały napis greet_ zostałby potraktowany jako identyfikator.
Plik Functions7.scala: def hello = "Hello!"
Metody bezparametrowe, takie jak metoda hello z pliku Functions7.scala, również można przekształcić na funkcje. Takie metody zostają przekształcone na funkcje z pustą listą parametrów.
scala> :load Functions7.scala Loading Functions7.scala... hello: String scala> val h = hello _ h: () => String = <function0> scala> h() res1: String = Hello!
Metoda hello jest bezparametrowa i można ją wywołać bez konieczności podawania po jej nazwie nawiasów okrągłych. Natomiast funkcja h wymaga nawiasów okrągłych przy wywołaniu.
Metoda above5method, zdefiniowana w pliku Functions8.scala, porównuje liczbę podaną jako jej argument z wartością 5.
Plik Functions8.scala: def above5method(x:Int) = x > 5
Ponieważ jest to metoda, to nie można jej użyć po prawej stronie znaku równości w poniższym poleceniu.
scala> :load Functions8.scala Loading Functions8.scala... above5method: (x: Int)Boolean scala> val above5 = above5method <console>:11: error: missing arguments for method above5method; follow this method with `_' if you want to treat it as a partially applied function val above5 = above5method ^
Można to zrobić po wcześniejszym przekształceniu jej na funkcję poprzez dodanie po nazwie metody znaku podkreślenia.
scala> val above5function = above5method _ above5function: Int => Boolean = <function1>
Poniższe przykłady pokazują, jak można użyć metody filter wraz z funkcją powstałą z metody above5method.
scala> :load Functions5.scala Loading Functions5.scala... filter: (f: Int => Boolean, list: List[Int])List[Int] scala> filter(above5function, List(3,4,5,6,7)) res2: List[Int] = List(6, 7) scala> filter(above5method _, List(3,4,5,6,7)) res3: List[Int] = List(6, 7) scala> filter(above5method, List(3,4,5,6,7)) res4: List[Int] = List(6, 7)
Ostatni z przykładów nie jest błędem, mimo że na pierwszy rzut oka pierwszym argumentem w wywołaniu metody filter jest metoda above5method, a nie utworzona z niej funkcja. Znak podkreślenia służący do oznaczenia zamiany metody na funkcję można pominąć, jeśli odwołanie do metody następuje w kontekście, w którym wymagana jest funkcja. Ponieważ metoda filter spodziewa się funkcji w swoim pierwszym argumencie, taki zapis jest dozwolony. Nie ma w tym miejscu konieczności dodawania znaku podkreślenia.
Metoda greet2, zdefiniowana w pliku Functions9.scala, ma dwie listy parametrów.
Plik Functions9.scala: def greet2(greeting: String)(name: String, surname: String) = greeting + " " + name + " " + surname + "!"
Co się stanie, kiedy zostanie przekształcona na funkcję?
scala> :load Functions9.scala Loading Functions9.scala... greet2: (greeting: String)(name: String, surname: String)String scala> val g2 = greet2 _ g2: String => ((String, String) => String) = <function1>
Otrzymaliśmy funkcję pobierającą jeden parametr i zwracającą inną funkcję. Poniższe polecenie przedstawia wywołanie funkcji g2.
scala> g2("Hello")("John","Brown") res5: String = Hello John Brown!
Funkcja zapisana w wartości g2 pobiera jeden argument — ("Hello") — i zwraca inną funkcję. Ta druga funkcja pobiera dwa argumenty — ("John","Brown") — i zwraca wartość typu String.
Zamiast wywoływać obie funkcje od razu w jednym wyrażeniu, możemy wywołać pierwszą z nich i zapamiętać ją w osobnej wartości.
scala> val welcome = g2("Welcome") welcome: (String, String) => String = <function2>
Teraz możemy wywołać funkcję welcome przekazując jej dwa argumenty.
scala> welcome("John","Brown") res6: String = Welcome John Brown!
Funkcja welcome powstała w dwóch krokach. Najpierw przekształciliśmy metodę greet na funkcję i zapisaliśmy w wartości g2. Następnie wywołaliśmy tę funkcję otrzymując funkcję welcome, która pobiera dwa argumenty i zwraca tekst pozdrowienia.
Możemy od razu otrzymać podobną funkcję za pomocą następującego wyrażenia.
scala> val hello = greet2("Hello")_ hello: (String, String) => String = <function2> scala> hello("Peter","Brown") res7: String = Hello Peter Brown!
Tym razem znak podkreślenia mógł być postawiony zaraz za zamkniętym nawiasem okrągłym, bez potrzeby umieszczania przed nim spacji, gdyż przed znakiem podkreślenia znajduje się nawias i nie zachodzi niebezpieczeństwo zinterpretowania zapisu jako identyfikatora. Powyższe wyrażenie zwraca funkcję podobną do otrzymanej wcześniej w dwóch krokach.
Specyfikacja języka Scala opisuje zamianę metod na funkcje w punkcie 6.7. |