Ruby'de bloklar için kıvırcık diş telleri vs yapın


155

Beni aktif olarak kullanmam gerektiğine ikna etmeye çalışan bir iş arkadaşım var .. end ve bunun yerine Ruby'deki çok satırlı blokları tanımlamak için kıvırcık parantez kullanın.

Ben sıkıca sadece kısa bir gömlekleri için kıvırcık parantez kullanma ve yapmak her şey için ..end kamp. Ama biraz çözüm almak için büyük topluma ulaşacağımı düşündüm.

Peki hangisi ve neden? (Bazı shoulda kodu örneği)

context do
  setup { do_some_setup() }
  should "do somthing" do
    # some more code...
  end
end

veya

context {
  setup { do_some_setup() }
  should("do somthing") {
    # some more code...
  }
}

Şahsen, sadece yukarıdakilere bakmak benim için soruyu cevaplıyor, ancak bunu daha büyük topluluğa açmak istedim.


3
Sadece merak ediyorum ama iş arkadaşlarınızın parantez kullanma argümanları neler? Mantıksal bir şeyden ziyade kişisel bir tercih gibi görünüyor.
Daniel Lee

6
Bir tartışma yapmak istiyorsanız bunu bir topluluk wiki'si yapın. Sorunuza: Bu sadece kişisel bir tarz. Kıvırcık parantezleri daha asosyal göründüklerinden tercih ederim.
halfdan

7
Tercih edilen bir şey değildir, stilistik nedenlere ek olarak birini diğerinin üzerinde kullanmanın sözdizimsel nedenleri vardır. Cevapların birçoğu nedenini açıklıyor. Doğru olanı kullanmamak, yöntemler için hiçbir zaman sarma parantezini kullanmak için başka bir "üslup" seçeneğinin yapılıp yapılmadığını bulmak çok zor olan çok küçük hatalara neden olabilir.
Tin Man

1
"Uç koşullar" kötü bir alışkanlığa sahip ve onlar hakkında bilmeyen insanlara gizlice girme alışkanlığı var. Defansif kodlama, vakalara hiç girme şansını en aza indiren kodlama stillerini kullanmaya karar vermek de dahil olmak üzere birçok şey anlamına gelir. Siz farkında olabilirsiniz, ancak sizden sonraki iki kişi aşiret bilgisi unutulduktan sonra olmayabilir. Ne yazık ki şirket ortamlarında olma eğilimindedir.
Kalay Adam

1
@tinman Bu tür kenar koşulları TDD tarafından ele alınır. Bu yüzden Rubist böyle bir kod yazabilir ve kodlarında bu tür hataların olmadığını bilerek tam bir gece dinlenebilir. TDD veya BDD ile gelişirken ve bu tür bir öncelik önceliği yapılırken, ekranda gösterilen kırmızı bize “aşiret bilgisini” hatırlatan şeydir. Genellikle çözüm, standart kurallar dahilinde tamamen kabul edilebilir bir yere birkaç parens eklemektir. :)
Blake Taylor

Yanıtlar:


247

Genel kural çok satırlı bloklar için do..end ve tek satırlı bloklar için kıvırcık parantez kullanmaktır, ancak bu örnekle gösterilebilen ikisi arasında da bir fark vardır:

puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil

Bu, {} 'nin öncekinden daha yüksek bir önceliğe sahip olduğu anlamına gelir. Bu nedenle, ne kullanmak istediğinize karar verirken bunu aklınızda bulundurun.

Not: Tercihlerinizi geliştirirken aklınızda bulundurmanız gereken bir örnek daha var.

Aşağıdaki kod:

task :rake => pre_rake_task do
  something
end

gerçekten şu anlama gelir:

task(:rake => pre_rake_task){ something }

Ve bu kod:

task :rake => pre_rake_task {
  something
}

gerçekten şu anlama gelir:

task :rake => (pre_rake_task { something })

