Korzystanie z funkcji częściowych w Scali - jak to działa?

Jestem nowy w Scali, używam 2.9.1 i staram się zrozumieć, jak korzystać z funkcji częściowych. Mam podstawowe zrozumienie funkcji curry i Wiem, że funkcje częściowe są jak funkcje Curry, gdzie są tylko 2nary lub takie. Jak widać, jestem w tym trochę zielony.

Wydaje się, że w niektórych przypadkach, takich jak filtrowanie XML, możliwość częściowych funkcji byłaby bardzo korzystna, więc mam nadzieję, że lepiej zrozumiemy, jak używać oni.

Mam funkcję, która używa struktury RewriteRule, ale potrzebuję jej do pracy z dwoma argumentami, podczas gdy struktura RewriteRule zajmuje tylko jeden lub część funkcji. Myślę, że to jedna z tych spraw, które uważam za pomocne.

Wszelkie rady, linki, słowa mądrości itp. Witamy!

Odpowiedzi do tej pory są doskonałe i wyjaśniły kilka podstawowych nieporozumień, które mam. Myślę, że wyjaśniają również, gdzie mam problemy - myślę, że może zamieszczając nowe pytanie jest nieco bardziej szczegółowe pomoże, więc ja też to zrobię.

Author: PlexQ, 2011-12-28

2 answers

Funkcja częściowa jest funkcją, która jest ważna tylko dla podzbioru wartości tych typów, które można przekazać do niej. Na przykład:

val root: PartialFunction[Double,Double] = {
  case d if (d >= 0) => math.sqrt(d)
}

scala> root.isDefinedAt(-1)
res0: Boolean = false

scala> root(3)
res1: Double = 1.7320508075688772

Jest to przydatne, gdy masz coś, co wie, jak sprawdzić, czy funkcja jest zdefiniowana, czy nie. Zbierać, na przykład:

scala> List(0.5, -0.2, 4).collect(root)   // List of _only roots which are defined_
res2: List[Double] = List(0.7071067811865476, 2.0)

To jest a nie pomoże Ci umieścić dwa argumenty tam, gdzie naprawdę chcesz jeden.

Natomiast częściowo zastosowana funkcja jest funkcją, w której niektóre z jej argumenty zostały już wypełnione.

def add(i: Int, j: Int) = i + j
val add5 = add(_: Int,5)

Teraz trzeba tylko jeden argument--rzecz dodać 5 do--zamiast dwóch:

scala> add5(2)
res3: Int = 7

Możesz zobaczyć z tego przykładu, jak z niego korzystać.

Ale jeśli musisz podać te dwa argumenty, to nadal tego nie zrobi-powiedzmy, że chcesz użyć map, na przykład, i musisz dać mu funkcję jednego argumentu, ale chcesz, aby dodała dwie różne rzeczy. W takim razie możesz

val addTupled = (add _).tupled

Które częściowo będą miały zastosowanie Funkcja (tak naprawdę, wystarczy utworzyć funkcję z metody, ponieważ nic nie zostało wypełnione), a następnie połączyć oddzielne argumenty w krotkę. Teraz możesz użyć tego w miejscach, które wymagają pojedynczego argumentu (zakładając, że typ jest prawidłowy):

scala> List((1,2), (4,5), (3,8)).map(addTupled)
res4: List[Int] = List(3, 9, 11)

Natomiast currying jest znowu inny; zamienia funkcje postaci (A,B) => C w A => B => C. To znaczy, biorąc pod uwagę funkcję złożoną z wielu argumentów, wytworzy łańcuch funkcji, z których każda przyjmuje jeden argument i zwraca łańcuch o jeden krótszy (można o tym myśleć jako o częściowym zastosowaniu jednego argumentu na raz).

val addCurried = (add _).curried

scala> List(1,4,3).map(addCurried)
res5: List[Int => Int] = List(<function1>, <function1>, <function1>)

scala> res5.head(2)   // is the first function, should add 1
res6: Int = 3

scala> res5.tail.head(5)   // Second function should add 4
res7: Int = 9

scala> res5.last(8)  // Third function should add 3
res8: Int = 11
 137
Author: Rex Kerr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-28 00:35:04

Wyjaśnienie Rexa Kerra jest bardzo dobre -- i tam też nie ma niespodzianki. Pytanie jest jednoznaczne z pomieszaniem funkcji częściowych z częściowo zastosowanych funkcji. Jeśli to coś znaczy, to sam zrobiłem to samo zamieszanie, kiedy nauczyłem się Scali.

Jednakże, ponieważ pytanie zwraca uwagę na funkcje częściowe, chciałbym o nich trochę porozmawiać.

Wiele osób twierdzi, że funkcje częściowe to funkcje, które nie są zdefiniowane dla wszystkich danych wejściowych, a to prawda o matematyce, ale nie o Scali. W Scali funkcja nie może być zdefiniowana dla wszystkich danych wejściowych. W rzeczywistości, ponieważ funkcja częściowa dziedziczy od funkcji, wtedy funkcja obejmuje również wszystkie funkcje częściowe, co czyni ją nieuniknioną.

Inni wspominają o metodzie isDefinedAt, która rzeczywiście jest różnicą, ale dotyczy głównie implementacji. Jest to o tyle prawdziwe, że Scala 2.10 prawdopodobnie zostanie wydana z "szybką funkcją częściową", która nie opiera się na isDefinedAt.

I kilka osób nawet implikować, że metoda apply dla funkcji częściowych robi coś innego niż metoda apply dla funkcji, jak tylko wykonywanie dla danych wejściowych, które są zdefiniowane - co nie może być dalej od prawdy. Metoda apply jest dokładnie taka sama.

Do funkcji cząstkowych sprowadza się inna metoda: orElse. To podsumowuje wszystkie przypadki użycia funkcji częściowych znacznie lepiej niż isDefinedAt, ponieważ funkcje częściowe naprawdę polegają na wykonaniu jednej z następujących czynności rzeczy:

  • Łańcuchowanie funkcji częściowych (co robi orElse), tak, że wejście będzie próbowane na każdej funkcji częściowej, dopóki jedna z nich się nie dopasuje.
  • robienie czegoś innego, jeśli funkcja częściowa nie pasuje, zamiast rzucania wyjątku, co by się stało, gdybyś połączył tę inną rzecz za pomocą orElse.

Nie mówię, że wszystko może być łatwo zaimplementowane w kategoriach orElse, pamiętaj. Mówię tylko, że częściowe funkcje polegają na robieniu czegoś innego, gdy dane wejściowe nie są dla niego zdefiniowane.

 35
Author: Daniel C. Sobral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-28 16:46:57