Utilizzo di funzioni parziali in Scala: come funziona?

Sono nuovo di Scala, sto usando 2.9.1 e sto cercando di capire come usare le funzioni parziali. Ho una conoscenza di base delle funzioni al curry e so che le funzioni parziali sono come le funzioni al curry in cui sono solo 2nari o altre. Come puoi dire, sono un po ‘verde in questo.

Sembra che in certi casi, come il filtraggio XML, la possibilità di funzioni parziali sarebbe molto vantaggiosa, quindi spero di ottenere una migliore comprensione di come usarli.

Ho una funzione che utilizza la struttura RewriteRule, ma ho bisogno che funzioni con due argomenti, mentre la struttura RewriteRule ne accetta una sola, OPPURE una funzione parziale. Penso che questo sia uno dei casi in cui penso che sia utile.

Qualche consiglio, collegamenti, parole di saggezza, ecc. Benvenuti!

Le risposte finora sono eccellenti e hanno chiarito alcuni malintesi fondamentali che ho. Penso che spieghino anche dove sto lottando – penso che pubblicare una nuova domanda potrebbe essere un po ‘più specifico, quindi lo farò anche io.

Una funzione parziale è una funzione valida solo per un sottoinsieme di valori di quei tipi che potresti passare ad esso. Per esempio:

 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 

Questo è utile quando hai qualcosa che sa come controllare se una funzione è definita o meno. Raccogli, ad esempio:

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

Questo non ti aiuterà a mettere due argomenti dove ne vuoi davvero uno.

Al contrario, una funzione parzialmente applicata è una funzione in cui alcuni dei suoi argomenti sono già stati compilati.

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

Ora hai solo bisogno di un argomento – la cosa per aggiungere 5 a – invece di due:

 scala> add5(2) res3: Int = 7 

Puoi vedere da questo esempio come usarlo.

Ma se hai bisogno di specificare questi due argomenti, questo non lo farà ancora – diciamo che vuoi usare la map , per esempio, e devi dargli una funzione di un argomento, ma vuoi aggiungere due cose diverse . Bene, allora puoi

 val addTupled = (add _).tupled 

che applicherà parzialmente la funzione (in realtà, basta creare una funzione dal metodo, poiché nulla è stato compilato) e quindi combinare gli argomenti separati in una tupla. Ora puoi usarlo in posti che richiedono un singolo argomento (assumendo che il tipo sia corretto):

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

Al contrario, il curry è diverso ancora una volta; trasforma le funzioni della forma (A,B) => C in A => B => C Cioè, data una funzione di più argomenti, produrrà una catena di funzioni che ognuna prende un argomento e restituisce una catena più corta (si può pensare che applichi parzialmente un argomento alla volta).

 val addCurried = (add _).curried scala> List(1,4,3).map(addCurried) res5: List[Int => Int] = List(, , ) 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 

La spiegazione di Rex Kerr è molto buona – e nessuna sorpresa neanche lì. La domanda sta chiaramente mescolando funzioni parziali con funzioni parzialmente applicate . Per quanto valga, ho fatto la stessa confusione quando ho imparato Scala.

Tuttavia, poiché la domanda attira l’attenzione su funzioni parziali, mi piacerebbe parlarne un po ‘.

Molte persone dicono che le funzioni parziali sono funzioni che non sono definite per tutti gli input, e questo è vero per la matematica, ma non per Scala. In Scala, una funzione potrebbe non essere definita per tutti gli input. Infatti, poiché la funzione parziale eredita dalla funzione, la funzione include anche tutte le funzioni parziali, il che rende inevitabile.

Altri menzionano il metodo isDefinedAt , che è, in effetti, una differenza, ma soprattutto l’implementazione. È così vero che Scala 2.10 sarà probabilmente rilasciato con una “funzione parziale veloce”, che non si basa su isDefinedAt .

E alcune persone implicano anche che il metodo apply per le funzioni parziali faccia qualcosa di diverso rispetto al metodo apply per le funzioni, come solo l’esecuzione per l’input che è definito – che non potrebbe essere più lontano dalla verità. Il metodo apply è esattamente lo stesso .

Le funzioni parziali in realtà sono un altro metodo: orElse . Questo riassume tutti i casi d’uso per le funzioni parziali molto meglio di isDefinedAt , perché le funzioni parziali consistono in realtà nel fare una delle seguenti cose:

  • Concatenamento di funzioni parziali (che è ciò che fa orElse ), in modo che venga provato un input su ciascuna funzione parziale fino a quando una di esse corrisponde.
  • Fare qualcosa di diverso se una funzione parziale non corrisponde, invece di lanciare un’eccezione, che è ciò che accadrebbe se si concatenasse quella cosa differente usando orElse .

Non sto dicendo che tutto possa essere facilmente implementato in termini di orElse , attenzione. Sto solo dicendo che le funzioni parziali riguardano il fare qualcos’altro quando un input non è definito per questo.