Bu nedenle, kıvırcık parantez ile istediğiniz gerçek tanımı elde etmek için şunları yapmanız gerekir:

task(:rake => pre_rake_task) {
  something
}

Belki de parametreler için parantez kullanmak yine de yapmak istediğiniz bir şeydir, ancak eğer yapmazsanız, bu karışıklığı önlemek için do..end kullanmak en iyisidir.


44
Bütün sayıyı ortadan kaldırmak için her zaman yöntem parametreleri etrafında parantez kullanmak için oy kullanıyorum.
Tin Man

@The Tin Man: Rake'de parantez mi kullanıyorsunuz?
Andrew Grimm

9
Benim için eski bir okul programlama alışkanlığı. Bir dil parantezi destekliyorsa, bunları kullanırım.
Tin Man

8
Öncelik sorununu belirtmek için +1. Bazen dönüştürmeyi denediğimde beklenmedik istisnalar alıyorum; {}
idrinkpabst

1
puts [1,2,3].map do |k| k+1; endNumaralandırıcıyı neden yalnızca bir şey yazdırıyor? Koyar yani burada iki argüman geçmedi mi [1,2,3].mapve do |k| k+1; end?
ben

55

Gönderen Ruby Programlama :

Diş telleri yüksek önceliğe sahiptir; düşük bir önceliğe sahiptir. Yöntem çağırma parantez içine alınmamış parametrelere sahipse, bir bloğun küme ayracı formu, genel çağırma değil son parametreye bağlanır. Do formu çağrışmaya bağlanacaktır.

Yani kod

f param {do_something()}

paramKodu değiştirirken bloğu değişkene bağlar

f param do do_something() end

Bloğu işleve bağlar f.

Ancak, işlev bağımsız değişkenlerini parantez içine alırsanız bu bir sorun değildir.


7
+1 "Ancak, işlev bağımsız değişkenlerini parantez içine alırsanız bu bir sorun değildir." Bunu şans eseri ve tercümanın kaprisine güvenmek yerine alışkanlık meselesi olarak yapıyorum. :-)
Tin Man

4
@theTinMan, tercümanınız ayrıştırıcısında şans kullanıyorsa, yeni bir tercümana ihtiyacınız vardır.
Paul Draper

@PaulDraper - gerçekten, ancak tüm diller bu konuda aynı fikirde değil. Ancak (sanırım) parens'in "işlerini" yapmamaları oldukça nadirdir, bu nedenle parens kullanmak evrensel olarak sizi dil tasarımcıları tarafından verilen "rastgele" kararları hatırlamaktan kurtarır - derleyiciler ve tercümanlar uygulayıcıları tarafından sadakatle yürütülse bile .
dlu

15

Bu konuda birkaç bakış açısı var, bu gerçekten kişisel bir tercih meselesi. Birçok rubyist yaptığınız yaklaşımı benimser. Ancak, yaygın olan diğer iki stil her zaman birini veya diğerini kullanmak veya {}değer döndüren do ... endbloklar için ve yan etkiler için yürütülen bloklar için kullanmaktır.


2
Bu sadece kişisel tercih meselesi değil. Metod parametrelerinin parantez içinde olmaması durumunda parametrelerin bağlanması ince hatalara neden olabilir.
Tin Man

1
Şu anda son seçeneği yapmıyorum, ancak bazı insanların bu yaklaşımı benimsediğinden bahsettiğim için + 1-ing.
Andrew Grimm

Avdi Grimm bunu bir blog yazısında savunuyor: devblog.avdi.org/2011/07/26/…
alxndr

8

Kıvırcık parantezlerin büyük bir yararı var - birçok editör onları eşleştirmek için çok daha kolay bir zamana sahip, bu da bazı hata ayıklama türlerini çok daha kolay hale getiriyor. Bu arada, "do ... end" anahtar kelimesinin eşleşmesi biraz daha zordur, özellikle "end" ler de "if" ile eşleştiğinden.


