2015-07-06 11 views
5

मैं एक ऐसी धारा को लागू करने की कोशिश कर रहा हूं जो इसके कार्यान्वयन में स्वयं का एक और उदाहरण उपयोग करता है। धारा में कुछ निरंतर तत्व हैं (IntStream.concat के साथ), इसलिए यह तब तक काम करना चाहिए जब तक संगत धारा नॉन-निरंतर भाग को आलसी बनाता है। मुझे लगता है कि IntStream.concat ("creates a lazily concatenated stream") के साथ StreamSupport.intStream overload taking a Supplier का उपयोग करना चाहिए, जब तत्वों की मांग की जाती है तो केवल दूसरा स्प्लिटरेटर बनाने के लिए पर्याप्त आलसी होना चाहिए, लेकिन यहां तक ​​कि स्ट्रीम (इसका मूल्यांकन नहीं) भी ढेर को बहती है। मैं आलसी धाराओं को कैसे जोड़ सकता हूं?मैं आलसी धाराओं को कैसे जोड़ूं?


मैं बंदरगाह के लिए प्रयास कर रहा हूँ जावा में this answer से स्ट्रीमिंग अभाज्य संख्या चलनी। यह चलनी पाइथन कोड में स्वयं का एक और उदाहरण (ps = postponed_sieve()) का उपयोग करती है। अगर मैं अपने खुद के प्रवाह में प्रारंभिक चार लगातार तत्वों (yield 2; yield 3; yield 5; yield 7;) को तोड़ने, यह एक spliterator के रूप में जनरेटर लागू करने के लिए आसान है:

/** 
* based on https://stackoverflow.com/a/10733621/3614835 
*/ 
static class PrimeSpliterator extends Spliterators.AbstractIntSpliterator { 
    private static final int CHARACTERISTICS = Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED; 
    private final Map<Integer, Supplier<IntStream>> sieve = new HashMap<>(); 
    private final PrimitiveIterator.OfInt postponedSieve = primes().iterator(); 
    private int p, q, c = 9; 
    private Supplier<IntStream> s; 
    PrimeSpliterator() { 
     super(105097564 /* according to Wolfram Alpha */ - 4 /* in prefix */, 
       CHARACTERISTICS); 
     //p = next(ps) and next(ps) (that's Pythonic?) 
     postponedSieve.nextInt(); 
     this.p = postponedSieve.nextInt(); 
     this.q = p*p; 
    } 

    @Override 
    public boolean tryAdvance(IntConsumer action) { 
     for (; c > 0 /* overflow */; c += 2) { 
      Supplier<IntStream> maybeS = sieve.remove(c); 
      if (maybeS != null) 
       s = maybeS; 
      else if (c < q) { 
       action.accept(c); 
       return true; //continue 
      } else { 
       s =() -> IntStream.iterate(q+2*p, x -> x + 2*p); 
       p = postponedSieve.nextInt(); 
       q = p*p; 
      } 
      int m = s.get().filter(x -> !sieve.containsKey(x)).findFirst().getAsInt(); 
      sieve.put(m, s); 
     } 
     return false; 
    } 
} 

मेरे अभाज्य संख्या का पहला प्रयास() विधि एक IntStream साथ एक निरंतर प्रवाह श्रृंखलाबद्ध रिटर्न एक नया PrimeSpliterator:

public static IntStream primes() { 
    return IntStream.concat(IntStream.of(2, 3, 5, 7), 
      StreamSupport.intStream(new PrimeSpliterator())); 
} 

कॉलिंग अभाज्य संख्या() एक StackOverflowError में परिणाम क्योंकि अभाज्य संख्या() हमेशा एक PrimeSpliterator को दर्शाता है, लेकिन PrimeSpliterator के क्षेत्र प्रारंभकर्ता हमेशा अभाज्य संख्या कॉल()।

public static IntStream primes() { 
    return IntStream.concat(IntStream.of(2, 3, 5, 7), 
      StreamSupport.intStream(PrimeSpliterator::new, PrimeSpliterator.CHARACTERISTICS, false)); 
} 

हालांकि, मैं बजाय (, छंटनी के रूप में यह दोहराता है) एक अलग पश्व-अनुरेखन के साथ एक StackOverflowError मिलती है: हालांकि, वहाँ है कि एक प्रदायक, जो lazily PrimeSpliterator बनाने की अनुमति चाहिए लेता StreamSupport.intStream की एक अधिभार है। ध्यान दें कि रिकर्स पूरी तरह से primes() पर कॉल में है - टर्मिनल ऑपरेशन इटरेटर() को लौटाई गई स्ट्रीम पर कभी भी नहीं बुलाया जाता है।

