mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Update documentation for pattern matching
This commit is contained in:
parent
208f7d7c80
commit
5c0abe2d94
1 changed files with 37 additions and 18 deletions
|
@ -15,15 +15,15 @@ Pattern matching in Ruby is implemented with the +case+/+in+ expression:
|
||||||
...
|
...
|
||||||
end
|
end
|
||||||
|
|
||||||
(Note that +in+ and +when+ branches can *not* be mixed in one +case+ expression.)
|
(Note that +in+ and +when+ branches can NOT be mixed in one +case+ expression.)
|
||||||
|
|
||||||
or with the +=>+ operator and the +in+ operator, which can be used in a standalone expression:
|
or with the <code>=></code> operator and the +in+ operator, which can be used in a standalone expression:
|
||||||
|
|
||||||
<expression> => <pattern>
|
<expression> => <pattern>
|
||||||
|
|
||||||
<expression> in <pattern>
|
<expression> in <pattern>
|
||||||
|
|
||||||
Pattern matching is _exhaustive_: if variable doesn't match pattern (in a separate +in+ clause), or doesn't matches any branch of +case+ expression (and +else+ branch is absent), +NoMatchingPatternError+ is raised.
|
The +case+/+in+ expression is _exhaustive_: if the value of the expression doesn't match any branch of +case+ expression (and +else+ branch is absent), +NoMatchingPatternError+ is raised.
|
||||||
|
|
||||||
Therefore, +case+ expression might be used for conditional matching and unpacking:
|
Therefore, +case+ expression might be used for conditional matching and unpacking:
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ Therefore, +case+ expression might be used for conditional matching and unpackin
|
||||||
end
|
end
|
||||||
# Prints: "Connect with user 'admin'"
|
# Prints: "Connect with user 'admin'"
|
||||||
|
|
||||||
whilst the +=>+ operator is most useful when expected data structure is known beforehand, to just unpack parts of it:
|
whilst the <code>=></code> operator is most useful when expected data structure is known beforehand, to just unpack parts of it:
|
||||||
|
|
||||||
config = {db: {user: 'admin', password: 'abc123'}}
|
config = {db: {user: 'admin', password: 'abc123'}}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ whilst the +=>+ operator is most useful when expected data structure is known be
|
||||||
puts "Connect with user '#{user}'"
|
puts "Connect with user '#{user}'"
|
||||||
# Prints: "Connect with user 'admin'"
|
# Prints: "Connect with user 'admin'"
|
||||||
|
|
||||||
+<expression> in <pattern>+ is the same as +case <expression>; in <pattern>; true; else false; end+.
|
<code><expression> in <pattern></code> is the same as <code>case <expression>; in <pattern>; true; else false; end</code>.
|
||||||
You can use it when you only want to know if a pattern has been matched or not:
|
You can use it when you only want to know if a pattern has been matched or not:
|
||||||
|
|
||||||
users = [{name: "Alice", age: 12}, {name: "Bob", age: 23}]
|
users = [{name: "Alice", age: 12}, {name: "Bob", age: 23}]
|
||||||
|
@ -65,12 +65,12 @@ Patterns can be:
|
||||||
* find pattern: <code>[*variable, <subpattern>, <subpattern>, <subpattern>, ..., *variable]</code>; (<em>Find pattern</em>)
|
* find pattern: <code>[*variable, <subpattern>, <subpattern>, <subpattern>, ..., *variable]</code>; (<em>Find pattern</em>)
|
||||||
* hash pattern: <code>{key: <subpattern>, key: <subpattern>, ...}</code>; (<em>Hash pattern</em>)
|
* hash pattern: <code>{key: <subpattern>, key: <subpattern>, ...}</code>; (<em>Hash pattern</em>)
|
||||||
* combination of patterns with <code>|</code>; (<em>Alternative pattern</em>)
|
* combination of patterns with <code>|</code>; (<em>Alternative pattern</em>)
|
||||||
* variable capture: <code>variable</code> or <code><pattern> => variable</code>; (<em>Variable pattern</em>, <em>As pattern</em>)
|
* variable capture: <code><pattern> => variable</code> or <code>variable</code>; (<em>As pattern</em>, <em>Variable pattern</em>)
|
||||||
|
|
||||||
Any pattern can be nested inside array/find/hash patterns where <code><subpattern></code> is specified.
|
Any pattern can be nested inside array/find/hash patterns where <code><subpattern></code> is specified.
|
||||||
|
|
||||||
Array patterns and find patterns match arrays, or objects that respond to +deconstruct+ (see below about the latter).
|
Array patterns and find patterns match arrays, or objects that respond to +deconstruct+ (see below about the latter).
|
||||||
Hash patterns match hashes, or objects that respond to +deconstruct_keys+ (see below about the latter). Note that only symbol keys are supported for hash patterns, at least for now.
|
Hash patterns match hashes, or objects that respond to +deconstruct_keys+ (see below about the latter). Note that only symbol keys are supported for hash patterns.
|
||||||
|
|
||||||
An important difference between array and hash patterns behavior is arrays match only a _whole_ array
|
An important difference between array and hash patterns behavior is arrays match only a _whole_ array
|
||||||
|
|
||||||
|
@ -92,6 +92,24 @@ while the hash matches even if there are other keys besides specified part:
|
||||||
end
|
end
|
||||||
#=> "matched"
|
#=> "matched"
|
||||||
|
|
||||||
|
<code>{}</code> is the only exclusion from this rule. It matches iff an empty hash is given:
|
||||||
|
|
||||||
|
case {a: 1, b: 2, c: 3}
|
||||||
|
in {}
|
||||||
|
"matched"
|
||||||
|
else
|
||||||
|
"not matched"
|
||||||
|
end
|
||||||
|
#=> "not matched"
|
||||||
|
|
||||||
|
case {}
|
||||||
|
in {}
|
||||||
|
"matched"
|
||||||
|
else
|
||||||
|
"not matched"
|
||||||
|
end
|
||||||
|
#=> "matched"
|
||||||
|
|
||||||
There is also a way to specify there should be no other keys in the matched hash except those explicitly specified by pattern, with <code>**nil</code>:
|
There is also a way to specify there should be no other keys in the matched hash except those explicitly specified by pattern, with <code>**nil</code>:
|
||||||
|
|
||||||
case {a: 1, b: 2}
|
case {a: 1, b: 2}
|
||||||
|
@ -122,7 +140,7 @@ Both array and hash patterns support "rest" specification:
|
||||||
end
|
end
|
||||||
#=> "matched"
|
#=> "matched"
|
||||||
|
|
||||||
In +case+ (but not in +=>+ and +in+) expression, parentheses around both kinds of patterns could be omitted
|
In +case+ (but not in <code>=></code> and +in+) expression, parentheses around both kinds of patterns could be omitted:
|
||||||
|
|
||||||
case [1, 2]
|
case [1, 2]
|
||||||
in Integer, Integer
|
in Integer, Integer
|
||||||
|
@ -140,7 +158,7 @@ In +case+ (but not in +=>+ and +in+) expression, parentheses around both kinds o
|
||||||
end
|
end
|
||||||
#=> "matched"
|
#=> "matched"
|
||||||
|
|
||||||
Find pattern is similar to array pattern but it can be used to check if the given object has any elements that match the pattern.
|
Find pattern is similar to array pattern but it can be used to check if the given object has any elements that match the pattern:
|
||||||
|
|
||||||
case ["a", 1, "b", "c", 2]
|
case ["a", 1, "b", "c", 2]
|
||||||
in [*, String, String, *]
|
in [*, String, String, *]
|
||||||
|
@ -187,7 +205,7 @@ If no additional check is required, only binding some part of the data to a vari
|
||||||
end
|
end
|
||||||
#=> "matched: 1"
|
#=> "matched: 1"
|
||||||
|
|
||||||
For hash patterns, even a simpler form exists: key-only specification (without any value) binds the local variable with the key's name, too:
|
For hash patterns, even a simpler form exists: key-only specification (without any sub-pattern) binds the local variable with the key's name, too:
|
||||||
|
|
||||||
case {a: 1, b: 2, c: 3}
|
case {a: 1, b: 2, c: 3}
|
||||||
in a:
|
in a:
|
||||||
|
@ -235,17 +253,17 @@ Binding to variables currently does NOT work for alternative patterns joined wit
|
||||||
end
|
end
|
||||||
# SyntaxError (illegal variable in alternative pattern (a))
|
# SyntaxError (illegal variable in alternative pattern (a))
|
||||||
|
|
||||||
<code>_</code> is the only exclusion from this rule: it still binds the first match to local variable <code>_</code>, but allowed to be used in alternative patterns:
|
Variables that start with <code>_</code> are the only exclusions from this rule:
|
||||||
|
|
||||||
case {a: 1, b: 2}
|
case {a: 1, b: 2}
|
||||||
in {a: _} | Array
|
in {a: _, b: _foo} | Array
|
||||||
"matched: #{_}"
|
"matched: #{_}, #{_foo}"
|
||||||
else
|
else
|
||||||
"not matched"
|
"not matched"
|
||||||
end
|
end
|
||||||
# => "matched: 1"
|
# => "matched: 1"
|
||||||
|
|
||||||
It is, though, not advised to reuse bound value, as <code>_</code> pattern's goal is to signify discarded value.
|
It is, though, not advised to reuse bound value, as these pattern's goal is to signify discarded value.
|
||||||
|
|
||||||
== Variable pinning
|
== Variable pinning
|
||||||
|
|
||||||
|
@ -262,7 +280,7 @@ Due to variable binding feature, existing local variable can't be straightforwar
|
||||||
# expected: "not matched. expectation was: 18"
|
# expected: "not matched. expectation was: 18"
|
||||||
# real: "matched. expectation was: 1" -- local variable just rewritten
|
# real: "matched. expectation was: 1" -- local variable just rewritten
|
||||||
|
|
||||||
For this case, the pin operator <code>^</code> can be used, to tell Ruby "just use this value as a part of pattern"
|
For this case, the pin operator <code>^</code> can be used, to tell Ruby "just use this value as a part of pattern":
|
||||||
|
|
||||||
expectation = 18
|
expectation = 18
|
||||||
case [1, 2]
|
case [1, 2]
|
||||||
|
@ -294,9 +312,9 @@ One important usage of variable pinning is specifying the same value should happ
|
||||||
end
|
end
|
||||||
#=> "not matched"
|
#=> "not matched"
|
||||||
|
|
||||||
== Matching non-primitive objects: +deconstruct_keys+ and +deconstruct+
|
== Matching non-primitive objects: +deconstruct+ and +deconstruct_keys+
|
||||||
|
|
||||||
As already mentioned above, hash and array/find patterns besides literal arrays and hashes will try to match any object implementing +deconstruct+ (for array/find patterns) or +deconstruct_keys+ (for hash patterns).
|
As already mentioned above, array/find and hash patterns besides literal arrays and hashes will try to match any object implementing +deconstruct+ (for array/find patterns) or +deconstruct_keys+ (for hash patterns).
|
||||||
|
|
||||||
class Point
|
class Point
|
||||||
def initialize(x, y)
|
def initialize(x, y)
|
||||||
|
@ -315,7 +333,7 @@ As already mentioned above, hash and array/find patterns besides literal arrays
|
||||||
end
|
end
|
||||||
|
|
||||||
case Point.new(1, -2)
|
case Point.new(1, -2)
|
||||||
in px, Integer # subpatterns and variable binding works
|
in px, Integer # sub-patterns and variable binding works
|
||||||
"matched: #{px}"
|
"matched: #{px}"
|
||||||
else
|
else
|
||||||
"not matched"
|
"not matched"
|
||||||
|
@ -418,6 +436,7 @@ So, only subsequently loaded files or `eval`-ed code is affected by switching th
|
||||||
Alternatively, command-line key <code>-W:no-experimental</code> can be used to turn off "experimental" feature warnings.
|
Alternatively, command-line key <code>-W:no-experimental</code> can be used to turn off "experimental" feature warnings.
|
||||||
|
|
||||||
== Appendix A. Pattern syntax
|
== Appendix A. Pattern syntax
|
||||||
|
|
||||||
Approximate syntax is:
|
Approximate syntax is:
|
||||||
|
|
||||||
pattern: value_pattern
|
pattern: value_pattern
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue