स्काला में सूचकांक के साथ कुशल पुनरावृत्ति


83

चूंकि स्कैला में forइंडेक्स के साथ पुराने जावा स्टाइल लूप नहीं हैं ,

// does not work
val xs = Array("first", "second", "third")
for (i=0; i<xs.length; i++) {
  println("String #" + i + " is " + xs(i))
}

हम कुशलतापूर्वक और बिना उपयोग किए कैसे पुनरावृति कर सकते हैं var ?

आप ऐसा कर सकते हैं

val xs = Array("first", "second", "third")
val indexed = xs zipWithIndex
for (x <- indexed) println("String #" + x._2 + " is " + x._1)

लेकिन सूची दो बार ट्रेस की गई है - बहुत कुशल नहीं है।


ये सभी अच्छी प्रतिक्रियाएं हैं। 'छोरों' के लिए मैं जावा से जो कुछ याद कर रहा हूं, वह कई इनिशियलाइज़र्स की क्षमता है, और सिर्फ इंक्रीमेंट / डीक्रीमेंट से अधिक का उपयोग करके "इट्रेट" करने की क्षमता है। यह एक उदाहरण है जहां जावा स्कैला से अधिक संक्षिप्त हो सकता है।
तेज़

... "इरीटेट" का उपयोग केवल वेतन वृद्धि / वेतन वृद्धि से अधिक है ... स्केला में लूप हेडर में "इफ" स्थिति के साथ कदम से चलना, या इसके साथ पुनरावृति संभव है। या आप कुछ और खोज रहे हैं?
ओम-नॉम-नॉमिनेट

1
/ * Java * / for (int i = 0, j = 0; i + j <100; i + = j * 2, j + = i + 2) {...} आप इसे Scala में 1 लाइन में कैसे कर सकते हैं?
तेज़

3
@ स्न्नापी: मेरी राय में, स्काला का सबसे स्वाभाविक अनुवाद एक whileलूप होगा। जैसा कि मुझे याद है, कुछ साल पहले एक बहस चल रही थी कि क्या स्काला को जावा के for(;;)पाश को विरासत में लेना चाहिए , और यह तय किया गया कि लाभ जोड़ा जटिलता का औचित्य साबित करने के लिए पर्याप्त नहीं था।
किप्टन बैरोस

जवाबों:


130

दो बार फंसने से भी बदतर, यह जोड़े का एक मध्यस्थ सरणी बनाता है। आप उपयोग कर सकते हैं view। जब आप ऐसा करते हैं collection.view, तो आप पुनरावृत्ति के दौरान अभिनय के रूप में कॉल के बारे में सोच सकते हैं। यदि आप एक उचित पूरी तरह से महसूस किए गए संग्रह को वापस प्राप्त करना चाहते हैं, तो आप forceअंत में कॉल करते हैं। यहाँ यह बेकार और महंगा होगा। इसलिए अपना कोड बदल दें

for((x,i) <- xs.view.zipWithIndex) println("String #" + i + " is " + x)

6
अच्छा विचार, केवल एक ही आघात, लेकिन यह एन जोड़े भी बनाता है, भले ही यह एक नया संग्रह उचित न बनाता हो।
तेज़

2
बिलकुल सही। अच्छी तरह से एक अस्पष्ट उम्मीद हो सकती है कि जेवीएम उन निर्माण को दूर कर सकता है, लेकिन मैं उस पर भरोसा नहीं करूंगा। मुझे ऐसा कोई समाधान दिखाई नहीं देता जो तब सूचकांक पर पुनरावृत्ति पर आधारित नहीं होगा।
डिडिएर डुपोंट

1
@snappy इस एक को एक जवाब के रूप में चुना जाना चाहिए था! अनुक्रमणिका द्वारा तत्वों तक पहुँच, जो कि अन्य अधिकांश उत्तरों में सुझाई गई थी, स्काला की कार्यात्मक प्रकृति का उल्लंघन करती है और लिंक की गई सूचियों पर भयानक रूप से प्रदर्शन करती है (जैसे List, स्काला में सबसे अधिक उपयोग किया जाने वाला संग्रह) - और न केवल उन पर। यहां परapply ऑपरेशन देखें । लिंक किए गए सूची-प्रकार के संग्रह में सूची के ट्रैवर्सल में अनुक्रमणिका के परिणाम से तत्व तक हर पहुंच।
निकिता वोल्कोव

काफी अलग तरीके यहां दिखाए गए हैं: stackoverflow.com/questions/6821194/…
नील

