Geben Sie Inferenz auf anonyme Funktionen mit Enrich-my-Library ein
-
26-10-2019 - |
Frage
Sagen Sie, ich habe eine Methode, die eine (Funktion auf zwei Elementen) in eine (Funktion auf zwei Sequenzen) verwandelt:
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
In Worten erfordert die resultierende Funktion zwei Sequenzen xs
und ys
, und erzeugt eine neue Sequenz aus (xs(0) f ys(0), xs(1) f ys(1), ...)
Also zum Beispiel, wenn xss
ist Seq(Seq(1,2),Seq(3,4))
und f
ist (a: Int, b: Int) => a + b
, Wir können es so aufrufen:
xss reduceLeft seqed(f) // Seq(4, 6)
oder mit einer anonymen Funktion:
xss reduceLeft seqed[Int](_+_)
Das ist ziemlich gut; Es wäre schön, das loszuwerden [Int]
Geben Sie Argument ein, aber ich sehe nicht wie (irgendwelche Ideen?).
Damit es sich ein bisschen mehr wie das anfühlt tupled
Methode habe ich auch das AnAnrich-My-Library-Muster ausprobiert:
class SeqFunction[T](f: (T,T) => T) {
def seqed: (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
}
implicit def seqFunction[T](f: (T,T) => T) = new SeqFunction(f)
Für eine vordefinierte Funktion funktioniert dies hervorragend, aber es ist hässlich mit anonymen
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
Gibt es eine andere Möglichkeit, dies neu formulieren, damit die Typen abgeleitet werden, und ich kann Syntax so etwas wie folgt:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? Oder frage ich zu viel Typ -Inferenz?
Lösung 3
Der Grund, warum eine Typanentwicklung erforderlich ist in
xss reduceLeft seqed[Int](_+_)
aber nicht in
xs zip ys map Function.tupled(_+_)
ist auf den Unterschied in den Typanforderungen zwischen dem zwischen map
und reduceLeft
.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
reduceLeft
erwartet seqed
Typ zurückgeben B
wo B >: Int
. Es scheint daher der genaue Typ für seqed
Kann nicht bekannt sein, also müssen wir die Annotation bereitstellen. Weitere Informationen in diese Frage.
Eine Möglichkeit, dies zu überwinden, besteht darin, erneut zu implementieren reduceLeft
ohne die untere Grenze.
implicit def withReduceL[T](xs: Seq[T]) = new {
def reduceL(f: (T, T) => T) = xs reduceLeft f
}
Prüfen:
scala> Seq(Seq(1,2,3), Seq(2,2,2)) reduceL seqed(_+_)
res1: Seq[Int] = List(3, 4, 5)
Das Problem ist nun, dass dies jetzt nicht auf Subtypen von funktioniert Seq
(z.B List
), mit oder ohne die [Int]
Parameter:
scala> Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
<console>:11: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
^
reduceL
erwartet eine Funktion des Typs (List[Int], List[Int]) => List[Int]
. Da Function2
ist definiert als Function2 [-T1, -T2, +R]
, (Seq[Int], Seq[Int]) => Seq[Int]
ist keine gültige Substitution.
Andere Tipps
Sie können es nicht so machen, wie Sie es wollen, aber schauen Sie sich an Function.tupled
, was eine Gegenpartie ist zu .tupled
Das löst das gleiche Problem.
scala> List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
<console>:8: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
<console>:8: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
scala> List(1, 2, 3) zip List(1, 2, 3) map Function.tupled(_ + _)
res7: List[Int] = List(2, 4, 6)
Ich bin mir ziemlich sicher, dass du dich bist sind zu viel fragen. Geben Sie Inferenz in Scala ein geht von links nach rechts, so die Art von (_+_)
muss zuerst herausgefunden werden .sedeq
Teil. Und dort sind nicht genügend Informationen.