Feature #14916
openProposal to add Array#===
Description
概要¶
Array#===
を追加する提案になります。
基本的な動作は『Array#==
の ===
で比較する版』になります。
仕様¶
配列の各要素をそれぞれ順に ===
で比較し、全要素が true
の場合に true
を返す。そうでない場合は false
を返す。
動作例¶
# 配列の各要素を #=== を使用して比較する
[ String, /\w/ ] === [ "a", "c", 7 ] # => false
[ String, /\w/, (1..10) ] === [ "a", "c", 7 ] # => true
[ String, /\w/, (1..10) ] === [ "a", "!", 42 ] # => false
真を返すケース¶
- レシーバと引数が配列で同じサイズかつ、各要素を
===
で比較したときに全てtrue
になる場合 - レシーバと引数が同じオブジェクトの場合
偽を返すケース¶
- レシーバと引数が配列で同じサイズかつ、各要素を
===
で比較したときにfalse
がある場合 - レシーバと引数の配列のサイズが異なる場合
- 引数が配列以外の場合
ユースケース¶
引数の値によって処理を変える¶
可変長引数で引数を受け取り、そのまま case-when で値を精査する
def plus *args
case args
# 数値の場合
when [Integer, Integer]
args[0] + args[1]
# 数字の場合
when [/^\d+$/, /^\d+$/]
args[0].to_i + args[1].to_i
# それ以外はエラー
else
raise "Error"
end
end
p plus 1, 2
# => 3
p plus "3", "4"
# => 7
p plus "homu", "mado"
# Error (RuntimeError)
FizzBuzz を用いた例¶
任意の処理の結果を複数回参照したい場合、配列でまとめて case-when で利用する
def fizzbuzz n
_ = proc { true }
case [n % 3, n % 5]
# n % 3 === 0 && n % 5 === 0
when [0, 0]
"FizzBuzz"
# n % 3 === 0
when [0, _]
"Fizz"
# n % 5 === 0
when [_, 0]
"Buzz"
else
n
end
end
p (1..20).map &method(:fizzbuzz)
# => [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz", 16, 17, "Fizz", 19, "Buzz"]
関連しそうなチケット¶
- Feature #14869: Proposal to add Hash#=== - Ruby trunk - Ruby Issue Tracking System
- Feature #14913: Extend case to match several values at once - Ruby trunk - Ruby Issue Tracking System
以上、 Array#===
に関する提案になります。
挙動に関して疑問点や意見などございましたらコメント頂けると助かります。
Files
Updated by mrkn (Kenta Murata) over 6 years ago
- Related to Feature #14869: Proposal to add Hash#=== added
Updated by mrkn (Kenta Murata) over 6 years ago
- Related to Feature #14913: Extend case to match several values at once added
Updated by baweaver (Brandon Weaver) over 6 years ago
I recently got permission to repurpose the Any
gem, which gives us this:
require 'any'
case ['Foo', 25]
when [/^F/, Any] then true
else false
end
# => true
That should make this even more flexible, and eliminate the need for proc { true }
explicitly.
Updated by Eregon (Benoit Daloze) about 6 years ago
I think there is a potential incompatibility here, due to changing the behavior of #===.
The fact Module#===
doesn't return true for mod === mod exacerbates the issue.
For instance,
v = [String, 42]
case v
when [String, 42]
p :ok
else
:incompatible
end
Are there no existing use of case/when with an Array in when?
Updated by osyo (manga osyo) about 6 years ago
- File array_eqq.patch array_eqq.patch added
I think there is a potential incompatibility here, due to changing the behavior of #===.
The fact Module#=== doesn't return true for mod === mod exacerbates the issue.
こちらですが、互換性を壊さないように ===
だけではなくて ==
でも比較するようにし、いずれかの比較演算子の結果が真であれば true
を返すように変更してみました。
String === String
# => false
[String] === [String]
# => true
[/aaa/] === [/aaa/]
# => true
v = [String, 42]
case v
when [String, 42]
p :ok
else
:incompatible
end
# => :ok
Updated by baweaver (Brandon Weaver) about 6 years ago
I've noticed that this will always return false if the other value is not an array:
(line 4011 of patch)
if (!RB_TYPE_P(ary2, T_ARRAY)) return Qfalse;
Have we considered potentially checking if the object responds to to_ary
, and if so coercing it in the comparison?
This behavior is present in IPAddr
, which coerces the right-hand value to an IPAddr
before comparison.
I believe this would, while taking a minor speed hit, give much greater flexibility and duck-typing compatibility to this feature.
Updated by osyo (manga osyo) about 6 years ago
- File array_eqq.patch array_eqq.patch added
I believe this would, while taking a minor speed hit, give much greater flexibility and duck-typing compatibility to this feature.
OK, support call to_ary
.
Updated by timriley (Tim Riley) about 6 years ago
Like I just mentioned in https://bugs.ruby-lang.org/issues/14869#note-13, would you consider changing this to run the explicit #to_a
converter as well as (or instead of) the implicit #to_ary
? This would make the matching even more flexible, since it could be using with objects that want to provide an interface for conversion into an array without necessarily pretending to "be" one (as is the case for #to_ary
).