2011-08-22 10 views
15

उदाहरण के लिए मुद्रित करने के लिए अंतिम पंक्तिक्या लूप के लिए स्कैला में अंतिम मामले को अलग-अलग संभालने का कोई तरीका है?

println("}") 
+0

@ निकोलस का उत्तर पहले ही इंगित करता है, यदि आप * वांछित * पुनरावृत्ति के लिए अलग-अलग कार्य करने के लिए अपने वांछित व्यवहार को दोबारा कर सकते हैं, तो यह अक्सर बहुत आसान होता है, आखिरी के बजाए। पहले पुनरावृत्ति की पहचान करना तुच्छ है। स्केल –

+4

+1। यह देखने के लिए अच्छा है कि यह मानक पुस्तकालय में कैसे किया जाता है। –

उत्तर

18

मिलान पैटर्न का उपयोग कर सकते हैं एक अनुशंसा में println से बचने की सलाह देते हैं। यह कभी-कभी किसी संग्रह के बीच में होने वाली बग को ट्रैक करने के लिए उपयोगी हो सकता है, लेकिन अन्यथा कोड को ले जाता है जो रिफैक्टर और परीक्षण करना कठिन होता है।

अधिक आम तौर पर, जीवन आमतौर पर आसान हो जाता है यदि आप को साइड-इफेक्ट के प्रकार को प्रतिबंधित कर सकते हैं। तो बजाय:

for (line <- myData) { 
    println("}, {") 
} 

आप लिख सकते हैं:

val lines = for (line <- myData) yield "}, {" 
println(lines mkString "\n") 

मैं भी यहाँ एक अनुमान लेने के लिए है कि आप उत्पादन में प्रत्येक पंक्ति की सामग्री चाहते थे जा रहा हूँ!

val lines = for (line <- myData) yield (line + "}, {") 
println(lines mkString "\n") 

हालांकि अगर आप सिर्फ सीधे mkString इस्तेमाल किया आप अभी भी बेहतर होगा - कि क्या इसके लिए है!

val lines = myData.mkString("{", "\n}, {", "}") 
println(lines) 

नोट कैसे हम पहली बार एक String उत्पादन कर रहे हैं, फिर एक ही आपरेशन में यह मुद्रण। इस दृष्टिकोण को आसानी से अलग-अलग तरीकों से विभाजित किया जा सकता है और आपकी कक्षा में toString लागू करने के लिए या परीक्षण में उत्पन्न स्ट्रिंग का निरीक्षण करने के लिए उपयोग किया जा सकता है।

6

आप एक उदाहरण के रूप TraversableOnce विशेषता के addString समारोह का समय लग सकता लगता है मुझे

for (line <- myData) { 
    println("}, {") 
    } 

पाने के लिए एक रास्ता है नहीं है।

def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = { 
    var first = true 

    b append start 
    for (x <- self) { 
    if (first) { 
     b append x 
     first = false 
    } else { 
     b append sep 
     b append x 
    } 
    } 
    b append end 

    b 
} 

आपके मामले में, विभाजक }, { है और अंत }

+0

+1 का उपयोग करने के लिए –

28

आप का लाभ लेने के लिए अपने कोड refactor कर सकते हैं है निर्मित mkString?

scala> List(1, 2, 3).mkString("{", "}, {", "}") 
res1: String = {1}, {2}, {3} 
2

आप में निर्मित mkString समारोह का उपयोग नहीं करना चाहते, तो आप कर सकते हैं की तरह

for (line <- lines) 
    if (line == lines.last) println("last") 
    else println(line) 

अद्यतन कुछ:didierd टिप्पणी में उल्लेख किया है, इस समाधान क्योंकि अंतिम मान गलत है कई बार हो सकता है, वह अपने answer में बेहतर समाधान प्रदान करता है।

क्योंकि last समारोह "प्रभावी ढंग से निरंतर समय" उनके लिए Lists के लिए के रूप में लेता है, यह Vectors के लिए ठीक है, यह रेखीय समय लगता है,, मैं तो तुम आगे किसी भी जाने से पहले

@tailrec 
def printLines[A](l: List[A]) { 
    l match { 
    case Nil => 
    case x :: Nil => println("last") 
    case x :: xs => println(x); printLines(xs) 
    } 
} 
+1

