มี Java เทียบเท่ากับคีย์เวิร์ด 'yield' ของ C # หรือไม่?


112

ฉันรู้ว่าไม่มีการเทียบเท่าโดยตรงใน Java แต่อาจเป็นของบุคคลที่สาม?

มันสะดวกมาก ตอนนี้ฉันต้องการใช้ตัววนซ้ำที่ให้โหนดทั้งหมดในทรีซึ่งเป็นโค้ดประมาณห้าบรรทัดพร้อมผลตอบแทน


6
ฉันรู้ว่าฉันรู้ว่า. แต่ฉันคิดว่าการรู้ภาษามากขึ้นนั้นมีพลังมากกว่า นอกจากนี้การพัฒนาแบ็กเอนด์ (ที่ฉันทำ) ใน บริษัท ที่ฉันทำงานอยู่ตอนนี้กำลังดำเนินการใน Java ดังนั้นฉันจึงไม่สามารถเลือกภาษาได้ :(
ripper234

คำตอบ:


91

สองตัวเลือกที่ฉันรู้จักคือไลบรารีคอลเลกชัน infomancers ของ Aviad Ben Dov จากปี 2550และห้องสมุด YieldAdapter ของ Jim Blackler จากปี 2008 (ซึ่งกล่าวถึงในคำตอบอื่น ๆ ด้วย)

ทั้งสองจะช่วยให้คุณสามารถเขียนโค้ดด้วยyield returnโครงสร้าง -like ใน Java ดังนั้นทั้งสองจะตอบสนองคำขอของคุณ ความแตกต่างที่โดดเด่นระหว่างทั้งสองคือ:

กลศาสตร์

ไลบรารีของ Aviad ใช้การจัดการ bytecode ในขณะที่ Jim's ใช้มัลติเธรด ขึ้นอยู่กับความต้องการของคุณแต่ละคนอาจมีข้อดีและข้อเสียของตัวเอง เป็นไปได้ว่าโซลูชันของ Aviad จะเร็วกว่าในขณะที่ Jim's พกพาสะดวกกว่า (เช่นฉันไม่คิดว่าไลบรารีของ Aviad จะทำงานบน Android)

อินเตอร์เฟซ

ไลบรารีของ Aviad มีอินเทอร์เฟซที่สะอาดกว่า - นี่คือตัวอย่าง:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

ในขณะที่จิมมีความซับซ้อนกว่า แต่ต้องการให้คุณadeptใช้ยาสามัญCollectorซึ่งมีcollect(ResultHandler)วิธีการ ... ฮึ อย่างไรก็ตามคุณสามารถใช้กระดาษห่อหุ้มรอบรหัสของ Jim ได้โดย Zoom Informationซึ่งช่วยให้ง่ายขึ้นมาก:

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

ใบอนุญาต

โซลูชันของ Aviad คือ BSD

โซลูชันของจิมเป็นสาธารณสมบัติดังนั้นกระดาษห่อหุ้มที่กล่าวถึงข้างต้น


3
คำตอบที่ยอดเยี่ยม คุณไม่เพียง แต่ตอบคำถามทั้งหมดเท่านั้น แต่คุณยังตอบคำถามได้อย่างชัดเจนอีกด้วย นอกจากนี้ฉันชอบรูปแบบคำตอบของคุณและวิธีที่คุณใส่ข้อมูลใบอนุญาต ติดตามคำตอบที่ยอดเยี่ยม! :)
Malcolm

1
AbstractIteratorอย่าลืมของฝรั่ง
shmosel

ฉันเพิ่งเผยแพร่โซลูชันอื่น (ได้รับใบอนุญาตจาก MIT) ที่นี่ซึ่งเปิดตัวเธรดแยกต่างหากสำหรับผู้ผลิตและตั้งค่าคิวที่มีขอบเขตระหว่างผู้ผลิตและผู้บริโภค: github.com/lukehutch/Producer
Luke Hutchison

โปรดทราบว่าyield(i)จะแตกเป็น JDK 13 เนื่องจากyieldจะถูกเพิ่มเป็นคำสั่ง Java ใหม่ / คีย์เวิร์ดที่สงวนไว้
Luke Hutchison

14

ทั้งสองวิธีนี้สามารถทำความสะอาดได้เล็กน้อยตอนนี้ Java มี Lambdas แล้ว คุณสามารถทำสิ่งที่ชอบ

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

ฉันอธิบายเพิ่มเติมเล็กน้อยที่นี่


4
Yielderableเหรอ? มันควรจะไม่ใช่Yieldableเหรอ? (คำกริยาที่เป็นเพียง 'ให้ผล' ไม่ใช่ 'yielder' หรือ 'ยอมแพ้' หรืออะไรก็ตาม)
Jochem Kuijpers