Örneğin RubyMine, do ... endanahtar kelimeleri varsayılan olarak vurgulayın
ck3g

1
Diş telleri tercih ederken, bir editörün tek bir karaktere göre 2/3 karakter dizesi bulmakta sorun yaşaması için hiçbir neden göremiyorum. Çoğu editör zaten parantez ile eşleşiyor argümanı olmasına rağmen do ... end, dile özgü mantık gerektiren özel bir durum.
Chinoto Vokro


7

Yap / bitir için oy kullanıyorum


Sözleşme çok do .. endsatırlı ve{ ... } tek gömleklidir.

Ama do .. enddaha çok hoşuma gidiyor , bu yüzden bir astarım olduğunda, do .. endyine de kullanıyorum, ancak üç satırda do / end için her zamanki gibi biçimlendiriyorum. Bu herkesi mutlu ediyor.

  10.times do 
    puts ...
  end

İle ilgili bir sorun { } şiir modu-düşman (çünkü tüm parametre çağrısı değil, son parametreye sıkıca bağlar, bu yüzden yöntem parens dahil etmelisiniz) ve sadece, bence, güzel görünmüyor. İfade grupları değildirler ve okunabilirlik için karma sabitlerle çakışırlar.

Ayrıca, { }C programlarında yeterince gördüm . Ruby'nin yolu, her zamanki gibi daha iyidir. Tam olarak bir tür ifblok vardır ve asla geri dönüp bir ifadeyi bir bileşik ifadesine dönüştürmeniz gerekmez.


2
"C programlarında {} yeterince gördüm" ... ve Perl. Ve orada birkaç ifade için ve Ruby'nin zen benzeri kodlama stillerini savunmak için +1. :-)
Tin Man

1
Açıkçası, başka bir "if" sözdizimi - postfix "if" ( do_stuff if x==1) vardır, bu da normal sözdizimine dönüştürmek için can sıkıcı bir durumdur. Ama bu başka bir tartışma.
Kelvin

Önek if, postfix if, postfix unless(ayrıca bir tür, ifdeğilse) ve bir satır ifvardır then. Ruby yolu, aslında her zaman aynı olan, C'nin sunduğundan çok daha basit ve katı kurallara göre daha kötü bir şeyi ifade etmenin birçok yoluna sahip olmaktır.
Mecki

2
Yani C'deki {} 'i seversek, bu onları Ruby'de de kullanmamız gerektiği anlamına mı geliyor?
Warren Dew

6

Birkaç etkili rubyist, dönüş değerini kullandığınızda parantez kullanmanızı ve kullanmadığınızda yapmayı / bitirmenizi önerir.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace (archive.org )

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (archive.org )

Bu genel olarak iyi bir uygulama gibi görünüyor.

Tek bir satırda do / end kullanmaktan kaçınmanız gerektiğini söylemek için bu prensibi biraz değiştirirdim çünkü okumak daha zor.

Diş telleri kullanırken daha dikkatli olmalısınız, çünkü tüm yöntem çağrısı yerine bir yöntemin son parametresine bağlanır. Bundan kaçınmak için parantez ekleyin.


2

Aslında bu kişisel bir tercih, ama yakut deneyimlerimin son 3 yılı boyunca öğrendiğim şeyin yakutun tarzı olduğunu söyledikten sonra.

Bir örnek, bir JAVA arka planından geliyorsanız, kullanabileceğiniz bir boole yöntemi için

def isExpired
  #some code
end 

deve durumuna dikkat edin ve boolean yöntemi olarak tanımlamak için çoğunlukla 'is' önekidir.

Ama yakut dünyasında, aynı yöntem

def expired?
  #code
end

bu yüzden kişisel olarak 'yakut yolu' ile gitmek daha iyi olur (Ama birinin anlaması biraz zaman alır (beni 1 yıl civarında aldı D)).

