เมื่อใดที่ฉันควรใช้ Struct vs. OpenStruct


184

โดยทั่วไปแล้วข้อดีและข้อเสียของการใช้ OpenStruct เมื่อเปรียบเทียบกับโครงสร้างคืออะไร กรณีการใช้งานทั่วไปประเภทใดที่เหมาะสมกับแต่ละกรณี


1
ฉันมีข้อสังเกตบางประการเกี่ยวกับ Struct vs. OpenStruct vs. Hash ในบล็อกของฉันเมื่อเร็ว ๆ นี้คอมเม้นต์"Structs Inside Out"ในกรณีที่มีคนสนใจ
Robert Klemme

ข้อมูลเกี่ยวกับความเร็วของแฮชโครงสร้างและ OpenStruct ล้าสมัย ดูstackoverflow.com/a/43987844/128421สำหรับมาตรฐานล่าสุด
Tin Man

คำตอบ:


173

ด้วยOpenStruct, คุณสามารถสร้างแอททริบิวต์ได้เอง Structบนมืออื่น ๆ ที่ต้องมีแอตทริบิวต์ที่กำหนดไว้เมื่อคุณสร้างมันขึ้นมา ตัวเลือกของตัวเลือกอื่น ๆ จะขึ้นอยู่กับว่าคุณจะต้องสามารถเพิ่มคุณสมบัติในภายหลังได้หรือไม่

วิธีที่จะคิดเกี่ยวกับพวกเขาคือเป็นจุดศูนย์กลางของสเปกตรัมระหว่างแฮชด้านหนึ่งและคลาสอื่น ๆ พวกเขาบอกเป็นนัยถึงความสัมพันธ์ที่เป็นรูปธรรมมากขึ้นในหมู่ข้อมูลมากกว่าที่จะทำ a Hashแต่พวกเขาไม่มีวิธีอินสแตนซ์เหมือนคลาส ตัวอย่างของตัวเลือกสำหรับฟังก์ชั่นตัวอย่างเช่นใช้แฮช พวกมันเกี่ยวข้องกันอย่างหลวม ๆ ชื่อ, อีเมล์และหมายเลขโทรศัพท์ที่จำเป็นโดยฟังก์ชั่นจะได้รับการบรรจุเข้าด้วยกันในหรือStruct OpenStructหากชื่ออีเมลและหมายเลขโทรศัพท์นั้นจำเป็นต้องใช้วิธีในการระบุชื่อในรูปแบบ "First Last" และ "Last, First" คุณควรสร้างคลาสเพื่อจัดการกับคลาสนั้น


49
"แต่พวกเขาไม่มีวิธีอินสแตนซ์เหมือนคลาส" มีรูปแบบที่ค่อนข้างธรรมดาที่จะใช้เป็น "คลาสปกติ":class Point < Struct.new(:x, :y); methods here; end
tokland

10
@tokland สำหรับวันนี้ "แนะนำ" Point = Struct.new(:x, :y) { methods here }วิธีการปรับแต่งโครงสร้างด้วยวิธีการคือการผ่านบล็อกเพื่อสร้าง (ที่มา ) แน่นอน { ... }ว่าสามารถเขียนเป็นบล็อกหลายบรรทัด ( do ... end) และฉันคิดว่านั่นเป็นวิธีที่ต้องการ
Ivan Kolmychek

1
@IvanKolmychek: เจ๋งจริง ๆ แล้วฉันชอบวิธีการบล็อก
tokland

@tokland ดี ฉันแค่ต้องการชี้แจงว่าขณะนี้มีวิธีการที่ดีกว่าเพราะความคิดเห็นของคุณได้รับการโหวตอย่างสูงดังนั้นผู้คนใหม่ ๆ สำหรับทับทิมจึงสามารถคิดได้ว่า "ตกลงดังนั้นนั่นเป็นวิธีที่ควรทำ" ทำให้ทุกคนเห็นด้วย ?" :)
Ivan Kolmychek

4
คำถาม: เมื่อคุณมาถึงในช่วงเวลาที่คุณต้องการเพิ่มวิธีการใน struct ของคุณทำไมไม่ใช้เรียน?
jaydel

82

มาตรฐานอื่น ๆ :

require 'benchmark'
require 'ostruct'

REP = 100000

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.bm 20 do |x|
  x.report 'OpenStruct slow' do
    REP.times do |index|
       OpenStruct.new(:name => "User", :age => 21)
    end
  end

  x.report 'OpenStruct fast' do
    REP.times do |index|
       OpenStruct.new(HASH)
    end
  end

  x.report 'Struct slow' do
    REP.times do |index|
       User.new("User", 21)
    end
  end

  x.report 'Struct fast' do
    REP.times do |index|
       User.new(USER, AGE)
    end
  end
end

สำหรับคนใจร้อนที่ต้องการทราบผลการเปรียบเทียบโดยไม่ต้องเรียกใช้ตัวเองนี่คือผลลัพธ์ของโค้ดข้างต้น (บน MB Pro 2.4GHz i7)

                          user     system      total        real