yield -> { ... }จะหยุดทำงานเมื่อ JDK 13 เนื่องจากyieldจะถูกเพิ่มเป็นคำสั่ง Java ใหม่ / คีย์เวิร์ดที่สงวนไว้
Luke Hutchison

1

ฉันรู้ว่ามันเป็นคำถามเก่ามากที่นี่และมีสองวิธีที่อธิบายไว้ข้างต้น:

  • การจัดการ bytecode นั้นไม่ใช่เรื่องง่ายในขณะที่พอร์ต
  • ตามเธรดyieldที่เห็นได้ชัดว่ามีต้นทุนทรัพยากร

อย่างไรก็ตามมีอีกครั้งที่สามและอาจจะเป็นธรรมชาติมากที่สุดวิธีการของการใช้yieldเครื่องกำเนิดไฟฟ้าใน Java ที่เป็นการดำเนินงานใกล้เคียงกับสิ่ง C # 2.0 + คอมไพเลอร์ทำเพื่อyield return/breakรุ่น: ลอมบอก-PG มันขึ้นอยู่กับเครื่องของรัฐอย่างสมบูรณ์และต้องการความร่วมมืออย่างแน่นหนาjavacเพื่อจัดการกับซอร์สโค้ด AST น่าเสียดายที่ดูเหมือนว่าการสนับสนุน lombok-pg จะหยุดให้บริการ (ไม่มีกิจกรรมพื้นที่เก็บข้อมูลนานกว่าหนึ่งปีหรือสองปี) และProject Lombokดั้งเดิมขาดyieldคุณสมบัตินี้ไปอย่างน่าเสียดาย(มี IDE ที่ดีกว่าเช่น Eclipse, IntelliJ IDEA)


1

Stream.iterate (seed, seedOperator) .limit (n) .foreach (action)ไม่เหมือนกับตัวดำเนินการให้ผลผลิต แต่อาจเป็นประโยชน์ในการเขียนเครื่องกำเนิดไฟฟ้าของคุณเองด้วยวิธีนี้:

import java.util.stream.Stream;
public class Test01 {
    private static void myFoo(int someVar){
        //do some work
        System.out.println(someVar);
    }
    private static void myFoo2(){
        //do some work
        System.out.println("some work");
    }
    public static void main(String[] args) {
        Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo);     //var1
        Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2());  //var2
    }
}

0

ฉันขอแนะนำด้วยว่าคุณใช้RXJavaในโปรเจ็กต์ของคุณอยู่แล้วหรือไม่เพื่อใช้ Observable เป็น "yielder" สามารถใช้ในลักษณะคล้ายกันได้หากคุณสร้าง Observable ของคุณเอง

public class Example extends Observable<String> {

    public static void main(String[] args) {
        new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
    }

    @Override
    protected void subscribeActual(Observer<? super String> observer) {
        observer.onNext("a"); // yield
        observer.onNext("b"); // yield
        observer.onNext("c"); // yield
        observer.onNext("d"); // yield
        observer.onComplete(); // finish
    }
}

Observables สามารถเปลี่ยนเป็นตัววนซ้ำเพื่อให้คุณสามารถใช้มันในแบบดั้งเดิมสำหรับลูปได้มากขึ้น นอกจากนี้ RXJava ยังให้เครื่องมือที่ทรงพลังแก่คุณ แต่ถ้าคุณต้องการเพียงแค่บางสิ่งที่เรียบง่ายนั่นอาจจะเป็นการใช้งานมากเกินไป


0

ฉันเพิ่งเผยแพร่โซลูชันอื่น (ที่ได้รับอนุญาตจาก MIT) ที่นี่ซึ่งเปิดตัวผู้ผลิตในเธรดแยกต่างหากและตั้งค่าคิวที่มีขอบเขตระหว่างผู้ผลิตและผู้บริโภคทำให้สามารถบัฟเฟอร์การควบคุมการไหลและการวางท่อคู่ขนานระหว่างผู้ผลิตและผู้บริโภค (ดังนั้น ผู้บริโภคสามารถดำเนินการเพื่อบริโภครายการก่อนหน้าในขณะที่ผู้ผลิตกำลังดำเนินการผลิตรายการถัดไป)

คุณสามารถใช้แบบฟอร์มคลาสภายในที่ไม่ระบุชื่อนี้:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

ตัวอย่างเช่น:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
    @Override
    public void producer() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Producing " + i);
            produce(i);
        }
        System.out.println("Producer exiting");
    }
}) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

หรือคุณสามารถใช้สัญกรณ์แลมบ์ดาเพื่อตัดลงบนแผ่นสำเร็จรูป:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
    for (int i = 0; i < 20; i++) {
        System.out.println("Producing " + i);
        producer.produce(i);
    }
    System.out.println("Producer exiting");
})) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.