Kapan saya harus menggunakan Struct vs OpenStruct?
184
Secara umum, apa keuntungan dan kerugian menggunakan OpenStruct dibandingkan dengan Struct? Apa jenis kasus penggunaan umum yang cocok untuk masing-masing kasus ini?
Saya punya beberapa komentar tentang Struct vs OpenStruct vs Hash di komentar blog saya baru-baru ini "Structs inside out" , kalau-kalau ada yang tertarik.
Robert Klemme
Informasi mengenai kecepatan Hash, Struct dan OpenStruct sudah usang. Lihat stackoverflow.com/a/43987844/128421 untuk benchmark yang lebih baru.
the Tin Man
Jawaban:
173
Dengan OpenStruct, Anda dapat membuat atribut secara sewenang-wenang. A Struct, di sisi lain, harus memiliki atribut yang ditentukan ketika Anda membuatnya. Pilihan satu di atas yang lain harus didasarkan terutama pada apakah Anda harus dapat menambahkan atribut nanti.
Cara untuk berpikir tentang mereka adalah sebagai jalan tengah dari spektrum antara Hash di satu sisi dan kelas di sisi lain. Mereka menyiratkan hubungan yang lebih konkret antara data daripada Hash, tetapi mereka tidak memiliki metode instan seperti halnya kelas. Banyak opsi untuk suatu fungsi, misalnya, masuk akal dalam hash; mereka hanya terkait longgar. Nama, email, dan nomor telepon yang dibutuhkan oleh suatu fungsi dapat dikemas bersama dalam suatu Structatau OpenStruct. Jika nama, email, dan nomor telepon itu memerlukan metode untuk memberikan nama dalam format "First Last" dan "Last, First", maka Anda harus membuat kelas untuk menanganinya.
"Tetapi mereka tidak memiliki metode instance seperti halnya kelas". baik, ada pola yang cukup umum untuk menggunakannya sebagai "kelas normal":class Point < Struct.new(:x, :y); methods here; end
tokland
10
@tokland seperti untuk hari ini, pendekatan "pilihan" untuk mengkustomisasi struct dengan metode adalah meneruskan blok ke konstruktor Point = Struct.new(:x, :y) { methods here }. ( sumber ) Tentu saja, { ... }ada dapat ditulis sebagai blok multi-line ( do ... end) dan, saya pikir, itulah cara yang disukai.
Ivan Kolmychek
1
@IvanKolmychek: Keren, sebenarnya saya lebih suka pendekatan blok.
tokland
@tokland bagus. Saya hanya ingin mengklarifikasi bahwa sekarang ada pendekatan yang lebih baik, mengingat komentar Anda sangat banyak dipilih, jadi, orang yang baru mengenal ruby sebenarnya dapat berpikir "OK, jadi begitulah seharusnya dilakukan, karena semua orang setuju dengan itu, kan ? " :)
Ivan Kolmychek
4
Sebuah pertanyaan: begitu Anda tiba pada saat Anda ingin menambahkan metode ke struct Anda, mengapa tidak menggunakan kelas?
Untuk yang tidak sabar yang ingin mendapatkan gambaran tentang hasil benchmark, tanpa menjalankannya sendiri, berikut adalah output dari kode di atas (pada MB Pro 2.4GHz i7)
user system total real
OpenStruct slow 4.4300000.2500004.680000(4.683851)OpenStruct fast 4.3800000.2700004.650000(4.649809)Struct slow 0.0900000.0000000.090000(0.094136)Struct fast 0.0800000.0000000.080000(0.078940)
Informasi yang sangat berguna untuk pecandu kinerja seperti saya. Terima kasih.
Bernardo Oliveira
Saya mengacu pada implementasi Matz tentang Ruby (MRI)
Tilo
1
Hai @Tilo, bisakah Anda membagikan kode Anda untuk mendapatkan hasil di atas? Saya ingin menggunakannya untuk membandingkan Struct & OStruct dengan Hashie :: Mash. Terima kasih.
Donny Kurnia
1
Hei @ Donny, saya baru saja melihat upvote dan menyadari bahwa ini diukur pada tahun 2011 - Saya perlu menjalankannya kembali dengan Ruby 2.1: P tidak yakin apakah saya memiliki kode itu, tetapi seharusnya mudah untuk mereproduksi. Saya akan mencoba memperbaikinya segera.
Anda bisa menganggap kelas Struct di Ruby 1.9 sebagai setara dengan structdeklarasi di C. Di Ruby Struct.newmengambil seperangkat nama bidang sebagai argumen dan mengembalikan Kelas baru. Demikian pula, dalam C, structdeklarasi mengambil set bidang dan memungkinkan pemrogram untuk menggunakan tipe kompleks baru seperti yang ia lakukan pada tipe bawaan apa pun.
Rubi:
Newtype=Struct.new(:data1,:data2)
n =Newtype.new
C:
typedef struct {
int data1;
char data2;} newtype;
newtype n;
Kelas OpenStruct dapat dibandingkan dengan deklarasi struct anonim dalam C. Ini memungkinkan programmer untuk membuat turunan dari tipe yang kompleks.
Rubi:
o =OpenStruct.new(data1:0, data2:0)
o.data1 =1
o.data2 =2
Ini adalah jawaban yang bagus untuk perbedaan konseptual di antara mereka. Terima kasih telah menunjukkan anonimitas OpenStruct, saya merasa seperti itu membuatnya jauh lebih jelas.
bryant
Penjelasan hebat!
Yuri Ghensev
24
OpenStructs menggunakan lebih banyak memori dan berkinerja lebih lambat dibandingkan Structs.
Tetapi Anda sadar bahwa tes OpenStruct menciptakan banyak hash sementara. Saya sarankan patokan yang sedikit dimodifikasi - yang masih mendukung putusan Anda (lihat di bawah).
Terima kasih untuk contohnya. Sangat membantu untuk memahami dalam praktek.
Ahsan
5
Lihat API terkait metode baru. Banyak perbedaan dapat ditemukan di sana.
Secara pribadi, saya sangat menyukai OpenStruct, karena saya tidak harus mendefinisikan struktur objek sebelumnya, dan hanya menambahkan hal-hal yang saya inginkan. Saya kira itu akan menjadi keuntungan (dis) utama?
Menggunakan kode @Robert, saya menambahkan Hashie :: Mash ke item patokan dan mendapatkan hasil ini:
user system total real
Hashie::Mash slow 3.6000000.0000003.600000(3.755142)Hashie::Mash fast 3.0000000.0000003.000000(3.318067)OpenStruct slow 11.2000000.01000011.210000(12.095004)OpenStruct fast 10.9000000.00000010.900000(12.669553)Struct slow 0.3700000.0000000.370000(0.470550)Struct fast 0.1400000.0000000.140000(0.145161)
Nah, hasilnya akan bervariasi antara versi ruby yang digunakan, dan perangkat keras yang digunakan untuk menjalankannya. Tetapi polanya masih sama, OpenStruct adalah yang paling lambat, Struct adalah yang tercepat. Hashie jatuh di tengah.
Donny Kurnia
0
Sebenarnya bukan jawaban untuk pertanyaan, tetapi pertimbangan yang sangat penting jika Anda peduli dengan kinerja . Harap perhatikan bahwa setiap kali Anda membuat OpenStructoperasi menghapus cache metode, yang berarti aplikasi Anda akan berjalan lebih lambat. Kelambatan atau tidaknya OpenStructbukan hanya tentang cara kerjanya dengan sendirinya, tetapi implikasi yang menggunakan mereka membawa ke seluruh aplikasi: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs
Jawaban:
Dengan
OpenStruct
, Anda dapat membuat atribut secara sewenang-wenang. AStruct
, di sisi lain, harus memiliki atribut yang ditentukan ketika Anda membuatnya. Pilihan satu di atas yang lain harus didasarkan terutama pada apakah Anda harus dapat menambahkan atribut nanti.Cara untuk berpikir tentang mereka adalah sebagai jalan tengah dari spektrum antara Hash di satu sisi dan kelas di sisi lain. Mereka menyiratkan hubungan yang lebih konkret antara data daripada
Hash
, tetapi mereka tidak memiliki metode instan seperti halnya kelas. Banyak opsi untuk suatu fungsi, misalnya, masuk akal dalam hash; mereka hanya terkait longgar. Nama, email, dan nomor telepon yang dibutuhkan oleh suatu fungsi dapat dikemas bersama dalam suatuStruct
atauOpenStruct
. Jika nama, email, dan nomor telepon itu memerlukan metode untuk memberikan nama dalam format "First Last" dan "Last, First", maka Anda harus membuat kelas untuk menanganinya.sumber
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( sumber ) Tentu saja,{ ... }
ada dapat ditulis sebagai blok multi-line (do ... end
) dan, saya pikir, itulah cara yang disukai.Tolok ukur lainnya:
Untuk yang tidak sabar yang ingin mendapatkan gambaran tentang hasil benchmark, tanpa menjalankannya sendiri, berikut adalah output dari kode di atas (pada MB Pro 2.4GHz i7)
sumber
MEMPERBARUI:
Pada Ruby 2.4.1 OpenStruct dan Struct lebih cepat dalam hal kecepatan. Lihat https://stackoverflow.com/a/43987844/128421
SEBELUMNYA:
Untuk kelengkapan: Struct vs. Class vs Hash vs OpenStruct
Menjalankan kode yang sama seperti burtlo's, di Ruby 1.9.2, (1 dari 4 core x86_64, 8GB RAM) [tabel diedit untuk menyelaraskan kolom]:
OpenStructs adalah sloooooow dan memori intensif , dan tidak skala dengan baik untuk set data besar
Membuat 1 Mio OpenStructs adalah ~ 100x lebih lambat daripada membuat 1 Mio Hash .
sumber
Kasus penggunaan untuk keduanya sangat berbeda.
Anda bisa menganggap kelas Struct di Ruby 1.9 sebagai setara dengan
struct
deklarasi di C. Di RubyStruct.new
mengambil seperangkat nama bidang sebagai argumen dan mengembalikan Kelas baru. Demikian pula, dalam C,struct
deklarasi mengambil set bidang dan memungkinkan pemrogram untuk menggunakan tipe kompleks baru seperti yang ia lakukan pada tipe bawaan apa pun.Rubi:
C:
Kelas OpenStruct dapat dibandingkan dengan deklarasi struct anonim dalam C. Ini memungkinkan programmer untuk membuat turunan dari tipe yang kompleks.
Rubi:
C:
Berikut adalah beberapa kasus penggunaan umum.
OpenStructs dapat digunakan untuk dengan mudah mengkonversi hash menjadi objek satu kali yang merespons semua kunci hash.
Struct dapat berguna untuk definisi kelas steno.
sumber
OpenStructs menggunakan lebih banyak memori dan berkinerja lebih lambat dibandingkan Structs.
Pada sistem saya, kode berikut dijalankan dalam 14 detik dan menghabiskan memori 1,5 GB. Jarak tempuh Anda mungkin bervariasi:
Itu selesai hampir secara instan dan menghabiskan 26,6 MB memori.
sumber
Struct
:OpenStruct
:sumber
Lihat API terkait metode baru. Banyak perbedaan dapat ditemukan di sana.
Secara pribadi, saya sangat menyukai OpenStruct, karena saya tidak harus mendefinisikan struktur objek sebelumnya, dan hanya menambahkan hal-hal yang saya inginkan. Saya kira itu akan menjadi keuntungan (dis) utama?
sumber
Menggunakan kode @Robert, saya menambahkan Hashie :: Mash ke item patokan dan mendapatkan hasil ini:
sumber
Sebenarnya bukan jawaban untuk pertanyaan, tetapi pertimbangan yang sangat penting jika Anda peduli dengan kinerja . Harap perhatikan bahwa setiap kali Anda membuat
OpenStruct
operasi menghapus cache metode, yang berarti aplikasi Anda akan berjalan lebih lambat. Kelambatan atau tidaknyaOpenStruct
bukan hanya tentang cara kerjanya dengan sendirinya, tetapi implikasi yang menggunakan mereka membawa ke seluruh aplikasi: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructssumber