Scala'da "bağlama bağlı" nedir?


115

Scala 2.8'in yeni özelliklerinden biri bağlam sınırlarıdır. Bağlama bağlı nedir ve nerede yararlıdır?

Elbette önce araştırdım (ve örneğin bunu buldum ) ancak gerçekten net ve ayrıntılı bir bilgi bulamadım.


8
ayrıca her tür sınırda
Arjan Blokzijl

2
Bu mükemmel cevap, bağlam sınırlarını ve görüş sınırlarını karşılaştırır / karşılaştırır: stackoverflow.com/questions/4465948/…
Aaron Novstrup

Yanıtlar:


107

Eğer buldunuz bu yazıyı ? Dizi iyileştirmeleri bağlamında yeni bağlama bağlı unsuru kapsar.

Genel olarak, bağlam bağlantılı bir tür parametresi biçimdedir [T: Bound]; türden Törtük bir parametre ile birlikte düz tür parametresine genişletilir Bound[T].

tabulateBelirli bir f fonksiyonunun 0'dan belirli bir uzunluğa kadar olan bir sayı aralığına uygulanmasının sonuçlarından bir dizi oluşturan yöntemi düşünün . Scala 2.7'ye kadar, tablo aşağıdaki gibi yazılabilir:

def tabulate[T](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

Scala 2.8'de bu artık mümkün değildir, çünkü doğru temsili oluşturmak için çalıştırma zamanı bilgisi gereklidir Array[T]. Bu bilgiyi ClassManifest[T]yönteme örtük bir parametre olarak iletmek gerekir :

def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

Bir kısaltma biçimi olarak, bunun yerine tür parametresinde bir bağlam sınırı kullanılabilir T, bu da:

def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
    val xs = new Array[T](len)
    for (i <- 0 until len) xs(i) = f(i)
    xs
}

145

Robert'ın cevabı, Bağlam Sınırlarının teknik ayrıntılarını kapsıyor. Size onların anlamları hakkında yorumumu vereceğim.

Scala'da View Bound ( A <% B), 'olarak görülebilir' <:kavramını yakalar (oysa bir üst sınır , 'a'dır' kavramını yakalar). Bir bağlam sınırı ( A : C), bir tür hakkında 'vardır' der. Manifestlerle ilgili örnekleri " Thas a Manifest" olarak okuyabilirsiniz . OrderedAbout vs bağlantısına bağladığınız Orderingörnek, farkı göstermektedir. Bir metod

def example[T <% Ordered[T]](param: T)

parametrenin bir Ordered. İle karşılaştırmak

def example[T : Ordering](param: T)

parametrenin bir ilişkili olduğunu söyleyen Ordering.

Kullanım açısından, sözleşmelerin oluşturulması biraz zaman aldı, ancak bağlam sınırları, görünüm sınırları yerine tercih ediliyor ( görünüm sınırları artık kullanımdan kaldırılmıştır ). Bir öneri, örtük bir tanımı bir kapsamdan diğerine doğrudan başvurmaya gerek kalmadan aktarmanız gerektiğinde bağlam sınırının tercih edilmesidir (bu kesinlikle ClassManifestbir dizi oluşturmak için kullanılan durumdur ).

Görünüm sınırları ve bağlam sınırları hakkında düşünmenin başka bir yolu, ilkinin, arayanın kapsamından örtük dönüşümleri aktarmasıdır. İkincisi, örtük nesneleri arayanın kapsamından aktarır.


2
"Bir" olmaktan ziyade "bir" vardır "veya" olarak görülüyor "benim için kilit fikirdi - bunu başka hiçbir açıklamada görmedim. Aksi halde biraz şifreli operatörlerin / işlevlerin düz bir İngilizce sürümüne sahip olmak, anlamayı çok daha kolay hale getirir - teşekkürler!
DNA

1
@Ben Lings Ne demek istiyorsun .... 'has a' about a type ...? Bir tür hakkında ne var ?
jhegedus