Exception in thread "main" java.lang.StackOverflowError 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator$OfInt.<init>(StreamSpliterators.java:582) 
    at java.util.stream.IntPipeline.lazySpliterator(IntPipeline.java:155) 
    at java.util.stream.IntPipeline$Head.lazySpliterator(IntPipeline.java:514) 
    at java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:352) 
    at java.util.stream.IntPipeline.spliterator(IntPipeline.java:181) 
    at java.util.stream.IntStream.concat(IntStream.java:851) 
    at com.jeffreybosboom.projecteuler.util.Primes.primes(Primes.java:22) 
    at com.jeffreybosboom.projecteuler.util.Primes$PrimeSpliterator.<init>(Primes.java:32) 
    at com.jeffreybosboom.projecteuler.util.Primes$$Lambda$1/834600351.get(Unknown Source) 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513) 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536) 
    at java.util.stream.Streams$ConcatSpliterator.<init>(Streams.java:713) 
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:789) 
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:785) 
    at java.util.stream.Streams$ConcatSpliterator$OfInt.<init>(Streams.java:819) 
    at java.util.stream.IntStream.concat(IntStream.java:851) 
    at com.jeffreybosboom.projecteuler.util.Primes.primes(Primes.java:22) 
    at com.jeffreybosboom.projecteuler.util.Primes$PrimeSpliterator.<init>(Primes.java:32) 
    at com.jeffreybosboom.projecteuler.util.Primes$$Lambda$1/834600351.get(Unknown Source) 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.get(StreamSpliterators.java:513) 
    at java.util.stream.StreamSpliterators$DelegatingSpliterator.estimateSize(StreamSpliterators.java:536) 
    at java.util.stream.Streams$ConcatSpliterator.<init>(Streams.java:713) 
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:789) 
    at java.util.stream.Streams$ConcatSpliterator$OfPrimitive.<init>(Streams.java:785) 
    at java.util.stream.Streams$ConcatSpliterator$OfInt.<init>(Streams.java:819) 
    at java.util.stream.IntStream.concat(IntStream.java:851) 
    at com.jeffreybosboom.projecteuler.util.Primes.primes(Primes.java:22) 

मैं धाराओं lazily पर्याप्त एक धारा इसके कार्यान्वयन में खुद की एक प्रति का उपयोग करने की अनुमति कैसे जोड़ सकते हैं?

+0

@8484 यह कन्स्ट्रक्टर में दो बार उन्नत है, इसलिए मुझे नहीं लगता कि यह आलसी-प्रारंभिक कैसे हो सकता है। (मुझे लगता है कि प्रश्न अभी भी मान्य है, यह देखते हुए कि IntStream.concat को आलसी होने के लिए कैसे दस्तावेज किया गया है।) –

+1

यह आलसी धारा से संबंधित नहीं है, लेकिन 'x -> x + 2 * p' संभावित रूप से एक बग है क्योंकि 'p' एक सदस्य चर है जो लैम्ब्डा का मूल्यांकन करने से पहले बदल सकता है। – Misha

उत्तर

7

आपका स्पष्ट रूप से मानता है कि स्ट्रीम एपीआई स्प्लिटरेटर्स के तत्कालता तक आलस्य की गारंटी देता है; यह सही नहीं है। यह वास्तविक खपत शुरू होने से पहले किसी भी समय स्ट्रीम के स्प्लिटरेटर को तुरंत चालू करने में सक्षम होने की अपेक्षा करता है, उदाहरण के लिए केवल स्ट्रीम की विशेषताओं और रिपोर्ट आकार का पता लगाने के लिए। खपत केवल trySplit, tryAdvance, या forEachRemaining का आह्वान करके शुरू होती है।

यह ध्यान में रखते हुए, आप इसे आवश्यकतानुसार स्थगित चलनी शुरू कर रहे हैं। tryAdvance में else if भाग तक आप इसके किसी भी परिणाम का उपयोग नहीं करते हैं। तो अंतिम संभव पल जो सत्यता देता करने के लिए कोड के लिए कदम:

@Override 
public boolean tryAdvance(IntConsumer action) { 
    for (; c > 0 /* overflow */; c += 2) { 
     Supplier<IntStream> maybeS = sieve.remove(c); 
     if (maybeS != null) 
      s = maybeS; 
     else { 
      if (postponedSieve == null) { 
       postponedSieve = primes().iterator(); 
       postponedSieve.nextInt(); 
       this.p = postponedSieve.nextInt(); 
       this.q = p*p; 
      } 
      if (c < q) { 
       action.accept(c); 
       return true; //continue 

मुझे लगता है कि, यह परिवर्तन, के साथ भी primes() पर अपने पहले ही प्रयास काम करना चाहिए।

आप अपने वर्तमान दृष्टिकोण के साथ रहना चाहते हैं, तो आपको निम्न मुहावरा शामिल हो सकता है:

Stream.<Supplier<IntStream>>of(
()->IntStream.of(2, 3, 5, 7), 
()->intStream(new PrimeSpliterator())) 
.flatMap(Supplier::get); 

हो सकता है कि इस के रूप में आप की जरूरत है आप के रूप में ज्यादा आलस्य देता है।

+0

मुझे खेद है, मुझे समझ में नहीं आता है। पाइथन कोड दूसरे चलनी को दो बार आगे बढ़ाकर शुरू होता है: 'p = अगला (ps) और अगला (ps)', जो आपका सुझाव छोड़ देता है। (साथ ही, यह दृष्टिकोण मेरी विशिष्ट समस्या को हल कर सकता है, यह इंटस्ट्रीम.कोनकैट के बारे में आलसी होने का दावा करने के बारे में मेरे भ्रम को हल नहीं करता है, हालांकि शायद मुझे इसके बारे में कोई कोड नहीं होने के साथ एक अलग प्रश्न पूछना चाहिए।) एक बड़ा पायथन प्रोग्रामर नहीं है, शायद मैं स्रोत के बारे में उलझन में हूं, लेकिन वहां टिप्पणियों में एक सवाल है और जवाब है "यह केवल काम करता है क्योंकि यह आलसी है"। –

+1

यह इसे छोड़ नहीं देता है, यह इसे एक अलग जगह पर रखता है। –

+0

ठीक है, मुझे लगता है कि आपने इसे संपादित किया है, जो अधिक समझ में आता है। स्ट्रीम आलस्य के बारे में अभी भी उलझन में है, लेकिन अगर मैं कभी भी फिर से आता हूं तो मैं इसके बारे में एक और सवाल पूछूंगा। –

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