2014-10-18 14 views
5

ho il seguente codice:Java 8 forEach su più IntStreams

IntStream.range(0, width).forEach(x1 -> { 
     IntStream.range(0, height).forEach(y1 -> { 
      IntStream.rangeClosed(x1-1, x1+1).forEach(x2 -> { 
       IntStream.rangeClosed(y1-1, y1+1).forEach(y2 -> { 
        if ((x1 != x2 || y1 != y2) && getNode(x2, y2) != null){ 
         getNode(x1, y1).registerObserverAtNeighbor(getNode(x2, y2)); 
        } 
       }); 
      }); 
     }); 
    }); 

C'è un modo per scrivere quanto sopra utilizzando un minor numero di istruzioni nidificate? È fondamentalmente "per ciascun nodo da (0,0) a (larghezza, altezza) osservatore di registro a nodi da (x-1, y-1) a (x + 1, y + 1), ma non a sé".

risposta

1

Quello che hai sono praticamente 4 cicli annidati. Ha senso perché stai iterando su due dimensioni di una matrice, e quindi, per ogni nodo, esegui un'iterazione su una piccola matrice che comprende i suoi vicini.

Qualcosa di simile.

0000000 
0---000 
0-X-000 
0---000 
0000000 

Immagino che si possa usare una funzione ricorsiva solo per la sintassi, sebbene non ci sia alcun vantaggio.

iterateLambdas(0, width, 0, height, 1); 

public static void iterateLambdas(
     int start1, 
     int end1, 
     int start2, 
     int end2, 
     int depth) { 
    IntStream.range(start1, end1).forEach(x1 -> { 
     IntStream.range(start2, end2).forEach(y1 -> { 
        if (depth != 0) { 
          iterateLambdas(x1 - 1, x1 + 2, y1 - 1, y1 + 2, depth - 1); 
        } else { 
          // Current node : (start1 + 1), (start2 + 1) 
          // Current neighbour : x1, y1); 
          // Your logic here 
      } 
     }); 
    }); 
} 
0

Poiché si opera sui nodi, suggerisco di creare flussi di nodi in primo luogo. Tieni presente che sto facendo alcune supposizioni sui nodi.

getNodes(0, width - 1, 0, height - 1).forEach(node -> { 
    getNodes(node.getX() - 1, node.getX() + 1, node.getY() - 1, node.getY() + 1) 
    .filter(neighbor -> !neighbor.equals(node)) 
    .forEach(neighbor -> node.registerObserverAtNeighbor(neighbor)); 
}); 

Creazione di un flusso di utilizzare il metodo:

private static Stream<Node> getNodes(int x1, int x2, int y1, int y2) { 
    return IntStream.rangeClosed(x1, x2) 
    .mapToObj(x -> (Stream<Node>)IntStream.rangeClosed(y1, y2).mapToObj(y -> getNode(x, y))) 
    .flatMap(nodes -> nodes) 
    .filter(node -> node != null); 
} 
1

In linea di principio, è possibile sostituire cicli annidati con un Stream utilizzando flatMap. Ciò richiede che tu scelga un tipo di elemento in grado di mantenere le informazioni equivalenti alle variabili del ciclo, se ne hai bisogno. Nel tuo caso, sono i due valori per x e . Semplificherebbe il codice se la tua classe Node contiene queste informazioni poiché puoi facilmente iterare sui nodi anziché sui valori int. Dal momento che non è stato specificato le capacità della vostra classe Node, ecco un esempio che utilizza un long[] di dimensioni due per contenere i punti:

IntStream.range(0, width).boxed() 
    .flatMap(x->IntStream.range(0, height).mapToObj(y->new int[]{ x, y })) 
    .forEach(p1 -> { 
    Consumer<Node> register=getNode(p1[0], p1[1])::registerObserverAtNeighbor; 
    IntStream.rangeClosed(p1[0]-1, p1[0]+1).boxed() 
    .flatMap(x->IntStream.rangeClosed(p1[1]-1, p1[1]+1).mapToObj(y->new int[]{ x,y })) 
     .filter(p2 -> (p1[0] != p2[0] || p1[1] != p2[1])) 
     .map(point -> getNode(point[0], point[1])) 
     .filter(node -> node != null) 
     .forEach(register); 
    }); 

Semplifica ancora il codice interno spostando il codice verso l'esterno, in cui possibile, ad es le chiamate getNode. È inoltre possibile semplificare il codice mettendo il compito ripetuta di creazione di un Stream punti su di un'area in un metodo:

static Stream<int[]> area(int x0, int x1, int y0, int y1) { 
    return IntStream.range(x0, x1).boxed() 
    .flatMap(x->IntStream.range(y0, y1).mapToObj(y->new int[]{ x, y })); 
} 

quindi è possibile utilizzare in questo modo:

area(0, width, 0, height).forEach(p1 -> { 
    Consumer<Node> register=getNode(p1[0], p1[1])::registerObserverAtNeighbor; 
    area(p1[0]-1, p1[0]+2, p1[1]-1, p1[1]+2) 
     .filter(p2 -> (p1[0] != p2[0] || p1[1] != p2[1])) 
     .map(point -> getNode(point[0], point[1])) 
     .filter(node -> node != null) 
     .forEach(register); 
    }); 

E ancora potrebbe essere più facile se si ha/usa una classe di punti dedicata o se la classe di nodo contiene le informazioni sui punti (e ha un metodo di confronto per essa, nel migliore dei casi).