2012-09-28 6 views
14

में नेस्टेड सूचियों के डीप-रिवर्स मैं स्कैला में, सूचियों की एक सूची को पीछे हटाना चाहता हूं।स्कैला

मैं लिखा है गहरी सूची इस तरह अजगर में पराजयों:

def deepReverse(items): 
    if type(items) == list: 
     return [deepReverse(item) for item in reversed(items)] 
    else: 
     return items 

मैं स्काला में बराबर कैसे करना होगा? समस्या एल्गोरिदम नहीं है - यह प्रकार की चीजें है, जिसे मैं नया हूं।

मुझे किसी भी मनमानी गहराई के लिए [टी], या सूची [सूची [टी]], या टी की सूची और टी की सूची लेने के लिए फ़ंक्शन की आवश्यकता है। मैंने एक केस क्लास बनाने की कोशिश की जो कि उदाहरण के आधार पर मैंने कहीं और देखा था। मैं ऐसा फ़ंक्शन नहीं चाहता जो किसी को वापस लौटाए और किसी को स्वीकार करता हो; यह धोखाधड़ी की तरह लगता है।

case class NL[+T](val v : Either[List[NL[T]],T]) 

फिर भी, मैं अपने प्रकार को संतुलित करने के लिए काफी कुछ नहीं प्राप्त कर सका। मैं स्कैला के लिए नया हूं, लेकिन मुझे लगा कि यह रिकर्सन और टाइपिंग के साथ गड़बड़ करने का एक सही अवसर होगा।

उत्तर

16

यह वास्तव में प्रकार वर्ग दृष्टिकोण है कि sschaef का प्रस्ताव है कि मनमाने ढंग से नेस्ट सूचियों के लिए काम करेंगे के एक संस्करण लिखने के लिए भी मुश्किल नहीं है: हमारे rev विधि में निहित तर्क ev

trait Reverser[C] { 
    def reverse(xs: C): C 
} 

implicit def rev[A](implicit ev: Reverser[A] = null) = new Reverser[List[A]] { 
    def reverse(xs: List[A]) = 
    Option(ev).map(r => xs map r.reverse).getOrElse(xs).reverse 
} 

def deepReverse[A](xs: A)(implicit ev: Reverser[A]): A = ev.reverse(xs) 

सबूत है कि A ही है उलटा, और ev शून्य है जिसका मतलब है कि यह नहीं है। अगर हमारे पास यह सबूत है कि A उलटा है, तो हम इसका उपयोग List[A] के तत्वों को उलट करने के लिए करते हैं (यह map कर रहा है), और फिर हम सूची को उलट देते हैं। अगर हमारे पास यह सबूत नहीं है (getOrElse केस), तो हम केवल सूची को उलट सकते हैं।

scala> deepReverse(List.tabulate(3)(identity)) 
res0: List[Int] = List(2, 1, 0) 

scala> deepReverse(List.tabulate(2,3) { case (a, b) => a + b }) 
res1: List[List[Int]] = List(List(3, 2, 1), List(2, 1, 0)) 

scala> deepReverse(List.tabulate(2, 3, 4, 5, 6) { 
    | case (a, b, c, d, e) => a + b + c + d + e 
    | }).head.head.head.head 
res2: List[Int] = List(15, 14, 13, 12, 11, 10) 

के रूप में:

implicit def rev[A](implicit ev: Reverser[A] = null) = if (ev == null) { 
    new Reverser[List[A]] { 
    def reverse(xs: List[A]) = xs.reverse 
    } 
} else { 
    new Reverser[List[A]] { 
    def reverse(xs: List[A]) = (xs map ev.reverse).reverse 
    } 
} 

इन दो संस्करणों में से किसी का परीक्षण करने के लिए, हम निम्नलिखित लिख सकते हैं:

हम rev थोड़ा कम संक्षेप में (लेकिन संभवतः अधिक performantly) इस प्रकार लिख सकते अपेक्षित होना।


मैं जोड़ने चाहिए कि निम्नलिखित सही इस तरह के मामले में implicits प्राप्त करने के लिए एक अधिक सामान्य मुहावरा है:

trait ReverserLow { 
    implicit def listReverser[A] = new Reverser[List[A]] { 
    def reverse(xs: List[A]) = xs.reverse 
    } 
} 

