Menggunakan do block vs braces {}

112

Baru mengenal ruby, kenakan sarung tangan pemula Anda.

Apakah ada perbedaan (tidak jelas atau praktis) antara dua cuplikan berikut?

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

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

Saya menyadari sintaks brace akan memungkinkan Anda menempatkan blok pada satu baris

my_array.each { |item| puts item }

tetapi di luar itu, adakah alasan kuat untuk menggunakan satu sintaks di atas sintaks yang lain?

Alan Storm
sumber
5
Pertanyaan bagus. Saya juga tertarik dengan apa yang disukai penganut rubi berpengalaman.
Jonathan Sterling
3
Duplikat: stackoverflow.com/questions/533008
Mike Woodhouse
2
kemungkinan duplikat stackoverflow.com/questions/533008/…
John Topley
1
Anda juga dapat menulis satu baris blok do meskipun itu benar-benar hanya berguna ketika melakukan sesuatu seperti eval ("my_array.each do | item |; put item; end") tetapi berfungsi di irb atau pry tanpa eval dan tanda kutip Anda mungkin menemukan situasi saat itu lebih disukai. Tapi jangan tanya saya kapan. Itu topik lain untuk diselidiki.
Douglas G. Allen

Jawaban:

101

Buku masak Ruby mengatakan sintaks braket memiliki urutan prioritas yang lebih tinggi daripadado..end

Perlu diingat bahwa sintaks braket memiliki prioritas yang lebih tinggi daripada sintaks do..end. Pertimbangkan dua cuplikan kode berikut:

1.upto 3 do |x|
  puts x
end

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

Contoh kedua hanya berfungsi jika tanda kurung digunakan, 1.upto(3) { |x| puts x }

KAMU
sumber
8
Ah, mengerti. Jadi karena urutan prioritas, ketika Anda menggunakan do, Anda meneruskan blok sebagai parameter tambahan, tetapi ketika Anda menggunakan tanda kurung Anda meneruskan blok sebagai parameter pertama dari hasil pemanggilan metode ke kiri.
Alan Storm
2
Saya sering lebih suka jawaban pendek tetapi jawaban bkdir jauh lebih jelas.
yakout
71

Ini adalah pertanyaan yang agak lama tetapi saya ingin mencoba menjelaskan lebih banyak tentang {}dando .. end

seperti yang dikatakan sebelumnya

sintaks braket memiliki urutan prioritas lebih tinggi daripada do..end

tapi bagaimana yang satu ini membuat perbedaan:

method1 method2 do
  puts "hi"
end

dalam hal ini, metode1 akan dipanggil dengan blok do..enddan metode2 akan diteruskan ke metode1 sebagai argumen! yang setara denganmethod1(method2){ puts "hi" }

tetapi jika Anda mengatakan

method1 method2{
  puts "hi"
}

kemudian metode2 akan dipanggil dengan blok tersebut kemudian nilai yang dikembalikan akan diteruskan ke metode1 sebagai argumen. Yang mana setara denganmethod1(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
}

#### keluaran ####

#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
bkdir
sumber
39

Umumnya, konvensi digunakan {}saat Anda melakukan operasi kecil, misalnya, panggilan metode atau perbandingan, dll. Jadi ini sangat masuk akal:

some_collection.each { |element| puts element }

Tetapi jika Anda memiliki logika yang sedikit rumit yang mengarah ke beberapa baris, gunakan do .. endseperti:

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

Pada dasarnya, itu turun ke, jika logika blok Anda pergi ke beberapa baris dan tidak dapat dipasang pada baris yang sama maka gunakan do .. enddan jika logika blok Anda sederhana dan hanya satu baris kode sederhana kemudian gunakan {}.

nas
sumber
6
Saya setuju dengan menggunakan do / end untuk blok multi-baris, tetapi saya akan menggunakan tanda kurung jika saya merangkai metode tambahan ke akhir blok. Secara gaya saya suka {...}. Method (). Method () over do ... end.method (). Method, tapi itu mungkin saja saya.
Tin Man
1
Setuju, meskipun saya lebih suka menetapkan hasil metode dengan blok ke variabel yang bermakna dan kemudian memanggil metode lain di atasnya seperti result_with_some_condition = method{|c| c.do_something || whateever}; result_with_some_condition.another_methoditu hanya membuatnya sedikit lebih mudah dimengerti. Tetapi secara umum saya akan menghindari kecelakaan kereta api.
nas
Entah kenapa gaya ini begitu populer. Apakah ada alasan untuk itu selain "Kami selalu melakukannya dengan cara ini"?
iGEL
9

Ada dua gaya umum untuk memilih do endvs. { }untuk blok di Ruby:

Gaya pertama dan paling umum dipopulerkan oleh Ruby on Rails, dan didasarkan pada aturan sederhana antara satu baris vs. banyak baris:

  • Gunakan kawat gigi { }untuk blok garis tunggal
  • Gunakan do enduntuk blok multi-baris

Ini masuk akal karena do / end terbaca buruk dalam satu baris, tetapi untuk blok multi-baris, membiarkan penutup }tergantung pada barisnya sendiri tidak konsisten dengan semua hal lain yang digunakan enddi ruby, seperti definisi modul, kelas & metode ( defdll. .) dan struktur kontrol ( if, while, case, dll)

Gaya kedua, yang lebih jarang terlihat dikenal sebagai semantik, atau " Weirich Braces ", yang diusulkan oleh almarhum, ahli ruby ​​hebat Jim Weirich:

  • Gunakan do enduntuk blok prosedural
  • Gunakan kawat gigi { }untuk blok fungsional

Ini berarti bahwa ketika blok dievaluasi untuk nilai kembaliannya , blok tersebut harus dapat dirantai, dan tanda {}kurung kurawal lebih masuk akal untuk rangkaian metode.

Di sisi lain, ketika blok dievaluasi untuk efek sampingnya , maka nilai kembali tidak ada konsekuensinya, dan blok hanya "melakukan" sesuatu, jadi tidak masuk akal untuk dirantai.

Perbedaan dalam sintaks ini menyampaikan makna visual tentang evaluasi blok, dan apakah Anda harus peduli dengan nilai kembaliannya atau tidak.

Misalnya, di sini nilai kembali dari blok tersebut diterapkan ke setiap item:

items.map { |i| i.upcase }

Namun, di sini tidak menggunakan nilai kembali blok tersebut. Ini beroperasi secara prosedural, dan melakukan efek samping dengannya:

items.each do |item|
  puts item
end

Manfaat lain dari gaya semantik adalah Anda tidak perlu mengganti tanda kurung untuk melakukan / mengakhiri hanya karena garis telah ditambahkan ke blok.

Sebagai pengamatan, blok yang berfungsi secara kebetulan sering kali merupakan satu baris, dan blok prosedural (misalnya config) adalah multi-baris. Jadi, mengikuti gaya Weirich akhirnya terlihat hampir sama dengan gaya Rails.

Andrew Vit
sumber
1

Saya menggunakan gaya Weirich selama bertahun-tahun, tetapi baru saja beralih dari ini untuk selalu menggunakan kawat gigi . Saya tidak ingat pernah menggunakan info dari gaya blok, dan definisinya agak kabur. Sebagai contoh:

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

Apakah ini prokudual atau fungsional?

Dan hal jumlah baris tidak berguna menurut saya. Saya tahu, apakah ada 1 atau lebih garis, dan mengapa tepatnya saya harus mengubah gaya hanya karena saya telah menambahkan atau menghapus garis?

iGEL
sumber