Khi nào tôi nên sử dụng Struct so với OpenStabase?


184

Nói chung, những lợi thế và bất lợi của việc sử dụng OpenSturation so với Struct là gì? Loại trường hợp sử dụng chung nào sẽ phù hợp với từng trường hợp này?


1
Tôi có một vài nhận xét về Struct so với OpenStabase so với Hash trong bình luận blog gần đây của tôi "Structs Inside out" , trong trường hợp ai đó quan tâm.
Robert Klemme

Thông tin liên quan đến tốc độ của Hash, Struct và OpenSturation đã lỗi thời. Xem stackoverflow.com/a/43987844/128421 để biết điểm chuẩn gần đây hơn.
Người đàn ông Tin

Câu trả lời:


172

Với một OpenStruct, bạn có thể tùy ý tạo thuộc tính. Một Struct, mặt khác, phải có thuộc tính của nó được xác định khi bạn tạo ra nó. Việc lựa chọn cái này hơn cái kia nên chủ yếu dựa vào việc bạn có cần thêm thuộc tính sau này hay không.

Cách nghĩ về chúng là nền tảng giữa của quang phổ giữa một bên và các lớp ở bên kia. Chúng ngụ ý một mối quan hệ cụ thể giữa các dữ liệu hơn là một Hash, nhưng chúng không có các phương thức cá thể như một lớp. Một loạt các tùy chọn cho một chức năng, ví dụ, có ý nghĩa trong hàm băm; chúng chỉ liên quan một cách lỏng lẻo. Tên, email và số điện thoại cần thiết của một chức năng có thể được đóng gói cùng nhau trong một Structhoặc OpenStruct. Nếu tên, email và số điện thoại đó cần các phương thức để cung cấp tên ở cả hai định dạng "Đầu tiên cuối cùng" và "Cuối cùng, Đầu tiên", thì bạn nên tạo một lớp để xử lý nó.


49
"nhưng họ không có các phương thức ví dụ như một lớp". tốt, có một mô hình khá phổ biến để sử dụng nó như một "lớp bình thường":class Point < Struct.new(:x, :y); methods here; end
tokland

10
@tokland như ngày nay, cách tiếp cận "ưa thích" của việc tùy chỉnh cấu trúc với các phương thức là chuyển khối cho hàm tạo Point = Struct.new(:x, :y) { methods here }. ( nguồn ) Tất nhiên, { ... }có thể được viết dưới dạng một khối nhiều dòng ( do ... end) và, tôi nghĩ, đó là cách ưa thích.
Ivan Kolmychek

1
@IvanKolmychek: Thật tuyệt, thực sự tôi thích cách tiếp cận khối.
tokland

@tokland tốt. Tôi chỉ muốn làm rõ rằng bây giờ có một cách tiếp cận đẹp hơn, vì nhận xét của bạn được bình chọn rất cao, vì vậy, những người mới sử dụng ruby ​​thực sự có thể nghĩ "OK, vậy đó là cách nó nên được thực hiện", vì mọi người đều đồng ý với điều đó, đúng ? " :)
Ivan Kolmychek

4
Một câu hỏi: một khi bạn đến vào thời điểm bạn muốn thêm các phương thức vào struct của mình, tại sao không sử dụng một lớp?
jaydel

82

Điểm chuẩn khác:

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

