简体   繁体   中英

Can somebody please briefly explain to me what's happening with the IntStream?

I looked up multiple examples online but I just can't seem to grasp what's happening here. When the program prints out the current player and the scores, it's not apparent to me where these are stored. I'm a begginer and I was told to analyze this piece of code to help "better understand" our current class project.

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
import java.util.stream.IntStream;

public interface PigDice {
    public static void main(String... arguments) {
        final int maxScore = 100;
        final int playerCount = 2;
        final String[] yesses = { "y", "Y", "" };

        final Scanner scanner = new Scanner(System.in);
        final Random random = new Random();

        final int[] safeScore = new int[2];
        final int[] score = new int[2];

        IntStream.iterate(0, player -> (player + 1) % playerCount).map(player -> {
            boolean isRolling = true;
            while (isRolling) {
                System.out.printf("Player %d: (%d, %d) Rolling? (y/n) ", player, safeScore[player], score[player]);
            isRolling = safeScore[player] + score[player] < maxScore
                    && Arrays.asList(yesses).contains(scanner.nextLine());
                if (isRolling) {
                    final int rolled = random.nextInt(6) + 1;
                    System.out.printf("Rolled %d\n", rolled);
                    if (rolled == 1) {
                        System.out.printf("Bust! You lose %d but keep %d\n\n", score[player], safeScore[player]);
                        return -1;
                    } else {
                        score[player] += rolled;
                    }
                } else {
                    safeScore[player] += score[player];
                    if (safeScore[player] >= maxScore) {
                        return player;
                    }
                    System.out.printf("Sticking with %d\n\n", safeScore[player]);
                }
            }
            score[player] = 0;
            return -1;
        }).filter(player -> player > -1).findFirst().ifPresent(
                player -> System.out.printf("\n\nPlayer %d wins with a score of %d", player, safeScore[player]));
    }
}

To understand what this code does, I'd first apply a couple key refactorings:

  1. Promote all of main() 's local variables to static variables of the PigDice class.

  2. Extract the big multi-line lambda that's passed to map() into a static method of the PigDice class. Call this method turn() :

     static int turn(int player) { ... } 

The stream pipeline now looks like this:

    IntStream.iterate(0, player -> (player + 1) % playerCount)
             .map(PigDice::turn)
             .filter(player -> player > -1)
             .findFirst()
             .ifPresent(
                player ->
                    System.out.printf("\n\nPlayer %d wins with a score of %d",
                        player, safeScore[player]));

The IntStream.iterate() method produces a stream of int values starting with the provided initial value, which is zero in this case. Subsequent values are computed by the lambda expression. Here, that expression adds one and computes the remainder with playerCount , which is two in this example. The result is a stream of values 0, 1, 0, 1, 0, 1, ... representing the player number whose turn it is.

The map() stage passes each of these values to the turn() method, which executes all the game logic. But if you boil down this logic, basically it returns -1 if there is no winner and it returns its input argument (the current player number) if that player has won. This results in a stream -1, -1, -1, -1, ... 0 if player 0 has won (or 1 for the last value if player 1 has won).

The result stream from map() is only part of the story. The actual game logic operates by side effects on the game state, which is maintained in the captured variables scanner , random , score , and safeScore .

The filter() stage passes through only values that are greater than -1, thus it drops all those leading -1 values while the game is progressing, producing a value only after a player has won.

The findFirst() stage takes the first element and then terminates the stream. That's why the game eventually terminates.

Now, findFirst() returns an OptionalInt which contains the number of the winning player. In general it could be empty, but in this case it never will be empty because iterate() produces an infinite stream. Since it's never empty, the lambda expression inside of ifPresent() is always executed and is passed the number of the winning player; this prints out the final game result.

That's basically how this works.

Note that, as I and others have pointed out in comments, there are several bad code smells and anti-patterns here, plus a few bugs. I strongly recommend not using this as an example of good lambda and streams coding practices.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM