Do block ve kaşlı ayraçları kullanma {}


112

Ruby'de yeni, acemi eldivenlerinizi giyin.

Aşağıdaki iki kod parçacığı arasında herhangi bir fark (belirsiz veya pratik) var mı?

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

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

Ayraç sözdiziminin bloğu tek bir satıra yerleştirmenize izin vereceğini biliyorum.

my_array.each { |item| puts item }

ama bunun dışında bir sözdizimini diğerine tercih etmek için ikna edici nedenler var mı?


5
Harika soru. Tecrübeli yakutçuların neyi tercih ettiğiyle de ilgileniyorum.
Jonathan Sterling




1
Do bloğunun bir satırını da yazabilirsiniz, ancak gerçekten yalnızca eval ("my_array.each do | item |; puts item; end") gibi bir şey yaparken yararlıdır, ancak eval ve tırnak işaretleri olmadan irb veya gözetlemede çalışır. tercih edildiğinde bir durumla karşılaşabilir. Yine de ne zaman diye sorma. Bu araştırılması gereken başka bir konu.
Douglas G. Allen

Yanıtlar:


101

Ruby yemek kitabı , köşeli ayraç sözdiziminin şuna göre daha yüksek öncelik sırasına sahip olduğunu söylüyor:do..end

Parantez söz diziminin do..end sözdiziminden daha yüksek önceliğe sahip olduğunu unutmayın. Aşağıdaki iki kod parçacığını düşünün:

1.upto 3 do |x|
  puts x
end

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

İkinci örnek yalnızca parantez kullanıldığında işe yarar, 1.upto(3) { |x| puts x }


8
Ah, anladım. Dolayısıyla, öncelik sırasından dolayı, do kullandığınızda bloğu ek bir parametre olarak geçirirsiniz, ancak köşeli parantezleri kullandığınızda bloğu, yöntem çağrılarının sonuçlarının ilk parametresi olarak iletirsiniz. sol.
Alan Storm

2
Genellikle kısa cevapları tercih ederim ama bkdir'in cevabı çok daha net.
yakout

71

Bu biraz eski bir soru, ancak hakkında biraz daha açıklama yapmak istiyorum {}vedo .. end

daha önce söylendiği gibi

parantez sözdiziminin do..end'den daha yüksek öncelik sırası vardır

ama bu nasıl fark yaratır:

method1 method2 do
  puts "hi"
end

bu durumda, method1 bloğu ile çağrılacak do..endve method2 bir argüman olarak method1'e geçirilecektir! eşdeğer olanmethod1(method2){ puts "hi" }

ama eğer söylersen

method1 method2{
  puts "hi"
}

daha sonra method2 blokla çağrılır ve sonra döndürülen değer method1'e argüman olarak aktarılır. Eşdeğerimethod1(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
}

#### çıktı ####

#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

Genel olarak, kural, {}küçük bir işlem yaparken, örneğin bir yöntem çağrısı veya bir karşılaştırma vb.

some_collection.each { |element| puts element }

Ancak, birden çok satıra giden biraz karmaşık mantığınız varsa, aşağıdaki do .. endgibi kullanın :

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

Temel olarak, blok mantığınız birden fazla satıra giderse ve aynı satıra sığamazsa, o zaman kullanın do .. endve blok mantığınız basitse ve sadece basit / tek bir kod satırı ise o zaman kullanın {}.


6
Çok satırlı bloklar için do / end kullanımına katılıyorum, ancak bloğun sonuna ek yöntemler zincirliyorsam küme ayraçları kullanacağım. Biçimsel olarak {...}. Method (). Method () yerine do ... end.method (). Method'u seviyorum, ama bu sadece ben olabilirim.
The Tin Man

1
Kabul edildi, ancak bloğu olan bir yöntemin sonucunu anlamlı bir değişkene atamayı ve ardından başka bir yöntemi çağırmayı tercih etmeme rağmen, bu result_with_some_condition = method{|c| c.do_something || whateever}; result_with_some_condition.another_methodonu biraz daha kolay anlaşılır kılıyor. Ama genellikle bir tren enkazından kaçınırdım.
nas

Acaba bu tarz neden bu kadar popüler? "Biz hep böyle yaptık" dan başka bir sebebi var mı?
iGEL

9

Ruby'de blokları seçmek do endve { }bloklar için iki yaygın stil vardır :

İlk ve çok yaygın olan stil Ruby on Rails tarafından popüler hale getirildi ve tek ve çok satırlı basit bir kurala dayanıyordu:

  • { }Tek satırlık bloklar için parantez kullanın
  • do endÇok satırlı bloklar için kullanın

Bu mantıklıdır çünkü do / end tek satırda kötü okur, ancak çok satırlı bloklar }için kendi satırında asılı bir kapanış bırakmak end, modül, sınıf ve yöntem tanımları ( defvb .) Ruby'de kullanılan diğer her şeyle tutarsızdır. .) ve kontrol yapıları ( if, while, casevs.)

İkinci, daha az görülen tarz, son dönem büyük yakut yazarı Jim Weirich tarafından önerilen semantik veya " Weirich Braces " olarak bilinir :

  • do endProsedürel bloklar için kullanın
  • { }Fonksiyonel bloklar için parantez kullanın

Bu, blok dönüş değeri için değerlendirildiğinde , {}zincirlenebilir olması gerektiği ve parantezlerin yöntem zincirleme için daha anlamlı olduğu anlamına gelir .

Öte yandan, blok kendi için değerlendirildiğinde yan etkileri , o zaman dönüş değerinin bir önemi yoktur ve blok sadece bir şeyi "yapıyor", dolayısıyla zincirlenmenin bir anlamı yok.

Sözdizimindeki bu ayrım, bloğun değerlendirilmesi ve geri dönüş değerini önemsemeniz gerekip gerekmediği hakkında görsel bir anlam taşır.

Örneğin, burada bloğun dönüş değeri her öğeye uygulanır:

items.map { |i| i.upcase }

Ancak burada bloğun dönüş değerini kullanmıyor. Prosedürel olarak çalışıyor ve onunla bir yan etki yapıyor :

items.each do |item|
  puts item
end

Anlamsal stilin bir başka yararı da, bloğa bir satır eklendiği için parantezleri yapmak / sonlandırmak için değiştirmenize gerek olmamasıdır.

Bir gözlem olarak, tesadüfen işlevsel bloklar genellikle tek satırlıdır ve prosedür blokları (örneğin yapılandırma) çok satırlıdır. Böylece, Weirich stilini takip etmek, neredeyse Rails stili ile aynı görünmeye başlar.


1

Weirich stilini yıllardır kullandım, ancak bundan uzaklaşıp her zaman diş teli kullanmaya başladım . Blok stilindeki bilgiyi hiç kullandığımı hatırlamıyorum ve tanım biraz belirsiz. Örneğin:

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

Bunlar procudual mi yoksa işlevsel mi?

Ve benim görüşüme göre satır sayısı olayı işe yaramaz. 1 veya daha fazla satır olup olmadığını biliyorum ve sırf satır ekleyip çıkardığım için neden tam olarak stili değiştirmeliyim?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.