2014-09-12 9 views
7

Ho un programma con un ciclo while che ha diversi punti in cui determinate condizioni richiedono un'azione da eseguire e quindi il resto dell'iterazione da saltare.Terminare un'iterazione di un loop da una subroutine

Dato che questo sarà sempre lo stesso codice, ho voluto inserirlo in una subroutine, ma quando provo a usare 'next;' come ultima affermazione nel sottotitolo ricevo un avvertimento (Uscendo dalla subroutine tramite il prossimo a ...), anche se sembra funzionare come previsto.

cioè senza il sub:

while (#condition) { 

    ## do stuff 

    if (#condition to skip iteration) { 
     ## action 
     next; 
    } 

    ## do more stuff, with the above block repeated several times 
} 

sub con:

while (#condition) { 

    ## do stuff 

    &skip if (#condition to skip iteration); 

    ## do more stuff, with more calls to &skip 
} 

sub skip() { 
    ## action 
    next; 
} 

Il resto del codice nel blocco/sub è così breve che mettere tutto ma la successiva; l'enunciato in un sottotitolo praticamente sconfigge l'oggetto di usare una subroutine.

La mia domanda è:

  • sta usando 'prossimo;' in una subroutine in questo modo ok, o è pericoloso/scarsa pratica?
  • C'è un modo migliore per saltare il resto dell'iterazione da una subroutine?
  • Se è ok e non c'è un modo migliore, c'è un modo per sopprimere l'avviso?

risposta

4

L'istinto di avviso ha probabilmente ragione. Come dici tu, next non è il tipo di istruzione che si incorpora in una subroutine. Anche se non si preferisce, questo è quasi certamente meglio:

while (#condition) { 

    ## do stuff 

    &skip, next if (#condition to skip iteration); 

    ## do more stuff, with more calls to &skip 
} 

sub skip() { 
    ## action 
} 

(mi capita di avere un atteggiamento piuttosto rilassato verso le tecniche di programmazione non standard, soprattutto se non sono abusati Se si sente fortemente circa incorporare il next. in questo caso, quindi non discuterò con te, dal momento che è il tuo programma, tuttavia, dal momento che hai chiesto un parere, sarei d'accordo con il tuo istinto che incorporare lo next non è probabilmente una buona idea.)

+0

Perché utilizzare le parentesi tonde su sub skip? Non è necessario utilizzare prototipi o firme qui. Forse meno confuso per rimuoverli. – jmmeier

3

Avvertenze non avvertirebbero se questa è una buona pratica. Usa la tua funzione in modo che ritorni presto invece di next. È possibile verificare la presenza di risultato della funzione e decidere se passare alla prossima iterazione, o saltare incondizionatamente al prossimo,

use warnings; 

sub foo { 
    print ($_, "\n"); 
} 

for (1..10) { 
    foo(), next if $_ %2;; 
} 
6

Dal Learning Perl 6 ° edizione pagina 179 (nota)

probabilmente non una buona idea, ma è possibile utilizzare questi operatori di controllo loop all'interno di una subroutine per controllare un loop che si trova all'esterno della subroutine . Cioè, se una subroutine viene chiamata in un blocco di loop, e la subroutine viene eseguita l'ultima volta che non c'è un blocco di loop in esecuzione all'interno della subroutine, il flusso del controllo passerà subito dopo il blocco di loop nel codice principale. Questa possibilità di utilizzare il controllo di loop da all'interno di una subroutine può andare via in una versione futura di Perl, e non uno è probabile che manchi.

La soluzione potrebbe essere quello che gli altri già detto, o si può fare di prova supplementare nella sub, come

use strict; 
use warnings; 

while(<>) { 
    chomp; 
    maybeskip($_) && next if m/2/; #maybe skip if match 2 
    print "$_\n"; 
} 

sub maybeskip { 
    $_[0] !~ m/0/; #skip only if not match 0 
    # the sub retuns the result of the last executed expression 
    # if this is not wanted you should use explicit return $value; 
} 

per le

seq 25 | perl script 

stampe:

1 
3 
4 
5 
6 
7 
8 
9 
10 
11 
13 
14 
15 
16 
17 
18 
19 
20 

es saltati tutti uguagliati 2 ma non 20

8

È facile sopprimere l'avviso. Basta inserire la seguente riga sopra la next:

no warnings "exiting"; 

L'avvertimento è lì per un motivo - utilizzando next in un sub aiuto del genere che può essere fonte di confusione per la prossima persona che deve leggere il codice (che potrebbe essere voi tra 6 mesi!) perché next non si verifica in modo lessicale all'interno del blocco di loop. Se stai leggendo il blocco del ciclo, potresti non notare che next più in basso nel file; e se stai leggendo la definizione della subroutine, potresti non essere sicuro di cosa sia lo next. Hai bisogno di leggere entrambe le parti del codice insieme per dare un senso ad esso. Ciò rende più confuso l'utilizzo di next direttamente all'interno del ciclo.

Inoltre, limita la riutilizzabilità del sub skip() che hai appena definito. Vuoi riutilizzare quel sotto skip() all'interno di un altro ciclo? Faresti meglio a sperare che la logica di salto abbia ancora senso nel nuovo ciclo.

Se hai preso in considerazione tutto ciò e vuoi ancora andare avanti, basta disattivare l'avviso come ho mostrato sopra. Gli avvisi non sono errori, sono solo avvisi. Ecco perché gli avvertimenti sono chiamati "avvertimenti". Sono progettati per attirare la vostra attenzione su qualcosa potenzialmente problematico; non impedirti di fare qualcosa che hai deciso sia utile.

3

È possibile inserire parte/tutto della condizione di salto all'interno della subroutine restituendo truey a next e falsey per continuare a utilizzare l'operatore and.

while (#condition) { 

    ## do stuff 

    &skip and next if (#condition to skip iteration); 

    ## or like this 
    next if (#condition to skip iteration) and &skip; 

    ## or like this 
    (#condition to skip iteration) and &skip and next; 

    ## do more stuff, with more calls to &skip 
} 

sub skip() { 
    ## action 
    return $common_skip_condition; 
}