Sonunda,

do 
  #code
end

blokları.


2

Her ne kadar büyük fark zaten belirtilmiş olsa da (cevap / bağlama) ve bu sorun bulmakta zorlanmaya neden olabilir (Tin Man ve diğerleri bunu işaret etti). Bence benim örneğim o kadar alışılmadık bir kod pasajı ile sorunu gösteriyor, hatta deneyimli programcılar pazar günleri gibi okumuyor:

module I18n
    extend Module.new {
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    }
end

module InplaceTrans
    extend Module.new {
        def translate(old_translate, *args)
            Translator.new.translate(old_translate, *args)
        end
    }
end

Sonra bazı kod güzelleştirici yaptım ...

#this code is wrong!
#just made it 'better looking'
module I18n
    extend Module.new do
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end
end

{}burada değiştirirseniz do/endhatayı alırsınız, bu yöntemtranslate mevcut değildir ...

Bunun neden burada birden fazla - öncelik olduğu belirtiliyor. Ama buraya parantez koymak için nereye? (@Teneke Adam: Her zaman senin gibi parantez kullanırım, ama burada ... denetleniyor)

yani her cevap şöyle

If it's a multi-line block, use do/end
If it's a single line block, use {}

sadece yanlış olmadan kullanılırsa "AMA parantezi / önceliği bir göz atın!"

tekrar:

extend Module.new {} evolves to extend(Module.new {})

ve

extend Module.new do/end evolves to extend(Module.new) do/end

(uzatmanın sonucu blokla ne yaparsa ...)

Bu yüzden do / end komutunu kullanmak istiyorsanız bunu kullanın:

#this code is ok!
#just made it 'better looking'?
module I18n
    extend(Module.new do 
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end)
end

1

Kişisel önyargıya inerken, arka plan dillerinin çoğunun do / end konvansiyonunda kullanması nedeniyle daha fazla sayıda geliştirici için daha anlaşılır olduğu için bir do / end bloğu üzerinde kıvırcık parantezleri tercih ederim. Bununla birlikte, gerçek anahtar mağazanızda bir anlaşmaya varmaktır, eğer do / end 6/10 geliştiriciler tarafından kullanılıyorsa, HERKES bunları kullanmalıdır, 6/10 kıvırcık parantez kullanıyorsa, o paradigmaya sadık kalın.

Bu, bir bütün olarak takımın kod yapılarını daha hızlı tanımlayabilmesi için bir kalıp yapmakla ilgilidir.


3
Tercihten daha fazlasıdır. İkisi arasındaki bağlanma farklıdır ve teşhis edilmesi zor sorunlara neden olabilir. Cevapların bir kısmı sorunu detaylandırıyor.
Tin Man

4
Diğer dillerden gelen insanlar açısından - bence "anlaşılabilirlik" in farkı bu durumda dikkate alınmayacak kadar küçüktür. (Bu benim downvote btw değildi.)
Kelvin

1

Aralarında ince bir fark var, ancak {} yap / sonlandırmadan daha sıkı bağlanıyor.


0

Benim kişisel tarzım, böyle bir seçim mümkün olduğunda {... }vs do... katı kurallar üzerinde okunabilirliği vurgulamaktır end. Okunabilirlik fikrim şu şekildedir:

[ 1, 2, 3 ].map { |e| e + 1 }      # preferred
[ 1, 2, 3 ].map do |e| e + 1 end   # acceptable

[ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence
[ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable

Foo = Module.new do     # preferred for a multiline block, other things being equal
  include Comparable
end

Foo = Module.new {      # less preferred
  include Comparable
}

Foo = Module.new { include Comparable }      # preferred for a oneliner
Foo = module.new do include Comparable end   # imo less readable for a oneliner

[ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end }  # slightly better
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } }     # slightly worse

