Struct와 OpenStruct는 언제 사용해야합니까?


184

일반적으로 Struct와 비교하여 OpenStruct를 사용하는 경우의 장단점은 무엇입니까? 이들 각각에 적합한 일반적인 유스 케이스 유형은 무엇입니까?


1
최근 블로그 댓글 "Structs inside out" 에서 누군가 Struct vs. OpenStruct vs. Hash에 대해 언급 한 내용 이 있습니다.
Robert Klemme

해시, Struct 및 OpenStruct의 속도에 관한 정보가 오래되었습니다. 최신 벤치 마크는 stackoverflow.com/a/43987844/128421 을 참조하십시오 .
Tin Man

답변:


173

을 사용하면 OpenStruct임의로 속성을 만들 수 있습니다. ㅏStruct 당신이 그것을 만들 때, 다른 한편으로는, 그 속성이 정의되어 있어야합니다. 둘 중 하나의 선택은 기본적으로 나중에 속성을 추가 할 수 있어야하는지에 따라 결정되어야합니다.

그것들을 생각하는 방법은 한쪽의 해시와 다른 쪽의 클래스 사이의 스펙트럼의 중간 근거입니다. 그것들은 데이터보다 더 구체적인 관계를 암시 Hash하지만 클래스와 같은 인스턴스 메소드는 없습니다. 예를 들어 함수에 대한 많은 옵션은 해시에서 의미가 있습니다. 그들은 느슨하게 관련되어 있습니다. 기능에 필요한 이름, 이메일 및 전화 번호는 Struct또는로 함께 패키지 될 수 있습니다 OpenStruct. 해당 이름, 전자 메일 및 전화 번호에 "처음"형식과 "성, 이름"형식으로 이름을 제공하는 방법이 필요한 경우 처리 할 클래스를 만들어야합니다.


49
"하지만 클래스처럼 인스턴스 메소드가 없습니다". 글쎄요, 이것을 "정상적인 클래스"로 사용하는 일반적인 패턴이 있습니다 :class Point < Struct.new(:x, :y); methods here; end
tokland

10
@tokland 오늘은 메소드를 사용하여 구조체를 사용자 정의하는 "선호"접근 방식은 블록을 constructor에 전달하는 것 Point = Struct.new(:x, :y) { methods here }입니다. ( 소스 물론이) { ... }여러 줄 블록 (거기 쓸 수 있습니다 do ... end선호하는 방법)와, 내가 생각.
Ivan Kolmychek

1
@IvanKolmychek : 쿨, 실제로는 블록 접근법을 선호합니다.
tokland

@tokland 좋아. 귀하의 의견이 투표권을 얻었 기 때문에 루비를 처음 접하는 사람들은 실제로 "좋아요. 그래서 어떻게해야합니까? '라고 생각할 수 있습니다. ? " :)
Ivan Kolmychek

4
질문 : 일단 당신이 구조체에 메소드를 추가하고 싶은 순간에 도착하면 왜 클래스를 사용하지 않습니까?
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
루비 2.14에서 차이는 OpenStruct에서 0.94-0.97 vs Ostruct에서 0.02-0.03 (MB Pro 2.2Ghz i7)
basex

1
OpenStruct는 Struct를 사용하는 속도와 동일합니다. stackoverflow.com/a/43987844/128421을 참조하십시오 .
Tin Man

57

최신 정보:

Ruby 2.4.1부터 OpenStruct와 Struct는 속도가 훨씬 더 가깝습니다. 참조 https://stackoverflow.com/a/43987844/128421를

이전 :

완전성 : Struct vs. Class vs. Hash vs. OpenStruct

Ruby 1.9.2에서 burtlo와 유사한 코드 실행 (4 개의 코어 중 1 개 x86_64, 8GB RAM) [테이블을 정렬하여 열 정렬] :

1 Mio Structs 생성 : 1.43 초, 219MB / 90MB (virt / res)
1 개의 Mio 클래스 인스턴스 생성 : 1.43 초, 219MB / 90MB (virt / res)
1 개의 Mio Hashes 생성 : 4.46 초, 493MB / 364MB (virt / res)
Mio OpenStructs 1 개 생성 : 415.13 초, 2464MB / 2.3GB (virt / res) # ~ 100 배 느리다
100K OpenStructs 생성 : 10.96 초, 369MB / 242MB (virt / res)

OpenStructs는 빈약 하고 메모리 집약적 이며 대용량 데이터 세트에는 적합하지 않습니다.

1 Mio OpenStructs 생성은 1 Mio Hashes 생성 보다 ~ 100 배 느립니다 .

start = Time.now

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

stop = Time.now

puts "#{stop - start} seconds elapsed"

나 같은 성능 중독자에게 매우 유용한 정보. 감사.
Bernardo Oliveira

Matz의 Ruby (MRI) 구현을 언급하고 있습니다.
Tilo

1
@Tilo 안녕하세요, 위의 결과를 얻기 위해 코드를 공유 할 수 있습니까? Struct & OStruct와 Hashie :: Mash를 비교하는 데 사용하고 싶습니다. 감사.
Donny Kurnia

1
@Donny, 방금 upvote를보고 이것이 2011 년에 측정되었다는 것을 깨달았습니다. Ruby 2.1을 사용하여 이것을 다시 실행해야합니다. 곧 고칠 것입니다.
Tilo

2
Ruby 2.4.1부터 OpenStruct와 Struct는 속도가 훨씬 더 가깝습니다. 참조 stackoverflow.com/a/43987844/128421
양철 남자

34

두 가지 사용 사례는 상당히 다릅니다.

Ruby 1.9의 Struct 클래스는 structC 의 선언 과 동일하다고 생각할 수 있습니다 . 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

Structs는 속기 클래스 정의에 유용 할 수 있습니다.

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

m = MyClass.new
m.a = 1

3
이것은 그들 사이의 개념적 차이에 대한 큰 대답입니다. OpenStruct의 익명 성을 지적 해 주셔서 감사합니다. 더 명확 해졌습니다.
bryant

좋은 설명!
Yuri Ghensev

24

OpenStructs는 훨씬 더 많은 메모리를 사용하며 Structs보다 성능이 느립니다.

require 'ostruct' 

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

내 시스템에서 다음 코드는 14 초 안에 실행되었고 1.5GB의 메모리를 소비했습니다. 마일리지가 다를 수 있습니다 :

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

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

거의 즉시 완료되었고 26.6MB의 메모리를 사용했습니다.


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를 매우 좋아합니다. 나는 그것이 주요 (단점) 이점이 될 것이라고 생각합니까?


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)

당신의 벤치 마크는 정말 이상합니다. i5 mac에서 ruby2.1.1로 다음과 같은 결과를 얻었습니다. 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.