2010-11-19 17 views
5

üretmek için bir skalet koleksiyonu gerçekleştirin, bir default valued map uygulamaya çalışıyorum ve ayrıca bir DefaultingMap üzerinden, mümkün olduğunda bir DefaultingMap üretmek için filtreler, haritalar vb.Harita, filtre, vb. Doğru türde

class DefaultingMap[K, V](defaultValue: => V) 
extends mutable.HashMap[K, V] 
with mutable.MapLike[K, V, DefaultingMap[K, V]] { 

    override def empty = new DefaultingMap[K, V](defaultValue) 

    override def default(key: K): V = {     
    val result = this.defaultValue 
    this(key) = result 
    result            
    } 
} 

ben tip DefaultingMap Ben filter kullanmak nesneleri alırım ama map kullanmayın zaman:

scala> val counter = new DefaultingMap[Char, Int](0) 
counter: DefaultingMap[Char,Int] = Map() 

scala> for (c <- "ababcbbb") counter(c) += 1 

scala> counter.filter{case (k, v) => v > 1} 
res1: DefaultingMap[Char,Int] = Map((a,2), (b,5)) 

scala> counter.map{case (k, v) => (k, v * 2)} 
res2: scala.collection.mutable.HashMap[Char,Int] = Map((a,4), (c,2), (b,10)) 
Bu iki yöntem arasındaki fark gibi görünüyor

map bir sürdüğünü İşte benim ilk uygulama var örtük CanBuildFrom. Bu yüzden ben CanBuildFrom sağlamak için bir yere bir implicit def olması gerekiyor. İlk sezgi HashMap yaptıklarından yapmak oldu: Ben bu o derlemek için alacağı inanıyorum

object DefaultingMap extends generic.MutableMapFactory[DefaultingMap] { 

    def empty[K, V]: DefaultingMap[K, V] = // Not possible! 

    implicit def canBuildFrom[K, V]: 
    generic.CanBuildFrom[Coll, (K, V), DefaultingMap[K, V]] = 
     new MapCanBuildFrom[K, V] 
} 

ama empty yöntemini tanımlamak imkansız, çünkü bu yaklaşım işe yaramaz - Bilmen gerekenler defaultValue olmalı. CanBuildFrom'u sınıfın kendisinde tanımlayabilseydim, eşlik eden nesne yerine, ben iyi olurdu çünkü defaultValue orada kullanılabilir.

Bunu nasıl çalıştırabilirim?

cevap

5

Değişken harita Scala Builder ler şunlardır: Ama sana başka bir yaklaşım vermek.

Özel harita oluşturma kurallarınız varsa, yapabileceğiniz şeylerden biri, özel fabrikanızı collection.generic.MapFactory benzeri tanımlamaktır. Bunu benzer bir şekilde tanımlamanız gerekir, ancak empty yöntemini ve newBuilder yönteminin defaultValue için ek bir argüman almasını sağlayın.

çizgisinde

şey (Eğer jenerik arkadaşı uygulamak zorunda kalmamasıdır diğer bağlantı önerdi Scala 2.8 koleksiyonları API hakkında daha fazla okursanız, bulacağınız haritaları için nesneleri) :

import collection._                  


class DefaultingMap[K, V](val defaultValue: V)                  
extends mutable.HashMap[K, V]                      
with mutable.MapLike[K, V, DefaultingMap[K, V]] {                 

    override def empty = new DefaultingMap(defaultValue)                

}                             


object DefaultingMap {                        
    def newBuilder[K, V](d: V): DefaultingMap[K, V] = new DefaultingMap[K, V](d)          

    implicit def canBuildFrom[K, V] =                     
    new generic.CanBuildFrom[DefaultingMap[K, V], (K, V), DefaultingMap[K, V]] {          
     def apply(from: DefaultingMap[K, V]) = newBuilder[K, V](from.defaultValue)          
     def apply() = error("unsupported default apply")                
    }                            
}                             


object Main {                          
    def main(args: Array[String]) {                     
    println((new DefaultingMap[Int, Int](5)).defaultValue)               
    println(((new DefaultingMap[Int, Int](5)).map(x => x)).defaultValue)            
    }                             
} 

Baskılar: itiraf

$ scalac defaulting.scala 
$ scala Main 
5 
5 

, yine de bu parametresiz apply için sorunu çözmez.

+0

MutableMaps zaten Builders olmakla ilgili olan nokta güzeldir - bu kodları birkaç yerde basitleştirir. Parametresiz uygulandığında, ne zaman işe yarayacağını biliyor musunuz? Belki de yaygın değilse, bunun için çok endişelenmiyorum. – Steve

+0

BTW, 'newBuilder' yöntemini düşürdünüz ve doğrudan DefaultingMap'i doğrudan oluşturduysanız daha net olabilir. – Steve

+0

Parameterless 'apply',' breakOut' içinde çağrılır, bu benim bildiğim tek yerdir ... Koleksiyon paketi nesnesine bakın. Ve sen de 'newBuilder' hakkında haklısın. Bununla birlikte, yine de “newBuilder” yöntem tanımını yoldaşta bırakırım. “Harita” nın “Seq” lerle aynı şekilde çalışan arkadaşlarına sahip olsaydı, o zaman faydalı olurdu. Artı, onları 'newBuilder' olan standart' MapFactory 'arkadaşlarıyla uyumlu hale getirir. – axel22

0