object ReverserHigh extends ReverserLow { 
    implicit def nestedListReverser[A](implicit ev: Reverser[A]) = 
    new Reverser[List[A]] { 
     def reverse(xs: List[A]) = xs.map(ev.reverse).reverse 
    } 
} 

import ReverserHigh._ 

हम सिर्फ listReverser और nestedListReverser लिखा एक ही स्तर पर हैं तो

scala> deepReverse(List.tabulate(2, 3)(_ + _)) 
<console>:12: error: ambiguous implicit values: 
both method listReverser... 
and method nestedListReverser... 
match expected type Reverser[List[List[Int]]] 
       deepReverse(List.tabulate(2, 3)(_ + _)) 

दो को प्राथमिकता देने के लिए मानक तरीका है कम प्राथमिकता इम्प डाल करने के लिए: जब हम सूचियों की एक सूची को उल्टा करने की कोशिश हम निम्न त्रुटि प्राप्त होता एक विशेषता में लाइसेंस (WhateverLow) और दूसरा ऑब्जेक्ट (WhateverHigh) जो उस विशेषता को बढ़ाता है।इस तरह के एक साधारण साधारण मामले में, हालांकि, यह ऊपर मेरी rev विधि में डिफ़ॉल्ट तर्क चाल का उपयोग करने के लिए अधिक संक्षिप्त (और मेरी आंखों के लिए स्पष्ट) है। लेकिन आप अन्य लोगों के कोड में अन्य संस्करण देखने की अधिक संभावना है।

+1

अरे! मैंने खुद को टाइपक्लास इंजेक्शन देने का विचार नहीं किया था। महान चाल! – sschaef

+0

यह शानदार है! तो, अगर कोई नक्शा वापस नहीं आया है तो getOrElse मूल्य को वापस कर देता है? बहुत सराहना की। –

+1

@GrittyKitty: धन्यवाद! यहां चाल के बारे में अधिक जानकारी के लिए मेरा अपडेट देखें। –

5

आप करना चाहते हैं यह वास्तव में typesafe तो है, तो typeclass पैटर्न अपने दोस्त है:

object Reverse extends App { 

    trait Reverser[C] { 
    def reverse(xs: C): C 
    } 

    implicit def List1Reverser[A] = new Reverser[List[A]] { 
    def reverse(xs: List[A]) = 
     xs.reverse 
    } 

    implicit def List2Reverser[A] = new Reverser[List[List[A]]] { 
    def reverse(xs: List[List[A]]) = 
     xs.map(_.reverse).reverse 
    } 

    implicit def List3Reverser[A] = new Reverser[List[List[List[A]]]] { 
    def reverse(xs: List[List[List[A]]]) = 
     xs.map(_.map(_.reverse).reverse).reverse 
    } 

    def deepReverse[A](xs: A)(implicit rev: Reverser[A]): A = 
    rev.reverse(xs) 

    val xs = List(1,2) 
    val xxs = List(List(1,2),List(1,2),List(1,2)) 
    val xxxs = List(List(List(1,2),List(1,2)),List(List(1,2),List(1,2)),List(List(1,2),List(1,2))) 

    println(deepReverse(xs)) 
    println(deepReverse(xxs)) 
    println(deepReverse(xxxs)) 
} 

इस के साथ ही समस्या यह है कि आप प्रत्येक नेस्टेड सूची प्रकार के लिए एक typeclass की जरूरत है।

+0

बहुत सराहना की, हालांकि मैं निश्चित रूप से मनमानी गहराइयों के लिए घोंसले की तलाश में हूं, जिसका मतलब है कि मैं वास्तव में हर स्तर के लिए एक नया रिवर्सर परिभाषित नहीं कर सकता। –

+0

क्या आप वाकई "वास्तव में" मनमानी गहराई करेंगे? मेरा मतलब है 10 का स्तर (उदाहरण के लिए) पर्याप्त नहीं है? – sschaef

+0

+1 लेकिन मनमाने ढंग से गहराई संभव है (अगर यह फिट होगा तो मैं यहां एक टिप्पणी में अपना उत्तर पोस्ट कर दूंगा)। –