"Untuk" vs "masing-masing" di Ruby


200

Saya hanya punya pertanyaan singkat tentang loop di Ruby. Apakah ada perbedaan antara dua cara iterasi melalui koleksi?

# way 1
@collection.each do |item|
  # do whatever
end

# way 2
for item in @collection
  # do whatever
end

Hanya ingin tahu apakah ini persis sama atau jika mungkin ada perbedaan yang halus (mungkin kapan @collectionnihil).

Jawaban:


315

Inilah satu-satunya perbedaan:

setiap:

irb> [1,2,3].each { |x| }
  => [1, 2, 3]
irb> x
NameError: undefined local variable or method `x' for main:Object
    from (irb):2
    from :0

untuk:

irb> for x in [1,2,3]; end
  => [1, 2, 3]
irb> x
  => 3

Dengan forloop, variabel iterator masih hidup setelah blok selesai. Dengan eachloop, tidak, kecuali jika sudah didefinisikan sebagai variabel lokal sebelum loop dimulai.

Selain itu, forhanya sintaksis gula untuk eachmetode ini.

Ketika @collectionadalah nilkedua loop melemparkan sebuah pengecualian:

Pengecualian: variabel atau metode lokal yang tidak ditentukan `@ koleksi 'untuk utama: Objek


3
apakah ada alasan bagus mengapa x tetap ada di dalam case atau apakah desain yang buruk ini: P? Menurut saya ini agak tidak intuitif dibandingkan dengan kebanyakan bahasa lain.
cyc115

3
@ cyc115 Alasan mengapa xtetap ada dalam skenario for adalah karena fakta bahwa (secara umum) kata kunci tidak membuat cakupan baru. jika , kecuali , mulai , untuk , sementara , dll semua bekerja dengan ruang lingkup saat ini. #eachNamun menerima satu blok. Blok selalu menambahkan ruang lingkup mereka sendiri di atas ruang lingkup saat ini. Berarti mendeklarasikan variabel baru di blok (sehingga lingkup baru) tidak akan dapat diakses dari luar blok karena lingkup tambahan itu tidak tersedia di sana.
3limin4t0r


30

Contoh pertama Anda,

@collection.each do |item|
  # do whatever
end

lebih idiomatis . Meskipun Ruby mendukung konstruksi pengulangan seperti fordan while, sintaks blok umumnya lebih disukai.

Perbedaan halus lainnya adalah bahwa variabel apa pun yang Anda nyatakan dalam satu forloop akan tersedia di luar loop, sedangkan yang di dalam blok iterator secara pribadi bersifat pribadi.


whiledan untilbenar-benar memiliki beberapa kegunaan yang sangat konkret yang tidak dapat diganti dengan masing-masing seperti misalnya menghasilkan nilai unik atau untuk REPL.
maks


2

Sepertinya tidak ada perbedaan, forgunakan di eachbawahnya.

$ irb
>> for x in nil
>> puts x
>> end
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):1
>> nil.each {|x| puts x}
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):4

Seperti yang dikatakan Bayard, masing-masing lebih idiomatis. Itu menyembunyikan lebih banyak dari Anda dan tidak memerlukan fitur bahasa khusus. Komentar Per Telemakus

for .. in .. mengatur iterator di luar lingkup loop, jadi

for a in [1,2]
  puts a
end

daun adidefinisikan setelah loop selesai. Dimana eachtidak. Yang merupakan alasan lain yang mendukung penggunaan each, karena variabel temp tinggal periode yang lebih pendek.


1
Ada adalah perbedaan kecil (seperti yjerem, ChristopheD dan Bayard menyebutkan) tentang ruang lingkup variabel.
Telemakus

Salah, fortidak digunakan di eachbawahnya. Lihat jawaban lain.
akuhn

@akuhn Untuk klarifikasi lebih lanjut silakan lihat pertanyaan ini dan keduanya adalah jawaban yang sangat baik.
Sagar Pandya

2