रिकर्सिव समाधान ठीक है। == lines.last वाला एक गलत है जब तक कि आपको पता न हो कि अंतिम तत्व का मान कहीं और नहीं होता है। –

+0

@didierd धन्यवाद, मुझे याद आया। मैंने जवाब में गलती की ओर इशारा किया। – 4e6

+0

मैंने '==' के बजाय 'eq' का उपयोग करके इसे आजमाया और यह अभी भी काम नहीं करता है - स्ट्रिंग्स को कैश किया जाना चाहिए, जो दर्द का थोड़ा सा दर्द है –

12

मैं mkstring का उपयोग करने से पहले जो कहा गया है उससे पहले पूरी तरह सहमत हूं, और पिछले एक के बजाय पहले पुनरावृत्ति को अलग करता हूं। क्या आपको अभी भी अंतिम पर अंतर करने की आवश्यकता होगी, स्कैला संग्रह में init विधि है, जो सभी तत्वों को अंतिम रूप देता है लेकिन अंतिम। तो तुम

for(x <- coll.init) workOnNonLast(x) 
workOnLast(coll.last) 

(init और last सिर और पूंछ के विपरीत है, जो लेकिन पहले पहली और और सभी कर रहे हैं की जा रही है प्रकार) कर सकते हैं। हालांकि संरचना के आधार पर नोट, वे महंगा हो सकते हैं। Vector पर, वे सभी तेज़ हैं। List पर, जबकि सिर और पूंछ मूल रूप से मुक्त हैं, init और last दोनों सूची की लंबाई में रैखिक हैं। headOption और lastOption आपकी मदद कर सकती है जब संग्रह खाली हो सकता है, द्वारा

for (x <- coll.lastOption) workOnLast(x) 
1

अन्य उत्तर हक mkString की ओर इशारा कर रहे हैं workOnlast की जगह है, और डेटा का एक सामान्य राशि के लिए मैं भी है कि प्रयोग करेंगे।

हालांकि, mkStringStringBuilder के माध्यम से अंतिम परिणाम में स्मृति बनाता है (जमा करता है)। हमारे पास मौजूद डेटा की मात्रा के आधार पर यह हमेशा वांछनीय नहीं है।

इस मामले में, यदि हम चाहते हैं कि हम "प्रिंट" करना चाहते हैं तो हमें पहले बड़े परिणाम बनाने की आवश्यकता नहीं है (और शायद हम इससे भी बचना चाहते हैं)।

def forEachIsLast[A](iterator: Iterator[A])(operation: (A, Boolean) => Unit): Unit = { 
    while(iterator.hasNext) { 
    val element = iterator.next() 
    val isLast = !iterator.hasNext // if there is no "next", this is the last one 
    operation(element, isLast) 
    } 
}  

यह सभी तत्वों से अधिक iterates और operation invokes एक बूलियन मान के साथ बदले में प्रत्येक तत्व गुजर,:

इस सहायक समारोह के कार्यान्वयन पर विचार करें। मान true है यदि तत्व पारित किया गया है अंतिम एक है।

आपके मामले में इसे इस तरह इस्तेमाल किया जा सकता:

forEachIsLast(myData) { (line, isLast) => 
    if(isLast) 
    println("}") 
    else 
    println("}, {") 
} 

हम यहाँ निम्न लाभ हैं:

  • यह प्रत्येक तत्व पर चल रही है, एक के बाद एक, जरूरी में परिणाम अर्जित किए बिना स्मृति (जब तक आप नहीं चाहते)।
  • क्योंकि पूरे आकार को अपने आकार की जांच करने के लिए स्मृति में लोड करने की आवश्यकता नहीं है, अगर यह थका हुआ है या नहीं तो इटरेटर से पूछना पर्याप्त है। आप एक बड़ी फ़ाइल, या नेटवर्क से डेटा पढ़ सकते हैं, आदि
+2

या, थोड़ी अधिक सुंदरता से, शायद: 'प्रत्येक के लिए' बस 'वैल यह = seq हो सकता है।toIterator it.foreach {ऑपरेशन (_, it.hasNext)} ' –

+0

बहुत बहुत धन्यवाद @TheAchechetypalPaul! आपके सुझाव के आधार पर एक कार्यान्वयन का उपयोग करने के लिए मेरा जवाब संपादित किया। –

संबंधित मुद्दे

 संबंधित मुद्दे