C #의 'yield'키워드에 해당하는 Java가 있습니까?


112

Java 자체에 직접적인 동등 물이 없다는 것을 알고 있지만 아마도 타사일까요?

정말 편리합니다. 현재 저는 수율이있는 약 5 줄의 코드 인 트리의 모든 노드를 생성하는 반복기를 구현하고 싶습니다.


6
내가 알지. 하지만 더 많은 언어를 아는 것이 더 큰 힘이라고 생각합니다. 게다가 제가 지금 일하고있는 회사의 백엔드 개발은 자바로 이루어지고있어서 언어를 선택할 수 없습니다. :(
ripper234

답변:


91

내가 아는 두 가지 옵션 은 2007의 Aviad Ben Dov의 infomancers-collections 라이브러리2008의 Jim Blackler의 YieldAdapter 라이브러리입니다 (다른 답변에서도 언급 됨).

둘 다 yield returnJava에서 -like 구조로 코드를 작성할 수 있으므로 둘 다 요청을 충족시킬 것입니다. 둘 사이의 주목할만한 차이점은 다음과 같습니다.

역학

Aviad의 라이브러리는 바이트 코드 조작을 사용하고 Jim은 멀티 스레딩을 사용합니다. 필요에 따라 각각의 장점과 단점이있을 수 있습니다. Aviad의 솔루션은 더 빠르지 만 Jim은 더 이식성이 뛰어납니다 (예를 들어, 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();
        }
    }
};

Jim 's는 훨씬 더 복잡하지만 방법이 adept있는 제네릭 Collector을 요구합니다 collect(ResultHandler). ugh. 그러나 Zoom Information에 의한 Jim의 코드 주위에 다음과 같은 것을 사용할 수 있습니다 .

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입니다.

Jim의 솔루션은 공개 도메인이며 위에서 언급 한 래퍼도 마찬가지입니다.


3
환상적인 대답입니다. 당신은 질문에 완전히 대답했을뿐만 아니라 매우 명확한 방식으로 대답했습니다. 또한 귀하의 답변 형식과 라이센스 정보를 포함하는 방법이 마음에 듭니다. 멋진 답변을 계속하세요! :)
Malcolm

1
구아바의 AbstractIterator.
shmosel aug

github.com/lukehutch/Producer : 난 그냥 생산자와 소비자 사이의 경계 큐까지 다른 (MIT는 라이센스) 생산자에 대한 별도의 스레드를 실행 여기에 솔루션 및 세트 공개
누가 복음 허치슨

참고 yield(i)때문에, JDK (13)로 중단됩니다 yield새로운 자바 문의 / 예약 키워드로 추가되고있다.
Luke Hutchison

14

이제 Java에는 Lambda가 있습니다. 다음과 같이 할 수 있습니다.

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'또는 'yielderate'또는 무엇이든 단지 '수율'인)
Jochem Kuijpers

yield -> { ... }yield새 Java 문 / 예약 키워드로 추가 되므로 JDK 13부터 중단됩니다 .
Luke Hutchison

1

나는 이것이 매우 오래된 질문이라는 것을 알고 있으며 위에서 설명한 두 가지 방법이 있습니다.

  • 포팅하는 동안 쉽지 않은 바이트 코드 조작;
  • yield분명히 리소스 비용이 드는 스레드 기반 .

그러나 yieldC # 2.0+ 컴파일러가 yield return/break생성을 위해 수행하는 작업에 가장 가까운 구현 인 자바 에서 생성기 를 구현하는 세 번째이자 아마도 가장 자연스러운 방법 인 lombok-pg가 있습니다. 완전히 상태 머신을 기반으로하며 javac소스 코드 AST를 조작하기 위해 긴밀한 협력이 필요합니다 . 안타깝게도 lombok-pg 지원은 중단 된 것으로 보이며 (1 ~ 2 년 이상 리포지토리 활동 없음), 원래 프로젝트 Lombok 에는 안타깝게도이yield 기능 이 없습니다 (이클립스, IntelliJ IDEA 지원과 같은 더 나은 IDE가 있습니다).


1

Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) 은 yield 연산자와 동일하지 않지만 다음과 같은 방식으로 고유 한 생성기를 작성하는 것이 유용 할 수 있습니다.

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

또한 Observable을 "yielder"로 사용하기 위해 프로젝트에서 RXJava 를 이미 사용하고 있는지도 제안합니다 . 자신 만의 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
    }
}

Observable은 반복자로 변환되어 더 전통적인 for 루프에서도 사용할 수 있습니다. 또한 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.