Đối với những người thiếu kiên nhẫn muốn có ý tưởng về kết quả điểm chuẩn, mà không tự chạy chúng, đây là đầu ra của mã ở trên (trên 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
với ruby ​​2,14, sự khác biệt nhỏ hơn 0,94-0,97 với OpenSturation so với 0,02-0,03 với Ostruct (MB Pro 2.2Ghz i7)
basex

1
OpenSturation có tốc độ tương đương với việc sử dụng Struct. Xem stackoverflow.com/a/43987844/128421 .
Tin Man

57

CẬP NHẬT:

Kể từ Ruby 2.4.1 OpenSturation và Struct gần hơn về tốc độ. Xem https://stackoverflow.com/a/43987844/128421

TRƯỚC

Để hoàn thiện: Struct vs. Class vs. Hash so với OpenSturation

Chạy mã tương tự như burtlo, trên Ruby 1.9.2, (1 trong 4 lõi x86_64, RAM 8GB) [bảng được chỉnh sửa để căn chỉnh các cột]:

tạo 1 Mio Structs: 1,43 giây, 219 MB / 90MB (virt / res)
tạo các phiên bản 1 Mio Class: 1.43 giây, 219 MB / 90MB (virt / res)
tạo 1 Mio Băm: 4,46 giây, 493 MB / 364MB (virt / res)
tạo 1 Mio OpenStructs: 415,13 giây, 2464 MB / 2.3GB (virt / res) # ~ 100 lần chậm hơn so với Băm
tạo 100K OpenStructs: 10,96 giây, 369 MB / 242MB (virt / res)

OpenStructs là sloooooowbộ nhớ chuyên sâu , và không mở rộng tốt cho các tập dữ liệu lớn

Tạo 1 Mio OpenStructs chậm hơn ~ 100 lần so với tạo 1 Mio Hashing .

start = Time.now

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

stop = Time.now

puts "#{stop - start} seconds elapsed"

Thông tin rất hữu ích cho những người nghiện hiệu suất như tôi. Cảm ơn.
Bernardo Oliveira

Tôi đang đề cập đến việc triển khai Ruby (MRI) của Matz
Tilo

1
Xin chào @Tilo, bạn có thể chia sẻ mã của mình để nhận được kết quả ở trên không? Tôi muốn sử dụng nó để so sánh Struct & OSturation với Hashie :: Mash. Cảm ơn.
Donny Kurnia

1
Xin chào @Donny, tôi vừa xem upvote và nhận ra rằng điều này đã được đo vào năm 2011 - Tôi cần chạy lại nó với Ruby 2.1: P không chắc tôi có mã đó không, nhưng nó rất đơn giản để sao chép. Tôi sẽ cố gắng khắc phục sớm.
Tilo

2
Kể từ Ruby 2.4.1 OpenSturation và Struct gần hơn về tốc độ. Xem stackoverflow.com/a/43987844/128421
Tin Man

34

Các trường hợp sử dụng cho hai là khá khác nhau.

Bạn có thể nghĩ về lớp Struct trong Ruby 1.9 tương đương với structkhai báo trong C. Trong Ruby Struct.newlấy một tập hợp các tên trường làm đối số và trả về một Class mới. Tương tự, trong C, một structkhai báo lấy một tập các trường và cho phép lập trình viên sử dụng loại phức tạp mới giống như bất kỳ loại tích hợp nào.

Ruby:

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

C:

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

newtype n;

Lớp OpenSturation có thể được so sánh với một khai báo cấu trúc ẩn danh trong C. Nó cho phép lập trình viên tạo một thể hiện của một kiểu phức tạp.

Ruby:

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

C:

struct {
  int data1;
  char data2;
} o;

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

Dưới đây là một số trường hợp sử dụng phổ biến.

OpenStructs có thể được sử dụng để dễ dàng chuyển đổi băm thành các đối tượng một lần đáp ứng tất cả các khóa băm.

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

Cấu trúc có thể hữu ích cho các định nghĩa lớp tốc ký.

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

m = MyClass.new
m.a = 1

3
Đây là một câu trả lời tuyệt vời về sự khác biệt về khái niệm giữa chúng. Cảm ơn bạn đã chỉ ra tính ẩn danh của OpenSturation, tôi cảm thấy như thế làm cho nó rõ ràng hơn rất nhiều.
bryant

Giải thích tuyệt vời!
Yuri Ghensev

24

OpenStructs sử dụng nhiều bộ nhớ hơn và hoạt động chậm hơn so với Structs.

require 'ostruct' 

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

Trên hệ thống của tôi, đoạn mã sau được thực thi trong 14 giây và tiêu tốn 1,5 GB bộ nhớ. Số dặm của bạn có thể thay đổi:

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

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

Điều đó đã hoàn thành gần như ngay lập tức và tiêu thụ 26,6 MB bộ nhớ.


3
Nhưng bạn có biết rằng thử nghiệm OpenSturation tạo ra rất nhiều băm tạm thời. Tôi đề nghị một điểm chuẩn được sửa đổi một chút - vẫn hỗ trợ cho phán quyết của bạn (xem bên dưới).
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

Cảm ơn ví dụ. Nó giúp rất nhiều để hiểu trong thực tế.
Ahsan

5

Hãy xem API liên quan đến phương pháp mới. Rất nhiều sự khác biệt có thể được tìm thấy ở đó.

Cá nhân, tôi khá thích OpenSturation, vì tôi không phải xác định cấu trúc của đối tượng trước và chỉ cần thêm công cụ theo ý muốn. Tôi đoán đó sẽ là lợi thế chính (dis) của nó?


3

Sử dụng mã @Robert, tôi thêm Hashie :: Mash vào mục chuẩn và nhận được kết quả này:

                           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)

Điểm chuẩn của bạn thực sự lạ. Tôi đã nhận được kết quả sau với ruby2.1.1 trên máy i5 mac: gist.github.com/nicolas-besnard/ Kẻ
cappie013 17/12/14

Chà, kết quả sẽ khác nhau giữa phiên bản ruby ​​được sử dụng và phần cứng được sử dụng để chạy nó. Nhưng mô hình vẫn vậy, OpenSturation là chậm nhất, Struct là nhanh nhất. Hashie rơi vào giữa.
Donny Kurnia

0

Không thực sự là một câu trả lời cho câu hỏi, nhưng một xem xét rất quan trọng nếu bạn quan tâm đến hiệu suất . Xin lưu ý rằng mỗi khi bạn tạo một OpenStructthao tác sẽ xóa bộ đệm phương thức, có nghĩa là ứng dụng của bạn sẽ hoạt động chậm hơn. Sự chậm chạp hay không OpenStructkhông chỉ là về cách thức hoạt động của nó, mà là những hàm ý mà việc sử dụng chúng mang lại cho toàn bộ ứng dụng: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.