Çok satırlı iç içe bloklar gibi daha karmaşık sözdiziminde, en doğal sonuç için örneğin {... }ve do... endsınırlayıcılarını ayırmaya çalışırım .

Foo = Module.new { 
  if true then
    Bar = Module.new {                          # I intersperse {} and keyword delimiters
      def quux
        "quux".tap do |string|                  # I choose not to intersperse here, because
          puts "(#{string.size} characters)"    # for multiline tap, do ... end in this
        end                                     # case still loks more readable to me.
      end
    }
  end
}

Katı kuralların eksikliği, farklı programcılar için çok farklı seçenekler üretebilmesine rağmen, sübjektif olmasına rağmen, okunabilirlik için duruma göre optimizasyonun katı kurallara bağlılık üzerinde net bir kazanç olduğuna inanıyorum.


1
Python geliyor, belki Ruby atlamak ve bunun yerine Wisp öğrenmek gerekir.
Cees Timmerman

RubyOrada en iyi pragmatik dil olduğuna inanıyordum . Senaryo dilini söylemeliyim. Bugün, öğrenme konusunda eşit derecede iyi olacaksınız Brainfuck.
Boris Stitnicky

0

Burada en çok oy alan cevaptan bir örnek aldım. Söyle,

[1,2,3].map do 
  something 
end

Array.rb dosyasında hangi dahili uygulamayı kontrol ederseniz, map yönteminin başlığı şunu söyler :

Invokes the given block once for each element of self.

def map(*several_variants)
    (yield to_enum.next).to_enum.to_a
end

yani bir kod bloğunu kabul eder - do ile end arasında ne olursa olsun verim olarak yürütülür. Sonra sonuç tekrar dizi olarak toplanacak, böylece tamamen yeni bir nesne geri dönecek.

Bu nedenle, bir do-end bloğu veya {} ile karşılaştığınızda , kod bloğunun bir parametre olarak iletildiğini ve bunun dahili olarak yürütüleceğini aklınızda bulundurun.


-3

Üçüncü bir seçenek daha var: Girintiden kendi satırına "son" çıkarmak için bir önişlemci yazın. Kısa kodu tercih eden derin düşünürler haklılar.

Daha da iyisi, Ruby'yi hackleyin, bu bir bayrak.

Tabii ki, "kavgalarınızı seçin" en basit çözüm, bir dizinin hepsinin aynı satırda göründüğü stil kuralını benimsemek ve sözdizimini renklendirmeyi öğretmektir. Düzenleme kolaylığı için, bu sekansları genişletmek / daraltmak için editör komut dosyaları kullanılabilir.

Yakut kod satırlarımın% 20 ila% 25'i kendi satırlarında "son", hepsi de girintili sözleşmeler tarafından önemsiz olarak çıkarıldı. Ruby, en büyük başarıyı elde eden lisp benzeri bir dildir. İnsanlar buna karşı çıktıklarında, tüm korkunç parantezlerin nerede olduğunu sorarlarsa, onlara yedi satırlık "son" satırla biten yakut bir işlev gösteririm.

Çoğu parantez çıkarmak için bir lisp ön işlemcisi kullanarak yıllarca kod yazdım: A bar '|' satırın sonunda otomatik olarak kapanmış bir grup açtı ve dolar işareti '$' boş bir yer tutucu olarak hizmet etti; burada gruplamaları çıkarmaya yardımcı olacak hiçbir sembol olmazdı. Bu elbette dini savaş bölgesi. Parantez olmadan Lisp / şeması tüm dillerin en şiiridir. Yine de, sözdizimi renklendirmesi kullanarak parantezleri sessizleştirmek daha kolaydır.

Hala Haskell için bir önişlemci ile kodlama, yorumlu metinler eklemek ve tüm floş satırları yorum olarak varsayılan olarak her şey kod olarak girintili. Dil ne olursa olsun, yorum karakterlerinden hoşlanmıyorum.

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.