Tidak pernah menggunakannya fordapat menyebabkan bug yang hampir tidak bisa dilacak.

Jangan tertipu, ini bukan tentang masalah kode gaya atau idiomatik. Implementasi Ruby formemiliki kelemahan serius dan tidak boleh digunakan.

Berikut adalah contoh di mana formemperkenalkan bug,

class Library
  def initialize
    @ary = []
  end
  def method_with_block(&block)
    @ary << block
  end
  def method_that_uses_these_blocks
    @ary.map(&:call)
  end
end

lib = Library.new

for n in %w{foo bar quz}
  lib.method_with_block { n }
end

puts lib.method_that_uses_these_blocks

Cetakan

quz
quz
quz

Menggunakan %w{foo bar quz}.each { |n| ... }cetakan

foo
bar
quz

Mengapa?

Dalam satu forlingkaran variabel ndidefinisikan sekali dan hanya dan kemudian bahwa satu definisi digunakan untuk semua iterasi. Oleh karena itu setiap blok merujuk ke yang sama nyang memiliki nilai quzpada saat loop berakhir. Bug!

Dalam satu eachlingkaran variabel baru ndidefinisikan untuk setiap iterasi, misalnya variabel di atas ndidefinisikan tiga kali terpisah. Oleh karena itu setiap blok merujuk pada yang terpisah ndengan nilai yang benar.


0

Sejauh yang saya tahu, menggunakan blok alih-alih struktur kontrol dalam bahasa lebih idiomatis.


0

Saya hanya ingin membuat poin spesifik tentang for in loop di Ruby. Ini mungkin tampak seperti sebuah konstruksi yang mirip dengan bahasa lain, tetapi sebenarnya itu adalah ekspresi seperti setiap konstruksi perulangan lainnya di Ruby. Bahkan, untuk dalam bekerja dengan objek Enumerable seperti setiap iterator.

Koleksi yang diteruskan ke dalam dapat berupa objek apa pun yang memiliki metode setiap iterator. Array dan hash mendefinisikan masing-masing metode, dan banyak objek Ruby lainnya juga. Loop for / in memanggil masing-masing metode objek yang ditentukan. Ketika iterator menghasilkan nilai, loop for memberikan setiap nilai (atau setiap set nilai) ke variabel yang ditentukan (atau variabel) dan kemudian mengeksekusi kode dalam tubuh.

Ini adalah contoh konyol, tetapi menggambarkan titik bahwa for in loop bekerja dengan objek APA PUN yang memiliki masing-masing metode, seperti halnya setiap iterator:

class Apple
  TYPES = %w(red green yellow)
  def each
    yield TYPES.pop until TYPES.empty?
  end
end

a = Apple.new
for i in a do
  puts i
end
yellow
green
red
=> nil

Dan sekarang setiap iterator:

a = Apple.new
a.each do |i|
  puts i
end
yellow
green
red
=> nil

Seperti yang Anda lihat, keduanya merespons setiap metode yang menghasilkan nilai kembali ke blok. Seperti yang dinyatakan oleh semua orang di sini, sudah pasti lebih baik menggunakan iterator masing-masing daripada untuk di dalam loop. Saya hanya ingin pulang ke titik bahwa tidak ada yang ajaib tentang untuk dalam lingkaran. Ini adalah ekspresi yang memanggil setiap metode koleksi dan kemudian meneruskannya ke blok kode. Oleh karena itu, ini adalah kasus yang sangat jarang Anda harus gunakan untuk masuk. Gunakan iterator masing-masing hampir selalu (dengan manfaat tambahan dari ruang lingkup blok).


0
(1..4).each { |i| 


  a = 9 if i==3

  puts a 


}
#nil
#nil
#9
#nil

for i in 1..4

  a = 9 if i==3

  puts a

end
#nil
#nil
#9
#9

Dalam loop 'untuk', variabel lokal masih hidup setelah setiap loop. Dalam loop 'setiap', variabel lokal me-refresh setelah setiap loop.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.