2013-05-29 11 views
6

Recentemente mi sono imbattuto in un problema/soluzione che utilizzava Loop Do. Raramente ho visto questo finora nel mio apprendimento di Ruby Programming (sono un principiante senza esperienza CS).Quando e perché utilizzare Loop Do Costruisci in Ruby

# Write a function, `nearest_larger(arr, i)` which takes an array and an 
# index. The function should return another index, `j`: this should 
# satisfy: 
# 
# (a) `arr[i] < arr[j]`, AND 
# (b) there is no `j2` closer to `i` than `j` where `arr[i] < arr[j]`. 
# 
# In case of ties (see example beow), choose the earliest (left-most) 
# of the two indices. If no number in `arr` is largr than `arr[i]`, 
# return `nil`. 
# 
# Difficulty: 2/5 

describe "#nearest_larger" do 
    it "handles a simple case to the right" do 
    nearest_larger([2,3,4,8], 2).should == 3 
    end 

    it "handles a simple case to the left" do 
    nearest_larger([2,8,4,3], 2).should == 1 
    end 

    it "treats any two larger numbers like a tie" do 
    nearest_larger([2,6,4,8], 2).should == 1 
    end 

    it "should choose the left case in a tie" do 
    nearest_larger([2,6,4,6], 2).should == 1 
    end 

    it "handles a case with an answer > 1 distance to the left" do 
    nearest_larger([8,2,4,3], 2).should == 0 
    end 

    it "handles a case with an answer > 1 distance to the right" do 
    nearest_larger([2,4,3,8], 1).should == 3 
    end 

    it "should return nil if no larger number is found" do 
    nearest_larger([2, 6, 4, 8], 3).should == nil 
    end 
end 

SOLUZIONE

def nearest_larger(arr, idx) 
    diff = 1 
    loop do 
    left = idx - diff 
    right = idx + diff 

    if (left >= 0) && (arr[left] > arr[idx]) 
     return left 
    elsif (right < arr.length) && (arr[right] > arr[idx]) 
     return right 
    elsif (left < 0) && (right >= arr.length) 
     return nil 
    end 

    diff += 1 
    end 
end 
nearest_larger([2,4,3,8], 1) 

Qualcuno può spiegare a me quando è il momento migliore per utilizzare un "loop do" costruire al posto del solito "mentre" o "meno" o "ogni" costruire ?

risposta

10

In una lingua senza loop, è potrebbe utilizzare un costrutto while come:

while(true) { 
    # Do stuff until you detect it is done 
    if (done) break; 
} 

il punto di questo è che si avvia il ciclo senza knowi ng quante iterazioni eseguire (o è difficile da calcolare in anticipo), ma è facile rilevare quando il ciclo dovrebbe terminare. Inoltre, per un caso particolare potresti trovare la sintassi equivalente while (! done) { # do stuff } maldestra, perché la condizione eseguita può avvenire a metà del ciclo o in più punti.

Ruby's loop è fondamentalmente la stessa cosa del while(true) - in effetti è possibile utilizzare while(true) quasi in modo intercambiabile con esso.

Nell'esempio dato, ci sono seguenti punti di ritorno all'interno di ogni iterazione:

if (left >= 0) && (arr[left] > arr[idx]) 
    return left # <-- HERE 
elsif (right < arr.length) && (arr[right] > arr[idx]) 
    return right # <-- HERE 
elsif (left < 0) && (right >= arr.length) 
    return nil # <-- HERE 
end 

C'è anche un implicito "se continuare loop" qui, se non sono soddisfatte le condizioni finali.

Questi possibili punti di uscita sono presumibilmente il motivo per cui l'autore ha scelto il costrutto loop, sebbene in pratica ci siano molti modi per risolvere questo problema con Ruby. Il codice della soluzione dato non è necessariamente superiore a tutte le altre possibilità.

+0

si usa solo un po 'quando non si sa quando finirà. – Woot4Moo

+0

@ Woot4Moo: buon punto. È difficile differenziare il motivo per cui si può scegliere tra 'while (condition) {}' e 'while (1) {if (condition) break; } 'sintassi in una risposta breve. –

+0

anzi :), mi piace anche qui il 'until' costrutto. – Woot4Moo

2

L'utilizzo del costrutto loop do consente di interrompere un condizionale.

per esempio:

i=0 
loop do 
    i+=1 
    print "#{i} " 
    break if i==10 
end 

Si consiglia di utilizzare questo quando si conosce il numero di elementi che verranno trattati, simile a quella del for each ciclo

+0

stesse usando la "fino a quando" costrutto essere la stessa di quella? – JaTo

+0

@JamieS 'until' è l'opposto di' while' nella misura in cui si sta cercando la negazione della variabile a cui fa riferimento fino a. Cioè: while (true) vizio fino a falso – Woot4Moo

+0

Puoi rompere da qualsiasi loop, non solo un 'loop do'. –

2

ciclo con il costrutto 'loop' eseguirà il blocco dato all'infinito finché il codice all'interno del blocco non si interrompe in determinate condizioni.

può essere utilizzato quando non si dispone di una raccolta su cui eseguire il loop, i posti dove "ciascuno" e "per" non possono funzionare.

la differenza tra 'ciclo' e mentre/finché è che mentre/finché sarà eseguire il blocco specificato quando certa condizione è incontrare, dove come in caso di ciclo v'è alcuna condizione di partenza, condizione risiede all'interno della blocco del ciclo.

per una migliore comprensione leggere doc.

http://www.ruby-doc.org/core-1.9.2/Kernel.html#method-i-loop

20

Sommando le risposte precedenti,

Il "ciclo do" costruire anche offre una sintassi più pulito quando si lavora con iteratori esterni, ad esempio

No "ciclo do"

my_iterator = (1..9).each 
begin 
    while(true) 
    puts my_iterator.next 
    end 
rescue StopIteration => e 
    puts e 
end 

E ora con "loop do" questo diventerebbe

my_iterator = (1..9).each 
loop do 
    puts my_iterator.next 
end 

E l'eccezione è gestita per voi. Esso consente inoltre di collegare attraverso due collezioni, allo stesso tempo e non appena uno di loro si esaurisce di elementi del ciclo esce con grazia,

iterator = (1..9).each 
iterator_two = (1..5).each 

loop do 
    puts iterator.next 
    puts iterator_two.next 
end 

Si stamperà: 1,1,2,2,3, 3,4,4,5,5,6.

Maggiori informazioni su di esso a: ruby-docs.org

Problemi correlati