Ruby 標準ライブラリ Comparable Struct

Ruby 標準機能・ライブラリ確認シリーズ。
今回は、Comparable Struct

参考

試す

Comparable

比較機能を提供してくれるモジュール Comparable

Observable など他のモジュールと違って公式リファレンスにも、サンプルがない。
<=> メソッドを用意することで比較機能を提供してくれる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class A
include Comparable

attr_reader :val

def initialize(val)
@val = val
end

def <=>(v)
# 引数 v のクラスが、A だったときは、.valを参照して比較する
return @val <=> v.val if v.class == self.class

@val <=> v
end
end

a1 = A.new(10)
a2 = A.new(20)
a3 = A.new(1)

# ただの数値と比較
p a1 < 5
# => false

p a1 < 15
# => true

# クラスA のオブジェクト同士で比較
p a1 < a2
# => true

p a1 < a3
# => false

特定のクラスオブジェクト同士の特定のパラメータで比較をするなら使ってみるのは検討対象になるのも良さそうです。

.clampメソッドも提供してくれる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class A
include Comparable

attr_reader :val

def initialize(val)
@val = val
end

def <=>(v)
return @val <=> v.val if v.class == self.class

@val <=> v
end
end

# min maxとRangeを与えるパターンがある
pp A.new(10).clamp(0, 100)
pp A.new(10).clamp(0..100)
# => <A:0x000055a37b6ac538 @val=10> 範囲内なら 元のオブジェクトが返る

pp A.new(10).clamp(50, 100)
pp A.new(10).clamp(50..100)
# => 50

pp A.new(100).clamp(0, 10)
pp A.new(100).clamp(0..10)
# => 10

p 50.clamp(0..100)
# => 50
p -50.clamp(0..)
# => 0
p 50.clamp(0..10)
# => 10

p 'x'.clamp('a'..'f')
# => "f"

.clampをオブジェクトで使うためにComparableを使うより、
比較機能のためにComparableを使い、.clampは一定範囲内に入力を収める為に数字や文字に使いそうです。

Struct

メソッドの実行結果を階層が連なったハッシュで返すことが多々あったが、Struct で返すのも 1 つの手段になりそう。
業界的なデファクトスタンダードはどちらだろうか?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 第一引数を文字列で渡す
tmp = Struct.new('B', :age)
puts tmp
# => Struct::B Struct空間のBクラスになる

# 第一引数をシンボルで渡す
pp Struct.new(:age)
# => #<Class:0x0000555d8c0e8708> 無名のクラスになる

ClassC = Struct.new('C', :age)

c1 = ClassC.new(30)
pp c1
# => #<struct Struct::C age=30>

# オブジェクトライクにも ハッシュライクにもプロパティにアクセスできる
p c1.age
# => 30
p c1[:age]
# => 30

# keyword_init: trueでキーワード引数で初期化できるクラスになる
ClassD = Struct.new('D', :age, keyword_init: true)
d1 = ClassD.new(age: 30)
pp d1
# => #<struct Struct::D age=30>

# ブロックを使うとメソッドが生やせる
ClassE = Struct.new('E', :age, keyword_init: true) do
def teen?
(13..19).cover? age
end
end

e1 = ClassE.new(age: 30)
p e1.teen?
# => false

Struct は、提供するメソッド多数のため一部分だけ。

ハッシュ or Struct or OpenStruct どれがいいのか問題は残ります。
本当にデファクトスタンダードは何なんだ。


もうしばらく、標準機能とモジュールの海を潜っていたいところです。

ではでは。