1
@jhegedus İşte benim çözümlemem: "bir tür hakkında", A'nın bir türü ifade ettiği anlamına gelir. "Bir" ifadesi genellikle nesne yönelimli tasarımda nesne ilişkilerini tanımlamak için kullanılır (örneğin, Müşteri "bir" Adrese sahiptir). Ancak burada "vardır" ilişkisi nesneler değil türler arasındadır. Bu gevşek bir benzetme çünkü "var" ilişkisi, OO tasarımında olduğu gibi içsel veya evrensel değildir; bir Müşterinin her zaman bir Adresi vardır, ancak bağlam için bir A'nın her zaman bir C'si yoktur. Bunun yerine, bağlam sınırı, bir C [A] örneğinin dolaylı olarak sağlanması gerektiğini belirtir.
jbyler

Bir aydır Scala öğreniyorum ve bu ay gördüğüm en iyi açıklama bu! Teşekkür ederim @ Ben!
Lifu Huang

@Ben Lings: Teşekkürler, bağlama bağlı olanı anlamak için bu kadar uzun zaman harcadıktan sonra cevabınız çok yardımcı oldu. [ has aBana daha mantıklı geliyor]
Shankar

39

(Bu, parantez içinde bir nottur. Önce diğer cevapları okuyup anlayın.)

Bağlam Sınırları aslında Görünüm Sınırlarını genelleştirir.

Dolayısıyla, Görünüm Sınırı ile ifade edilen bu kod verildiğinde:

scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String

scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence$1: (T) => String)Int

Bu, aynı zamanda tip fonksiyonları temsil eden bir tür diğer adı yardımıyla, Bound Bağlamında ile ifade edilebilir Ftürüne T.

scala> trait To[T] { type From[F] = F => T }           
defined trait To

scala> def f2[T : To[String]#From](t: T) = 0       
f2: [T](t: T)(implicit evidence$1: (T) => java.lang.String)Int

scala> f2(1)
res1: Int = 0

Bir tür oluşturucu ile bağlam sınırı kullanılmalıdır * => *. Bununla birlikte, tür kurucusu Function1bir türdür (*, *) => *. Tür diğer adının kullanımı, türle ikinci tür parametreyi kısmen uygularString , türle ve bağlam bağlı olarak kullanım için doğru türden bir tür yapıcısı verir.

Bir özellik içinde tür takma adını kullanmadan, kısmen uygulanan türleri Scala'da doğrudan ifade etmenize izin veren bir öneri vardır. Daha sonra yazabilirsiniz:

def f3[T : [X](X => String)](t: T) = 0 

#From'un f2'nin tanımındaki anlamını açıklayabilir misiniz? F türünün nerede inşa edildiğinden emin değilim (bunu doğru mu söyledim?)
Collin

1
Bu bir tür üyesini başvuran, bir tür projeksiyon denir FromÇeşidi To[String]. Bir tür bağımsız değişkeni sağlamıyoruz From, bu nedenle bir türe değil, tür oluşturucuya atıfta bulunuyoruz. Bu tür kurucu, bağlam sınırı olarak kullanılacak doğru türdendir - * -> *. Bu, türden Törtük bir parametre gerektirerek tür parametresini sınırlar To[String]#From[T]. Tür takma adlarını genişletin ve işte size kaldı Function1[String, T].
retronym

bu Function1 [T, String] olmalı mı?
ssanj

18

Bu başka bir parantez notudur.

Ben'in işaret ettiği gibi , bağlama bağlı bir tür parametresi ile bir tür sınıfı arasındaki "has-a" kısıtlamasını temsil eder. Başka bir deyişle, belirli bir tür sınıfın örtük bir değerinin var olduğuna dair bir kısıtlamayı temsil eder.

Bağlam sınırını kullanırken, çoğu zaman bu örtük değeri yüzeye çıkarmak gerekir. Örneğin, kısıtlama verildiğinde, T : Orderingçoğu zaman Ordering[T]kısıtlamayı karşılayan örneğine ihtiyaç duyulur . Burada gösterildiği gibi , implicitlyyöntemi veya biraz daha yararlı bir contextyöntemi kullanarak örtük değere erişmek mümkündür :

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) = 
   xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }

veya

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
   xs zip ys map { t => context[T]().times(t._1, t._2) }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.