यह कुशल क्यों है? यह एक नई सरणी ऑब्जेक्ट बना रहा है, और एक अतिरिक्त फ़ंक्शन (`दृश्य ') का उपयोग करता है, इसलिए मुझे यह देखने में मुश्किल होती है कि यह डेवलपर और मशीन के लिए कुशल क्यों है, एक तरफ स्मार्टली मुहावरेदार महसूस करने से अलग।
13

69

यह उल्लेख किया गया है स्काला कि करता है के लिए वाक्यविन्यास है forछोरों:

for (i <- 0 until xs.length) ...

या केवल

for (i <- xs.indices) ...

हालांकि, आपने दक्षता भी मांगी। यह पता चला है कि स्काला forसिंटैक्स वास्तव में उच्च क्रम विधियों जैसे कि वाक्य रचना चीनी हैmap , foreachआदि जैसे, कुछ मामलों में इन छोरों अक्षम हो सकता है, उदाहरण के लिए कैसे का अनुकूलन करने के लिए और स्काला में छोरों-comprehensions के लिए?

(अच्छी खबर यह है कि स्काला टीम इसे बेहतर बनाने पर काम कर रही है। यहाँ बग ट्रैकर में मुद्दा है: https://issues.scala-lang.org/browse/SI-4633 )

अत्यधिक दक्षता के लिए, एक whileलूप का उपयोग कर सकता है या, यदि आप इसके उपयोग को दूर करने के लिए जोर देते हैं, तो varपूंछ की पुनरावृत्ति:

import scala.annotation.tailrec

@tailrec def printArray(i: Int, xs: Array[String]) {
  if (i < xs.length) {
    println("String #" + i + " is " + xs(i))
    printArray(i+1, xs)
  }
}
printArray(0, Array("first", "second", "third"))

ध्यान दें कि वैकल्पिक @tailrec एनोटेशन यह सुनिश्चित करने के लिए उपयोगी है कि विधि वास्तव में पूंछ पुनरावर्ती है। Scala संकलक पूंछ-पुनरावर्ती कॉल का अनुवाद बाइट कोड में करता है जबकि लूप्स के बराबर।


+1 सूचकांकों की विधि / कार्य का उल्लेख करने के लिए, क्योंकि मैं इसे ऑफ-द-वन प्रोग्रामिंग त्रुटियों के एक पूरे सेट को समाप्त करने के कारण बेहतर लगता है।
chaotic3quilibrium

1
यहां यह ध्यान दिया जाना चाहिए कि यदि xsकिसी प्रकार की लिंक की गई सूची (जैसे कि व्यापक रूप से उपयोग की जाने वाली List) है, तो इसके तत्वों को इंडेक्स की तरह एक्सेस xs(i)करना रेखीय होगा और इस तरह यह for (i <- xs.indices) println(i + " : " + xs(i))इच्छा से भी बदतर प्रदर्शन करेगा for((x, i) <- xs.zipWithIndex) println(i + " : " + x), क्योंकि इसका परिणाम बहुत अधिक होगा। हुड के नीचे दो ट्रैवर्सल्स। इसलिए विचारों का उपयोग करने का सुझाव देने वाले @didierd के उत्तर को सबसे सामान्य एक और सबसे मुहावरेदार, IMO के रूप में स्वीकार किया जाना चाहिए।
निकिता वोल्कोव

1
यदि अधिकतम दक्षता की आवश्यकता होती है (उदाहरण के लिए, संख्यात्मक कंप्यूटिंग में) यह एक लिंक की गई सूची को पार करने की तुलना में सूचकांक सरणियों के लिए तेज़ है। एक लिंक की गई सूची के नोड्स अलग-अलग ढेर आवंटित किए गए हैं, और विभिन्न मेमोरी स्थानों पर कूदते हुए सीपीयू कैश के साथ अच्छी तरह से नहीं खेला जाता है। यदि एक viewका उपयोग किया जाता है, तो यह उच्च स्तर का अमूर्त भी ढेर और जीसी पर अधिक दबाव डालेगा। मेरे अनुभव में अक्सर संख्यात्मक कोड में ढेर आवंटन से बचने के द्वारा प्राप्त किए जाने वाले प्रदर्शन में 10 का एक कारक होता है।
किपटन बैरोस

20

एक और तरीका:

scala> val xs = Array("first", "second", "third")
xs: Array[java.lang.String] = Array(first, second, third)

scala> for (i <- xs.indices)
     |   println(i + ": " + xs(i))
0: first
1: second
2: third

5
मैं वास्तव में सूचक विधि / कार्य को इंगित करना पसंद करता हूं। यह जटिलता को कम करता है और वस्तुतः "ऑफ द वन" त्रुटियों के एक पूरे सेट को समाप्त कर देता है जो सभी सॉफ्टवेयर इंजीनियरिंग में सबसे आम प्रोग्रामिंग त्रुटि / बग है।
chaotic3quilibrium

14

दरअसल, स्कैला में इंडेक्स के साथ पुराने जावा-स्टाइल लूप हैं:

scala> val xs = Array("first","second","third")
xs: Array[java.lang.String] = Array(first, second, third)

scala> for (i <- 0 until xs.length)
     | println("String # " + i + " is "+ xs(i))

String # 0 is first
String # 1 is second
String # 2 is third

जहां 0 until xs.lengthया 0.until(xs.length)एक RichIntविधि है जो Rangeलूपिंग के लिए उपयुक्त है।

इसके अलावा, आप के साथ पाश की कोशिश कर सकते हैं to:

scala> for (i <- 0 to xs.length-1)
     | println("String # " + i + " is "+ xs(i))
String # 0 is first
String # 1 is second
String # 2 is third

5
xs(i)सूची में O (n ^ 2) की जटिलता बढ़ जाती है
वडज़िम

@Vadzim यह सच है, लेकिन यह जावा के मामले में भी आप के लिए एक लिंक्डलिस्ट के साथ सूचकांकों पर लूप के लिए इस्तेमाल किया जाएगा
francoisr

1
Arrays पर xs (i) के मामले में, उपरोक्त कोड O (n) है, है ना? चूंकि scala में Arrays निकट-निरंतर समय यादृच्छिक अभिगम की पेशकश करता है?
dhfromkorea

2
@dhfromkorea हाँ, Arrays के लिए उपवास होना चाहिए (वास्तव में O (n))
om-nom-nom-


4

स्काला में लूपिंग बहुत सरल है। पूर्व के लिए अपनी पसंद का कोई भी सरणी बनाएं।

val myArray = new Array[String](3)
myArray(0)="0";
myArray(1)="1";
myArray(2)="2";

छोरों के प्रकार,

for(data <- myArray)println(data)

for (i <- 0 until myArray.size)
println(i + ": " + myArray(i))

4

वास्तव में, zipWithIndexएक संग्रह पर कॉल करना इसे पीछे छोड़ देगा और जोड़े के लिए एक नया संग्रह भी बनाएगा। इससे बचने के लिए, आप zipWithIndexसंग्रह के लिए पुनरावृति पर कॉल कर सकते हैं । यह सिर्फ एक नया पुनरावृत्ती लौटाएगा जो पुनरावृति करते समय सूचकांक का ट्रैक रखता है, इसलिए एक अतिरिक्त संग्रह या अतिरिक्त ट्रैवर्सिंग किए बिना।

यह scala.collection.Iterator.zipWithIndexवर्तमान में 2.10.3 में कैसे लागू किया जाता है:

  def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] {
    var idx = 0
    def hasNext = self.hasNext
    def next = {
      val ret = (self.next, idx)
      idx += 1
      ret
    }
  }

यह संग्रह पर एक दृश्य बनाने की तुलना में थोड़ा अधिक कुशल होना चाहिए।


3

स्टडलिब में ऐसा कुछ भी नहीं है जो बिना टापू कचरा पैदा किए आपके लिए ऐसा करेगा, लेकिन खुद लिखना मुश्किल नहीं है। दुर्भाग्य से मैंने कभी भी यह जानने की जहमत नहीं उठाई कि उचित कैनबिलीफ्रॉम इम्प्लान्ट रेनडेंस को कैसे किया जाए ताकि वे इस तरह की चीजों को संग्रह के प्रकार में सामान्य बना सकें, लेकिन अगर यह संभव है, तो मुझे यकीन है कि कोई हमें बताएगा। :)

def foreachWithIndex[A](as: Traversable[A])(f: (Int,A) => Unit) {
  var i = 0
  for (a <- as) {
    f(i, a)
    i += 1
  }
}

def mapWithIndex[A,B](in: List[A])(f: (Int,A) => B): List[B] = {
  def mapWithIndex0(in: List[A], gotSoFar: List[B], i: Int): List[B] = {
    in match {
      case Nil         => gotSoFar.reverse
      case one :: more => mapWithIndex0(more, f(i, one) :: gotSoFar, i+1)
    }
  }
  mapWithIndex0(in, Nil, 0)
}

// Tests....

@Test
def testForeachWithIndex() {
  var out = List[Int]()
  ScalaUtils.foreachWithIndex(List(1,2,3,4)) { (i, num) =>
    out :+= i * num
  }
  assertEquals(List(0,2,6,12),out)
}

@Test
def testMapWithIndex() {
  val out = ScalaUtils.mapWithIndex(List(4,3,2,1)) { (i, num) =>
    i * num
  }

  assertEquals(List(0,3,4,3),out)
}

यह कुछ ऐसा है जो निश्चित रूप से मानक पुस्तकालय में जोड़ने के लिए समझ में आएगा।
तेज़

1
मुझे यकीन नहीं है, जब से आप सामान्य foreach / नक्शा API के अनुरूप होना चाहते हैं, वैसे भी आप tuples के साथ फंस गए हैं।
एलेक्स क्रूज

3

पुनरावृति के कुछ और तरीके:

scala>  xs.foreach (println) 
first
second
third

फ़ॉरच, और इसी तरह का नक्शा, जो कुछ लौटाएगा (फ़ंक्शन के परिणाम, जो कि प्रिंटलाइन, यूनिट के लिए है, इसलिए इकाइयों की एक सूची)

scala> val lens = for (x <- xs) yield (x.length) 
lens: Array[Int] = Array(5, 6, 5)

तत्वों के साथ काम करते हैं, न कि सूचकांक के साथ

scala> ("" /: xs) (_ + _) 
res21: java.lang.String = firstsecondthird

तह

for(int i=0, j=0; i+j<100; i+=j*2, j+=i+2) {...}

पुनरावृत्ति के साथ किया जा सकता है:

def ijIter (i: Int = 0, j: Int = 0, carry: Int = 0) : Int =
  if (i + j >= 100) carry else 
    ijIter (i+2*j, j+i+2, carry / 3 + 2 * i - 4 * j + 10) 

कैरी-भाग कुछ उदाहरण है, i और j के साथ कुछ करने के लिए। यह एक इंट नहीं होना चाहिए।

सरल सामान के लिए, सामान्य छोरों के करीब:

scala> (1 until 4)
res43: scala.collection.immutable.Range with scala.collection.immutable.Range.ByOne = Range(1, 2, 3)

scala> (0 to 8 by 2)   
res44: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)

scala> (26 to 13 by -3)
res45: scala.collection.immutable.Range = Range(26, 23, 20, 17, 14)

या बिना आदेश:

List (1, 3, 2, 5, 9, 7).foreach (print) 

3

मेरे पास निम्नलिखित दृष्टिकोण हैं

object HelloV2 {

   def main(args: Array[String]) {

     //Efficient iteration with index in Scala

     //Approach #1
     var msg = "";

     for (i <- args.indices)
     {
       msg+=(args(i));
     }
     var msg1="";

     //Approach #2
     for (i <- 0 until args.length) 
     {
       msg1 += (args(i));
     }

     //Approach #3
     var msg3=""
     args.foreach{
       arg =>
        msg3 += (arg)
     }


      println("msg= " + msg);

      println("msg1= " + msg1);

      println("msg3= " + msg3);

   }
}

2

एक सरल और कारगर तरीका, के कार्यान्वयन से प्रेरित transformमें SeqLike.scala

    var i = 0
    xs foreach { el =>
      println("String #" + i + " is " + xs(i))
      i += 1
    }

0

प्रस्तावित समाधान इस तथ्य से ग्रस्त हैं कि वे या तो एक संग्रह पर स्पष्ट रूप से पुनरावृति करते हैं या संग्रह को एक फ़ंक्शन में सामान करते हैं। स्काला के सामान्य मुहावरों के साथ चिपके रहना और सूचकांक को सामान्य मानचित्र के अंदर रखना स्वाभाविक है- या फॉर्च-मेथड। यह मेमोइज़िंग का उपयोग करके किया जा सकता है। परिणामी कोड की तरह लग सकता है

myIterable map (doIndexed(someFunction))

यहाँ इस उद्देश्य को प्राप्त करने का एक तरीका है। निम्नलिखित उपयोगिता पर विचार करें:

object TraversableUtil {
    class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] {
        private var index = 0
        override def apply(a: A): B = {
            val ret = f(index, a)
            index += 1
            ret
        }
    }

    def doIndexed[A, B](f: (Int, A) => B): A => B = {
        new IndexMemoizingFunction(f)
    }
}

यह पहले से ही आप सभी की जरूरत है। आप इसे इस प्रकार लागू कर सकते हैं:

import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))

जिसका परिणाम सूची में है

List(97, 99, 101)

इस तरह, आप अपने प्रभावी फ़ंक्शन को लपेटने की कीमत पर सामान्य ट्रैवर्सेबल-फ़ंक्शंस का उपयोग कर सकते हैं। का आनंद लें!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.