Używanie do block vs braces {}


112

Nowy w rubinie, załóż rękawiczki dla początkujących.

Czy jest jakaś różnica (niejasna lub praktyczna) między następującymi dwoma fragmentami?

my_array = [:uno, :dos, :tres]
my_array.each { |item| 
    puts item
}

my_array = [:uno, :dos, :tres]
my_array.each do |item| 
    puts item
end

Zdaję sobie sprawę, że składnia nawiasów klamrowych pozwoliłaby umieścić blok w jednej linii

my_array.each { |item| puts item }

ale poza tym, czy są jakieś istotne powody, by używać jednej składni zamiast drugiej?


5
Świetne pytanie. Interesuje mnie również to, co wolą doświadczeni rubiniści.
— Jonathan Sterling




1
Możesz także napisać jedną linijkę bloku do, chociaż jest to naprawdę przydatne tylko wtedy, gdy robisz coś takiego jak eval ("my_array.each do | item |; puts item; end"), ale działa w irb lub pry bez znaków eval i cudzysłowu. może natknąć się na sytuację, w której jest to preferowane. Nie pytaj mnie jednak kiedy. To kolejny temat do zbadania.
— Douglas G. Allen

Odpowiedzi:


101

Książka kucharska Ruby mówi, że składnia nawiasów ma wyższy priorytet niżdo..end

Należy pamiętać, że składnia nawiasów ma wyższy priorytet niż składnia do..end. Rozważ następujące dwa fragmenty kodu:

1.upto 3 do |x|
  puts x
end

1.upto 3 { |x| puts x }
# SyntaxError: compile error

Drugi przykład działa tylko wtedy, gdy używany jest nawias, 1.upto(3) { |x| puts x }


8
Ach, rozumiem. Tak więc ze względu na kolejność pierwszeństwa, kiedy używasz do, przekazujesz blok jako dodatkowy parametr, ale kiedy używasz nawiasów, przekazujesz blok jako pierwszy parametr wyników wywołania metody (-ów) do lewo.
— Alan Storm

2
Często wolę krótkie odpowiedzi, ale odpowiedź bkdir jest dużo jaśniejsza.
— yakout

71

To trochę stare pytanie, ale chciałbym spróbować wyjaśnić nieco więcej na temat {}ido .. end

jak zostało powiedziane wcześniej

składnia nawiasów ma wyższy priorytet niż do..end

ale jak to robi różnicę:

method1 method2 do
  puts "hi"
end

w tym przypadku metoda1 zostanie wywołana z blokiem, do..enda metoda2 zostanie przekazana do metody method1 jako argument! co jest równoważne zmethod1(method2){ puts "hi" }

ale jeśli powiesz

method1 method2{
  puts "hi"
}

wtedy metoda2 zostanie wywołana z blokiem, a następnie zwrócona wartość zostanie przekazana do metody method1 jako argument. Co jest równoważnemethod1(method2 do puts "hi" end)

def method1(var)
    puts "inside method1"
    puts "method1 arg = #{var}"
    if block_given?
        puts "Block passed to method1"
        yield "method1 block is running"
    else
        puts "No block passed to method1"
    end
end

def method2
    puts"inside method2"
    if block_given?
        puts "Block passed to method2"
        return yield("method2 block is running")
    else
        puts "no block passed to method2"
        return "method2 returned without block"
    end
end

#### test ####

method1 method2 do 
    |x| puts x
end

method1 method2{ 
    |x| puts x
}

#### wynik ####

#inside method2
#no block passed to method2
#inside method1
#method1 arg = method2 returned without block
#Block passed to method1
#method1 block is running

#inside method2
#Block passed to method2
#method2 block is running
#inside method1
#method1 arg = 
#No block passed to method1

39

Ogólnie rzecz biorąc, konwencja jest używana, {}gdy wykonujesz małą operację, na przykład wywołanie metody lub porównanie itp., Więc ma to sens:

some_collection.each { |element| puts element }

Ale jeśli masz nieco złożoną logikę, która prowadzi do wielu linii, użyj do .. end:

1.upto(10) do |x|
  add_some_num = x + rand(10)
  puts '*' * add_some_num
end

