Apakah ada Java yang setara dengan kata kunci 'hasil' C #?

112

Saya tahu tidak ada padanan langsung di Java itu sendiri, tapi mungkin pihak ketiga?

Ini sangat nyaman. Saat ini saya ingin menerapkan iterator yang menghasilkan semua node dalam pohon, yaitu sekitar lima baris kode dengan hasil.

ripper234
sumber
6
Saya tahu saya tahu. Tapi saya pikir mengetahui lebih banyak bahasa adalah lebih banyak kekuatan. Selain itu, pengembangan backend (yang saya lakukan) di perusahaan tempat saya bekerja saat ini sedang dilakukan di Java, jadi saya tidak bisa memilih bahasa :(
ripper234

Jawaban:

91

Dua opsi yang saya tahu adalah pustaka koleksi infomancers Aviad Ben Dov dari 2007 dan pustaka YieldAdapter Jim Blackler dari 2008 (yang juga disebutkan dalam jawaban lain).

Keduanya akan memungkinkan Anda untuk menulis kode dengan yield returnkonstruksi -seperti di Java, jadi keduanya akan memenuhi permintaan Anda. Perbedaan penting antara keduanya adalah:

Mekanika

Perpustakaan Aviad menggunakan manipulasi bytecode sementara Jim menggunakan multithreading. Tergantung pada kebutuhan Anda, masing-masing mungkin memiliki kelebihan dan kekurangannya sendiri. Sepertinya solusi Aviad lebih cepat, sedangkan Jim lebih portabel (misalnya, menurut saya perpustakaan Aviad tidak akan berfungsi di Android).

Antarmuka

Perpustakaan Aviad memiliki antarmuka yang lebih bersih - berikut contohnya:

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

Sementara Jim jauh lebih rumit, membutuhkan Anda untuk adeptgenerik Collectoryang memiliki collect(ResultHandler)metode ... ugh. Namun, Anda dapat menggunakan sesuatu seperti pembungkus ini di sekitar kode Jim dengan Informasi Zoom yang sangat menyederhanakan itu:

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

Lisensi

Solusi Aviad adalah BSD.

Solusi Jim adalah domain publik, begitu juga pembungkusnya yang disebutkan di atas.

ek
sumber
3
Jawaban yang fantastis. Anda tidak hanya menjawab pertanyaan secara total, Anda melakukannya dengan sangat jelas. Juga, saya suka format jawaban Anda dan bagaimana Anda memasukkan informasi lisensi. Pertahankan jawaban yang luar biasa! :)
Malcolm
1
Jangan lupa jambu biji AbstractIterator.
shmosel
Saya baru saja menerbitkan solusi lain (berlisensi MIT) di sini, yang meluncurkan utas terpisah untuk produsen, dan menyiapkan antrean terbatas antara produsen dan konsumen: github.com/lukehutch/Producer
Luke Hutchison
Perhatikan bahwa yield(i)akan berhenti pada JDK 13, karena yieldditambahkan sebagai pernyataan Java baru / kata kunci yang dipesan.
Luke Hutchison
14

Kedua pendekatan ini dapat dibuat sedikit lebih bersih sekarang Java memiliki Lambdas. Anda bisa melakukan sesuatu seperti

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

Saya menjelaskan lebih banyak di sini.

benjiweber
sumber
4
Yielderable? Bukankah seharusnya begitu Yieldable? (kata kerjanya hanya 'hasil', bukan 'yielder' atau 'yielderate' atau apa pun)
Jochem Kuijpers
yield -> { ... }akan rusak pada JDK 13, karena yieldditambahkan sebagai pernyataan Java baru / kata kunci yang dipesan.
Luke Hutchison
1

Saya tahu ini pertanyaan yang sangat lama di sini, dan ada dua cara yang dijelaskan di atas:

  • manipulasi bytecode yang tidak semudah itu saat porting;
  • berbasis benang yieldyang jelas memiliki biaya sumber daya.

Namun, ada cara lain, yang ketiga dan mungkin yang paling alami, untuk mengimplementasikan yieldgenerator di Java yang merupakan implementasi yang paling dekat dengan apa yang dilakukan oleh compiler C # 2.0+ untuk yield return/breakpembuatan: lombok-pg . Ini sepenuhnya didasarkan pada mesin negara, dan membutuhkan kerja sama yang erat javacuntuk memanipulasi kode sumber AST. Sayangnya, dukungan lombok-pg sepertinya dihentikan (tidak ada aktivitas repositori selama lebih dari satu atau dua tahun), dan Project Lombok yang asli sayangnya tidak memiliki yieldfitur (memiliki IDE yang lebih baik seperti Eclipse, namun dukungan IntelliJ IDEA).

Lyubomyr Shaydariv
sumber
1

Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) tidak sama dengan operator hasil, tetapi mungkin berguna untuk menulis generator Anda sendiri dengan cara ini:

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
    }
}
Andrey Lavrukhin
sumber
0

Saya juga menyarankan jika Anda sudah menggunakan RXJava dalam proyek Anda untuk menggunakan Observable sebagai "yielder". Ini dapat digunakan dengan cara yang sama jika Anda membuat Observable Anda sendiri.

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
    }
}

Observable dapat diubah menjadi iterator sehingga Anda bahkan dapat menggunakannya dalam for loop yang lebih tradisional. RXJava juga memberi Anda alat yang sangat kuat, tetapi jika Anda hanya membutuhkan sesuatu yang sederhana maka mungkin ini akan berlebihan.

Győri Sándor
sumber
0

Saya baru saja menerbitkan solusi lain (berlisensi MIT) di sini , yang meluncurkan produsen di utas terpisah, dan menyiapkan antrean terbatas antara produsen dan konsumen, memungkinkan penyanggaan, kontrol aliran, dan pemipaan paralel antara produsen dan konsumen (jadi bahwa konsumen dapat bekerja untuk memakan barang sebelumnya sementara produsen bekerja untuk memproduksi barang berikutnya).

Anda dapat menggunakan formulir kelas dalam anonim ini:

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

sebagai contoh:

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);
}

Atau Anda dapat menggunakan notasi lambda untuk mengurangi boilerplate:

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);
}
Luke Hutchison
sumber