2013-05-29 13 views
6

Niedawno natrafiłem na problem/rozwiązanie, które było używane w programie Loop Do. Rzadko widziałem to do tej pory w mojej nauce programowania Ruby (jestem początkującym bez doświadczenia w CS).Kiedy i dlaczego używam pętli do konstruowania w języku 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 

ROZWIĄZANIE

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) 

Czy ktoś mógłby mi wyjaśnić, kiedy jest najlepszy czas, aby użyć „pętli nie” budować zamiast zwykłego „a” lub „chyba” lub „każdy” skonstruować ?

Odpowiedz

10

w języku bez loop, ty może użyć while konstrukt jak:

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

punkt jest to, że po uruchomieniu pętli bez knowi ile iteracji należy wykonać (lub trudno ją obliczyć z góry), ale łatwo jest wykryć, kiedy pętla powinna się kończyć. Ponadto dla konkretnego przypadku możesz znaleźć równoważną składnię, ponieważ ukończony warunek może wystąpić w połowie pętli lub w wielu miejscach.

Ruby's loop to w zasadzie to samo, co while(true) - w rzeczywistości można z nim zamieniać się prawie zamiennie z while(true).

W podanym przykładzie, istnieją następujące punkty zwrotu w ciągu każdej iteracji:

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 

Istnieje również domniemanych „else kontynuować pętli” tutaj, jeśli są spełnione żadne warunki końcowe.

Te liczne możliwe punkty wyjścia są prawdopodobnie powodem, dla którego autor wybrał konstrukcję loop, chociaż w praktyce istnieje wiele sposobów rozwiązania tego problemu z Ruby. Podany kod rozwiązania niekoniecznie jest lepszy niż wszystkie inne możliwości.

+0

używasz tylko chwilę, gdy nie wiesz, kiedy to się skończy. – Woot4Moo

+0

@ Woot4Moo: Dobra uwaga. Trudno jest rozróżnić, dlaczego możesz wybrać pomiędzy 'while (condition) {}' and 'while (1) {if (warunek) break; } 'składnia w krótkiej odpowiedzi. –

+0

rzeczywiście :), lubię też konstruować 'until'. – Woot4Moo

2

Użycie konstrukcji loop do umożliwia przełamanie warunkowe.

na przykład:

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

co chcesz korzystać z tego, gdy wiesz, liczbę elementów, które będą przetwarzane, podobną do tej z for each pętli

+0

sposób z użyciem „aż” konstruktem być taka sama, jak? – JaTo

+0

@JamieS 'until' jest przeciwieństwem' while', o ile szukasz negacji zmiennej, do której odwołuje się until. To znaczy: while (true) vice until false – Woot4Moo

+0

Możesz przerwać z dowolnej pętli, a nie tylko 'loop do'. –

2

konstrukcja z "pętlą" wykona dany blok bez końca, dopóki kod wewnątrz bloku nie zostanie przerwany pod pewnymi warunkami.

Może być używany, gdy nie masz kolekcji do zapętlenia, miejsc, w których "każdy" i "dla" nie może działać.

różnica między „pętli” i czas /, aż jest to, że podczas gdy/aż będzie wykonać danego bloku, gdy pewien warunek jest spotkanie, gdzie podobnie jak w przypadku pętli nie jest warunek, aby rozpocząć, stan leży wewnątrz blok pętli.

dla lepszego zrozumienia przeczytaj dokument.

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

20

Dodawanie do poprzednich odpowiedzi,

W "pętla nie" skonstruować oferuje również czystsze składni podczas pracy z iteratorów zewnętrznych, np

Nie "pętla nie"

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

A teraz z "loop do" będzie to

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

Wyjątek jest dla Ciebie obsługiwany. To także pozwala na pętli dwóch zbiorów w tym samym czasie i jak tylko jeden z nich zabraknie elementów pętli wyjście z wdziękiem,

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

loop do 
    puts iterator.next 
    puts iterator_two.next 
end 

to wypisze: 1,1,2,2,3, 3,4,4,5,5,6.

Więcej informacji na jej temat na stronie: ruby-docs.org

Powiązane problemy