Scala 2.8 Collections API çok güzel bir belgedir ve tam olarak nerede olduğunu hatırlamama rağmen dönüşümün bu yönünü tartıştığını hatırlıyorum gibi görünüyor. Yukarıdaki 2.8 veya collection.immutable.Map kullanırsanız ben

+3

Hayır, çok fazla değil. ;-) – Steve

2

... çok yararlı değildir sanırım, o zaman withDefault yöntem sizin için kullanılabilir:

val m = collection.immutable.Map(1->"a", 2->"b", 3->"c") 
val n = m withDefaultValue "default" 

// n(7) will return "default" 

GÜNCELLEME

Eğer' koleksiyonu üzerinde haritalama yeniden işleme zincirinin sonuna withDefaultValue taşıyın:

val o = (for ((k, v) <- m) yield (k, v)) withDefaultValue "default" 
// o(0) will return "default" 
+0

"withDefaultValue" yöntemi, "map" ile de çalışmaz. Val o = ((k, v) <- n) verimi (k, v) için deneyin; Yukarıdaki kodunuzdan sonra (0) 've bir' NoSuchElementException' alacaksınız. – Steve

+0

@Steve (şimdi silindi) yorumunuza göre değişmez olması gerekmiyor. İlk sayıyı şu şekilde yapmayı deneyin: "ababcbbb" .groupBy (identity) mapValues ​​{_.size} ' –

+0

İşlem zincirinin sonuna' withDefaultValue 'seçeneği koymak gerçekten iyi bir seçenek değildir. Özellikle bu nesneyi başka birine iade ettiğimde, işlem zincirinin sonunun nerede olduğunu bilmiyorum. İşleme zincirinin neye benzediğini bilmiyorum. – Steve

1

Ben bırakırsanız,

scala> val counter = new DefaultingMap[Char, Int](0) 
counter: DefaultingMap[Char,Int] = Map() 

scala> for (c <- "ababcbbb") counter(c) += 1 

scala> import counter._ 
import counter._ 

scala> counter.map{case (k, v) => (k, v * 2)} 
res1: DefaultingMap[Char,Int] = Map((a,4), (c,2), (b,10)) 

scala> for ((k, v) <- counter; if v > 1) yield (k.toString, v * 2) 
res2: DefaultingMap[java.lang.String,Int] = Map((a,4), (b,10)) 

Ancak: Tamam, burada ne istiyorum çok, çok yakın bir yaklaşım: Ben kapsam içine CanBuildFrom olduğunu alırsanız

class DefaultingMap[K, V](defaultValue: => V) 
extends mutable.HashMap[K, V] 
with mutable.MapLike[K, V, DefaultingMap[K, V]] { 

    override def empty = new DefaultingMap[K, V](defaultValue) 

    override def default(key: K): V = {     
    val result = this.defaultValue 
    this(key) = result 
    result            
    } 

    implicit def canBuildFrom[NK] = 
    new generic.CanBuildFrom[DefaultingMap[K, V], (NK, V), DefaultingMap[NK, V]] { 
     def apply(from: DefaultingMap[K, V]) = 
     new DefaultingMap[NK, V](from.newDefaultValue) 
     def apply() = 
     new DefaultingMap[NK, V](defaultValue) 
    } 

    def newDefaultValue = defaultValue 
} 

, Şimdi, her şey bir cazibe gibi çalışır import counter._ kapalı, daha önce olduğu gibi aynı davranışı alıyorum. Bu implicit def canBuildFrom'u nasıl bulacağımı öğrenebilirsem ...

+0

Bu, sayaç.map {case (k, v) => (k, v.toString)} 'işlevini yaparken bozulduğunu unutmayın. Şimdi varsayılan değer 'Any' türündedir. – Debilski

+0

Bu doğru çıkarılan türdür - siz 'String' öğeleriniz var, ancak bir 'Int' varsayılanı ve ortak paylaştıkları tek şey "Any". Ama bence siz de haklısınız ki, 'NV>: V' 'yi sadece V' lehine bırakmanız daha faydalı olacaktır, böylece bu durumda bir 'HashMap [Char, String]' yerine geri dönersiniz. – Steve

+0

Tamam, önerildiği şekilde düzenledim - artık 'canBuildFrom [NK, NV>: V] 'dır, ancak sadece' canBuildFrom [NK]' dır. – Steve

0

map dönüş türüyle yardımcı olmaz (ancak daha sonra yeni varsayılan değer toplama genellikle map dönüşümü kullanılarak tanımlanamaz). Varsayılan olarak MapFactory bir inşaatçı elde etmek söz konusu türde bir boş harita alır böylece,

class DefaultHashMap[A, B](dflt: => B) extends scala.collection.mutable.Map[A, B] { 
    val underlying = new scala.collection.mutable.HashMap[A, B]() 

    def get(key: A) = underlying.get(key) orElse Some(default(key)) 
    def iterator: Iterator[(A, B)] = underlying.iterator 
    def +=(kv: (A, B)) = { underlying += kv; this } 
    def -=(key: A) = { underlying -= key; this } 
    override def empty: DefaultHashMap[A, B] = new DefaultHashMap[A, B](dflt) 
    override def default(key: A) = dflt 
} 
+1

Evet, ne yazık ki, 'map' dönüş türünün problemi oldukça fazladır. Yapışmak için bu varsayılan değerlere ihtiyacım var. – Steve

İlgili konular