it-swarm.dev

ما هو "رفع" في سكالا؟

أحيانًا عندما أقرأ مقالات في نظام Scala البيئي ، أقرأ مصطلح "lift"/"lifted". لسوء الحظ ، لا يتم تفسير ذلك بالضبط. لقد أجريت بعض الأبحاث ، ويبدو أن الرفع له علاقة بالقيم الوظيفية أو شيء من هذا القبيل ، لكنني لم أتمكن من العثور على نص يوضح ماهية الرفع في الواقع بطريقة ودية للمبتدئين.

يوجد تشويش إضافي من خلال Lift Framework الذي يرفع اسمه ، لكنه لا يساعد في الإجابة على السؤال.

ما هو "رفع" في سكالا؟

239
user573215

هناك عدد قليل من الاستخدامات:

PartialFunction

تذكر أن PartialFunction[A, B] هي وظيفة محددة لبعض المجموعات الفرعية من المجال A (كما هو محدد بواسطة طريقة isDefinedAt). يمكنك "رفع" رمز PartialFunction[A, B] إلى Function[A, Option[B]]. بمعنى ، دالة معرّفة على ككل من A لكن قيمها من النوع Option[B]

يتم ذلك عن طريق استدعاء صريح للأسلوب lift في PartialFunction.

scala> val pf: PartialFunction[Int, Boolean] = { case i if i > 0 => i % 2 == 0}
pf: PartialFunction[Int,Boolean] = <function1>

scala> pf.lift
res1: Int => Option[Boolean] = <function1>

scala> res1(-1)
res2: Option[Boolean] = None

scala> res1(1)
res3: Option[Boolean] = Some(false)

أساليب

يمكنك "رفع" استدعاء طريقة في وظيفة. وهذا ما يسمى إيتا التوسع (بفضل بن جيمس لهذا). هكذا على سبيل المثال:

scala> def times2(i: Int) = i * 2
times2: (i: Int)Int

نرفع طريقة إلى دالة عن طريق تطبيق تسطير أسفل السطر

scala> val f = times2 _
f: Int => Int = <function1>

scala> f(4)
res0: Int = 8

لاحظ الفرق الأساسي بين الأساليب والوظائف. res0 هو مثيل (أي ، هو قيمة ) من النوع (دالة) (Int => Int)

المنفذون

A functor (كما هو محدد بواسطة scalaz ) هو بعض "الحاوية" (يمكنني استخدام المصطلح للغاية فضفاضة) ، F بحيث ، إذا كان لدينا F[A] ووظيفة A => B ، ثم يمكننا الحصول على أيدينا F[B] (على سبيل المثال ، F = List وطريقة map)

يمكننا تشفير هذه الخاصية على النحو التالي:

trait Functor[F[_]] { 
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

هذا هو شكل غير طبيعي لتكون قادرة على "رفع" وظيفة A => B في مجال functor. هذا هو:

def lift[F[_]: Functor, A, B](f: A => B): F[A] => F[B]

هذا هو ، إذا كان F عاملًا ، ولدينا وظيفة A => B ، فلدينا وظيفة F[A] => F[B]. قد تحاول تطبيق الأسلوب lift - إنه أمر تافه للغاية.

محولات مناد

كما hcoopz يقول أدناه (ولقد أدركت للتو أن هذا من شأنه أن ينقذني من كتابة الكثير من التعليمات البرمجية غير الضرورية) ، المصطلح "lift" له أيضًا معنى داخل Monad Transformers . تذكر أن المحولات الأحادية هي طريقة "تكديس" الموناد فوق بعضها البعض (لا يؤلف الموناد).

على سبيل المثال ، افترض أن لديك وظيفة تقوم بإرجاع IO[Stream[A]]. يمكن تحويل هذا إلى محول monad StreamT[IO, A]. الآن قد ترغب في "رفع" بعض القيم الأخرى IO[B] ربما لأنها أيضًا StreamT. يمكنك إما كتابة هذا:

StreamT.fromStream(iob map (b => Stream(b)))

أو هذا:

iob.liftM[StreamT]

هذا يطرح السؤال: لماذا أريد تحويل IO[B] إلى StreamT[IO, B]؟ . الجواب سيكون "للاستفادة من إمكانيات التكوين". لنفترض أن لديك وظيفة f: (A, B) => C

lazy val f: (A, B) => C = ???
val cs = 
  for {
    a <- as                //as is a StreamT[IO, A]
    b <- bs.liftM[StreamT] //bs was just an IO[B]
  }
  yield f(a, b)

cs.toStream //is a Stream[IO[C]], cs was a StreamT[IO, C]
275
oxbow_lakes

هناك استخدام آخر لـ lift الذي صادفته في الأوراق (وليس بالضرورة تلك المرتبطة بـ Scala) وهو التحميل الزائد لوظيفة من f: A -> B مع f: List[A] -> List[B] (أو مجموعات ، مجموعات متعددة ، ...). يستخدم هذا غالبًا لتبسيط الإجراءات لأنه لا يهم ما إذا كان f يطبق على عنصر فردي أو على عناصر متعددة.

غالبًا ما يتم هذا النوع من التحميل الزائد بشكل تعريفي ، على سبيل المثال ،

f: List[A] -> List[B]
f(xs) = f(xs(1)), f(xs(2)), ..., f(xs(n))

أو

f: Set[A] -> Set[B]
f(xs) = \bigcup_{i = 1}^n f(xs(i))

أو حتمية ، على سبيل المثال

f: List[A] -> List[B]
f(xs) = xs map f
21
Malte Schwerhoff

لاحظ أنه قد يتم رفع أي مجموعة تمتد PartialFunction[Int, A] (كما أشار إلى oxbow_lakes) ؛ هكذا على سبيل المثال

Seq(1,2,3).lift
Int => Option[Int] = <function1>

الذي يحول وظيفة جزئية إلى دالة كلية حيث يتم تعيين القيم غير المحددة في المجموعة على None ،

Seq(1,2,3).lift(2)
Option[Int] = Some(3)

Seq(1,2,3).lift(22)
Option[Int] = None

علاوة على ذلك،

Seq(1,2,3).lift(2).getOrElse(-1)
Int = 3

Seq(1,2,3).lift(22).getOrElse(-1)
Int = -1

هذا يدل على نهج أنيق لتجنب الفهرس خارج الحدود الاستثناءات.

19
elm

هناك أيضًا إزاحة ، وهي العملية العكسية للرفع.

إذا تم تعريف رفع كما

تحويل دالة جزئية PartialFunction[A, B] إلى دالة شاملة A => Option[B]

ثم فتح هو

تحويل الدالة الكلية A => Option[B] إلى دالة جزئية PartialFunction[A, B]

تحدد مكتبة Scala القياسية Function.unlift as

def unlift[T, R](f: (T) ⇒ Option[R]): PartialFunction[T, R]

على سبيل المثال ، توفر مكتبة play-json إلغاء التثبيت للمساعدة في إنشاء متسلسلات JSON :

import play.api.libs.json._
import play.api.libs.functional.syntax._

case class Location(lat: Double, long: Double)

implicit val locationWrites: Writes[Location] = (
  (JsPath \ "lat").write[Double] and
  (JsPath \ "long").write[Double]
)(unlift(Location.unapply))
5
Mario Galic