Zasadniczo sprowadza się to do tego, że jeśli twoja logika blokowa przechodzi do wielu linii i nie można jej dopasować do tej samej linii, użyj, do .. enda jeśli twoja logika blokowa jest prosta i tylko prosta / pojedyncza linia kodu, użyj {}.


6
Zgadzam się z używaniem do / end dla bloków wieloliniowych, ale pójdę z nawiasami klamrowymi, jeśli połączę dodatkowe metody na końcu bloku. Stylistycznie podoba mi się metoda {...}. Method (). Method () zamiast metody do ... end.method ()., Ale to może być tylko ja.
— Tin Man,

1
Zgoda, chociaż wolę przypisać wynik metody z blokiem do znaczącej zmiennej, a następnie wywołać inną metodę, result_with_some_condition = method{|c| c.do_something || whateever}; result_with_some_condition.another_methodponieważ dzięki temu jest to trochę łatwiejsze do zrozumienia. Ale generalnie unikałbym katastrofy pociągu.
— nas

Zastanawiam się, dlaczego ten styl jest tak popularny. Czy jest jakiś inny powód niż „Zawsze robiliśmy to w ten sposób”?
— iGEL,

9

Istnieją dwa popularne style wybierania do endi { }blokowania w Rubim:

Pierwszy i bardzo popularny styl został spopularyzowany przez Ruby on Rails i opiera się na prostej zasadzie pojedynczej i wieloliniowej:

  • Użyj nawiasów klamrowych { }dla bloków jednowierszowych
  • Służy do enddo bloków wieloliniowych

Ma to sens, ponieważ do / end czyta źle w jednowierszowym, ale w przypadku bloków wieloliniowych, pozostawienie zamknięcia }wiszącego w osobnym wierszu jest niespójne ze wszystkim innym, co używa endw ruby, takim jak definicje modułów, klas i metod ( defitp. zwalczania.) i ( if, while, caseetc.)

Drugi, rzadziej spotykany styl znany jest jako semantyczny lub „ Weirich Braces ”, zaproponowany przez nieżyjącego już, wielkiego rubinisty Jima Weiricha:

  • Używaj do enddo bloków proceduralnych
  • Użyj szelek { }do bloków funkcjonalnych

Oznacza to, że gdy blok jest oceniany pod kątem wartości zwracanej , powinien być możliwy do utworzenia łańcucha, a {}nawiasy klamrowe mają większy sens w przypadku łączenia metod.

Z drugiej strony, gdy blok jest oceniany pod kątem skutków ubocznych , zwracana wartość nie ma żadnego znaczenia, a blok po prostu coś „robi”, więc nie ma sensu być łączonym w łańcuch.

To rozróżnienie w składni przekazuje wizualne znaczenie oceny bloku i tego, czy należy dbać o jego zwracaną wartość.

Na przykład tutaj wartość zwracana bloku jest stosowana do każdego elementu:

items.map { |i| i.upcase }

Jednak tutaj nie używa wartości zwracanej przez blok. To działa proceduralnie, a robi to efekt uboczny z nim:

items.each do |item|
  puts item
end

Kolejną zaletą stylu semantycznego jest to, że nie trzeba zmieniać nawiasów klamrowych na wykonanie / zakończenie tylko dlatego, że do bloku dodano wiersz.

Z obserwacji wynika, że ​​przypadkowo bloki funkcjonalne są często jednowierszowe, a bloki proceduralne (np. Config) są wieloliniowe. Więc podążanie za stylem Weiricha wygląda prawie tak samo jak styl Rails.


1

Używałem stylu Weirich przez lata, ale po prostu odszedłem od tego, aby zawsze używać aparatu ortodontycznego . Nie pamiętam, żebym kiedykolwiek używał informacji ze stylu bloku, a definicja jest trochę niejasna. Na przykład:

date = Timecop.freeze(1.year.ago) { format_date(Time.now) }
customer = Timecop.freeze(1.year.ago) { create(:customer) }

Czy są one prokudowe czy funkcjonalne?

A liczenie linii jest moim zdaniem bezużyteczne. Wiem, czy jest 1 lub więcej linii i dlaczego dokładnie powinienem zmienić styl tylko dlatego, że dodałem lub usunąłem linie?

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.