Heben in Scala

Suraj P 13 Juni 2022
  1. Methoden in Funktionen umwandeln
  2. Transformiere reine Funktionen in Funktoren in Scala
  3. Teilfunktionen in Scala-Funktionen umwandeln
  4. Monadentransformatoren in Scala
  5. Fazit
Heben in Scala

In diesem Artikel geht es um Lifting in der Programmiersprache Scala. Heben hat verschiedene Bedeutungen; es hängt vom Kontext ab, in dem wir es verwenden.

Schauen wir sie uns nacheinander an.

Methoden in Funktionen umwandeln

Wir stoßen manchmal auf Situationen, in denen wir Methoden in Funktionen umwandeln möchten. Dies ist ein Beispiel für das Heben.

Nehmen wir zum Beispiel an, wir haben die folgenden Methoden:

def mul(x: Int) = x*5
def isEven(x: Int) = x%2==0

Jetzt können wir diese Methoden wie folgt zusammenstellen:

isEven(mul(4))

Aber was ist, wenn wir wollen, dass diese Komposition auf funktionale Weise geschieht? Dann müssen wir diese obigen Methoden in Funktionen umwandeln.

Dies kann durch Anheben mit dem Symbol _ in Scala erfolgen.

val mulAns = mul _
val isEvenAns = isEven _

Jetzt können wir sie wie folgt funktional zusammensetzen:

( mulAns andThen isEvenAns)(5)

Vollständiger Arbeitscode:

object MyClass {
    def mul(x: Int) = x*5
    def isEven(x: Int) = x%2==0

    def main(args: Array[String])
    {
        println(isEven(mul(4)));

        val mulAns = mul _
        val isEvenAns = isEven _

        println(( mulAns andThen isEvenAns)(5))
    }
}

Ausgabe:

true
false

Erläuterung:

Die erste Ausgabe ist true, da 4 mit 5 multipliziert wird, was 20 ergibt. Diese 20 wird als Parameter an die Funktion isEven übergeben.

Der zweite Ausgang ist false, da zuerst 5 mit 5 multipliziert wird und dann das Ergebnis in der Funktion isEven geprüft wird.

Transformiere reine Funktionen in Funktoren in Scala

Funktor bezieht sich auf die Zuordnung zwischen Kategorien, ähnlich wie eine Funktion von Entitäten oder Objekten. Zum Beispiel haben wir die Kategorien A und B und einen Funktor F, der die Objekte von A auf die Objekte von B abbildet.

trait Functor[F[_]] {
    def mapping[A, B](X: F[A])(f: A => B): F[B]

    def lifting[A, B](f: A => B): F[A] => F[B] =
        X => map(X)(f)
}

In diesem Zusammenhang bezieht sich Heben darauf, eine Funktion von A=>B zu nehmen und sie in einen Funktor umzuwandeln, indem sie zu einer Funktion der Form F[A] => F[B] gemacht wird. Bei der Arbeit mit verschachtelten Datentypen kann dies sehr nützlich sein.

Teilfunktionen in Scala-Funktionen umwandeln

Lifting bezeichnet in diesem Zusammenhang die Erweiterung der Domain. Teilfunktionen sind Arten von Funktionen, die auf die Unterdomänen von Werten angewendet werden können.

Aber manchmal möchten wir vielleicht ihre Domäne erweitern, und das Heben hilft uns dabei, dies zu erreichen.

Nehmen wir an, wir haben eine partielle Funktion, die die Quadratwurzel einer positiven Zahl liefert.

val sqrt: PartialFunction[Double, Double] =
{
    case temp if temp >= 0 => Math.sqrt(temp)
}

Nun können wir den Definitionsbereich unserer Teilfunktion mit der lift-Methode erweitern, damit sie eleganter aussieht:

def getSqrtRootPartialFunction(t: Double) =
{
    sqrt.lift(t).map(ans => s"Square root of ${t} is ${ans}")
        .getOrElse(s"Cannot calculate square root for t")
}

Vollständiger Arbeitscode:

object MyClass {
     val sqrt: PartialFunction[Double, Double] = {
        case temp if temp >= 0 => Math.sqrt(temp)
    }

    def getSqrtRootPartialFunction(t: Double) = {
        sqrt.lift(t).map(ans => println(s"Square root of ${t} is ${ans}"))
           .getOrElse(println(s"Cannot calculate square root for t"))
    }

    def main(args: Array[String])
    {
        getSqrtRootPartialFunction(35)
    }
}

Ausgabe:

Square root of 35.0 is 5.916079783099616

Erläuterung:

Das Aufheben der sqrt-Funktion hat hier also bewirkt, dass der Bereich auf ganze Doubles von PartialFunction[Double, Double] auf eine Function[Double, Option[Double]] erweitert wurde.

Manchmal wird das Aufheben von Teilfunktionen verwendet, um die Ausnahme Index außerhalb der Grenze zu vermeiden.

Seq("1", "2", "3").lift(1) // Some("2")

Seq("1", "2", "3").lift(7) // none returned

Die Domain wurde von String auf Option[String] erweitert. Wenn wir also versuchen, auf einen außerhalb der Grenze liegenden Wert zuzugreifen, gibt das angehobene Seq None zurück, anstatt eine Ausnahme außerhalb der Grenze auszulösen.

Monadentransformatoren in Scala

Das Kombinieren von Monaden wird unter Verwendung von Monadentransformatoren durchgeführt. Nehmen wir an, wir haben zwei Futures:

val helloWorld: Future[Option[String]] = Future.successful(Some("hello world"))
val language: Future[String] = Future.successful("this is Scala language")

Da nun beide Futures unterschiedliche Domänen haben, können wir Monadentransformatoren verwenden, indem wir Sprache auf OptionT anheben. Dadurch können wir das Ergebnis konsistenter handhaben:

def MonadTransformer() = {
    val message: OptionT[Future, String] = for {
        hello <- OptionT(helloWorld)
        name  <- OptionT.liftF(language)
    } yield println(s"$hello $name")

    val result: Future[Option[String]] = message.value

    Await.result(result, 1 second)
}

Fazit

In diesem Artikel haben wir das Konzept des Hebens und seine Bedeutung in verschiedenen Kontexten kennengelernt. Es ist sehr nützlich, unseren Code zusammensetzbarer und idiomatischer zu schreiben, sodass wir uns mehr auf die Geschäftslogik des Codes konzentrieren können, anstatt Zeit damit zu verschwenden, nur verschiedene Teile davon zu erstellen.

Autor: Suraj P
Suraj P avatar Suraj P avatar

A technophile and a Big Data developer by passion. Loves developing advance C++ and Java applications in free time works as SME at Chegg where I help students with there doubts and assignments in the field of Computer Science.

LinkedIn GitHub

Verwandter Artikel - Scala Function