OpenStruct slow       4.430000   0.250000   4.680000 (  4.683851)
OpenStruct fast       4.380000   0.270000   4.650000 (  4.649809)
Struct slow           0.090000   0.000000   0.090000 (  0.094136)
Struct fast           0.080000   0.000000   0.080000 (  0.078940)

5
ด้วย ruby ​​2.14 ความแตกต่างนั้นเล็กลง 0.94-0.97 กับ OpenStruct กับ 0.02-0.03 กับ Ostruct (MB Pro
2.2Ghz

1
OpenStruct เทียบเท่ากับความเร็วในการใช้งานโครงสร้าง ดูstackoverflow.com/a/43987844/128421
Tin Man

57

UPDATE:

ในฐานะของ Ruby 2.4.1 OpenStruct และ Struct นั้นใกล้เคียงกับความเร็วมาก ดูhttps://stackoverflow.com/a/43987844/128421

ก่อนหน้านี้:

เพื่อความสมบูรณ์: Struct vs. Class vs. Hash vs. OpenStruct

ใช้งานโค้ดที่คล้ายกันกับของ burtlo บน Ruby 1.9.2, (1 จาก 4 แกน x86_64, RAM 8GB) [แก้ไขตารางเพื่อจัดแนวคอลัมน์]:

สร้าง 1 โครงสร้าง Mio: 1.43 วินาที, 219 MB / 90MB (อาศัย / res)
สร้าง 1 Mio Class อินสแตนซ์: 1.43 วินาที, 219 MB / 90MB (virt / res)
สร้าง 1 Mio Hashes: 4.46 วินาที, 493 MB / 364MB (อาศัย / res)
สร้าง 1 Mio OpenStructs: 415.13 วินาที, 2464 MB / 2.3GB (virt / res) # ~ 100x ช้ากว่าแฮช
สร้าง OpenStructs 100K: 10.96 วินาที, 369 MB / 242MB (อาศัย / res)

OpenStructs มีsloooooowและหน่วยความจำมากและไม่ได้ระดับดีสำหรับชุดข้อมูลขนาดใหญ่

การสร้าง 1 Mio OpenStructs เป็น~ 100x ช้ากว่าการสร้าง 1 Mio Hashes

start = Time.now

collection = (1..10**6).collect do |i|
  {:name => "User" , :age => 21}
end; 1

stop = Time.now

puts "#{stop - start} seconds elapsed"

ข้อมูลที่เป็นประโยชน์มากสำหรับผู้ติดยาเสพติดเช่นฉัน ขอบคุณ
เบอร์นาร์โดโอลิเวียร่า

ฉันหมายถึงการนำ Matz ไปใช้ของ Ruby (MRI)
Tilo

1
สวัสดี @Tilo คุณสามารถแบ่งปันรหัสของคุณเพื่อรับผลลัพธ์ด้านบนได้หรือไม่ ฉันต้องการใช้มันเพื่อเปรียบเทียบ Struct & OStruct กับ Hashie :: Mash ขอบคุณ
Donny Kurnia

1
เฮ้ @ ดอนนี่ฉันเพิ่งเห็น upvote และรู้ว่านี่เป็นวัดในปี 2011 - ฉันจำเป็นต้องเปิดใช้งานใหม่ด้วย Ruby 2.1: P ไม่แน่ใจว่าฉันมีรหัสนั้นหรือไม่ แต่ควรจะทำซ้ำได้ง่าย ฉันจะพยายามแก้ไขในไม่ช้า
Tilo

2
ตั้งแต่ Ruby 2.4.1 OpenStruct และ Struct นั้นใกล้เคียงกับความเร็วมาก ดูstackoverflow.com/a/43987844/128421
Tin Man

34

กรณีการใช้งานสำหรับทั้งสองมีความแตกต่างกันมาก

คุณสามารถคิดถึงคลาส Struct ใน Ruby 1.9 ได้เทียบเท่ากับการstructประกาศใน C ใน Ruby Struct.newจะใช้ชื่อชุดของฟิลด์เป็นอาร์กิวเมนต์และส่งคืนคลาสใหม่ ในทำนองเดียวกันใน C การstructประกาศใช้ชุดของเขตข้อมูลและอนุญาตให้โปรแกรมเมอร์ใช้ชนิดที่ซับซ้อนใหม่เช่นเดียวกับที่เขาต้องการชนิดในตัวใด ๆ

ทับทิม:

Newtype = Struct.new(:data1, :data2)
n = Newtype.new

ค:

typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;

คลาส OpenStruct สามารถเปรียบเทียบกับการประกาศโครงสร้างแบบไม่ระบุชื่อใน C. ซึ่งอนุญาตให้โปรแกรมเมอร์สร้างอินสแตนซ์ของชนิดที่ซับซ้อน

ทับทิม:

o = OpenStruct.new(data1: 0, data2: 0) 
o.data1 = 1
o.data2 = 2

ค:

struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;

นี่คือบางกรณีการใช้งานทั่วไป

OpenStructs สามารถใช้เพื่อแปลงแฮชให้เป็นวัตถุแบบครั้งเดียวซึ่งตอบสนองต่อคีย์แฮชทั้งหมดได้อย่างง่ายดาย

h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2

โครงสร้างจะมีประโยชน์สำหรับคำจำกัดความของชั้นเรียนชวเลข

class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1

3
นี่คือคำตอบที่ดีของความแตกต่างทางแนวคิดระหว่างพวกเขา ขอบคุณที่ชี้ให้เห็นว่าไม่เปิดเผยตัวตนของ OpenStruct ฉันรู้สึกว่ามันทำให้ชัดเจนมากขึ้น
bryant

คำอธิบายที่ดี!
Yuri Ghensev

24

OpenStructs ใช้หน่วยความจำมากขึ้นอย่างมากและช้ากว่านักแสดงเมื่อเทียบกับโครงสร้าง

require 'ostruct' 

collection = (1..100000).collect do |index|
   OpenStruct.new(:name => "User", :age => 21)
end

ในระบบของฉันรหัสต่อไปนี้ดำเนินการใน 14 วินาทีและใช้หน่วยความจำ 1.5 GB ไมล์สะสมของคุณอาจแตกต่างกันไป:

User = Struct.new(:name, :age)

collection = (1..100000).collect do |index|
   User.new("User",21)
end

ซึ่งเกือบจะทันทีและใช้หน่วยความจำ 26.6 MB


3
แต่คุณทราบว่าการทดสอบ OpenStruct สร้างแฮชชั่วคราวจำนวนมาก ฉันขอแนะนำเกณฑ์มาตรฐานที่ปรับเปลี่ยนเล็กน้อย - ซึ่งยังคงสนับสนุนการตัดสินของคุณ (ดูด้านล่าง)
Robert Klemme

6

Struct:

>> s = Struct.new(:a, :b).new(1, 2)
=> #<struct a=1, b=2>
>> s.a
=> 1
>> s.b
=> 2
>> s.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>

OpenStruct:

>> require 'ostruct'
=> true
>> os = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
>> os.a
=> 1
>> os.b
=> 2
>> os.c
=> nil

ขอบคุณสำหรับตัวอย่าง ช่วยได้มากในการทำความเข้าใจในทางปฏิบัติ
Ahsan

5

ดูที่ API เกี่ยวกับวิธีการใหม่ ความแตกต่างมากมายสามารถพบได้ที่นั่น

โดยส่วนตัวแล้วฉันชอบ OpenStruct มากเพราะฉันไม่จำเป็นต้องกำหนดโครงสร้างของวัตถุไว้ก่อนและเพียงเพิ่มสิ่งต่าง ๆ ตามที่ฉันต้องการ ฉันเดาว่ามันจะเป็นข้อได้เปรียบหลัก (dis)?


3

ใช้รหัส @Robert ฉันเพิ่ม Hashie :: Mash ในรายการเบนช์มาร์กแล้วรับผลลัพธ์นี้:

                           user     system      total        real
Hashie::Mash slow      3.600000   0.000000   3.600000 (  3.755142)
Hashie::Mash fast      3.000000   0.000000   3.000000 (  3.318067)
OpenStruct slow       11.200000   0.010000  11.210000 ( 12.095004)
OpenStruct fast       10.900000   0.000000  10.900000 ( 12.669553)
Struct slow            0.370000   0.000000   0.370000 (  0.470550)
Struct fast            0.140000   0.000000   0.140000 (  0.145161)

มาตรฐานของคุณแปลกมาก ฉันได้รับผลลัพธ์ดังต่อไปนี้ด้วย ruby2.1.1 บน i5 mac: gist.github.com/nicolas-besnard/ ......
cappie013

ผลจะแตกต่างกันระหว่างรุ่นทับทิมที่ใช้กับฮาร์ดแวร์ที่ใช้ในการรัน แต่รูปแบบยังคงเหมือนเดิม OpenStruct ช้าที่สุด Struct เร็วที่สุด Hashie ตกอยู่ตรงกลาง
Donny Kurnia

0

ไม่จริงคำตอบของคำถาม แต่อย่างที่สำคัญการพิจารณาถ้าคุณดูแลเกี่ยวกับประสิทธิภาพ โปรดสังเกตว่าทุกครั้งที่คุณสร้างการOpenStructดำเนินการจะล้างแคชของเมธอดซึ่งหมายความว่าแอปพลิเคชันของคุณจะทำงานช้าลง ความช้าหรือไม่ได้OpenStructเป็นเพียงเกี่ยวกับการทำงานของตัวเองเท่านั้น แต่ความหมายที่ใช้พวกมันนำมาสู่แอปพลิเคชันทั้งหมด: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.