ruby-changes:64424
From: Kazuki <ko1@a...>
Date: Tue, 22 Dec 2020 02:09:28 +0900 (JST)
Subject: [ruby-changes:64424] 5c0abe2d94 (master): Update documentation for pattern matching
https://git.ruby-lang.org/ruby.git/commit/?id=5c0abe2d94 From 5c0abe2d94c545275079a862dc36bb163953010a Mon Sep 17 00:00:00 2001 From: Kazuki Tsujimoto <kazuki@c...> Date: Tue, 22 Dec 2020 02:08:32 +0900 Subject: Update documentation for pattern matching diff --git a/doc/syntax/pattern_matching.rdoc b/doc/syntax/pattern_matching.rdoc index a4af301..323f7ca 100644 --- a/doc/syntax/pattern_matching.rdoc +++ b/doc/syntax/pattern_matching.rdoc @@ -15,15 +15,15 @@ Pattern matching in Ruby is implemented with the +case+/+in+ expression: https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L15 ... 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> 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: @@ -39,7 +39,7 @@ Therefore, +case+ expression might be used for conditional matching and unpackin https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L39 end # 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'}} @@ -48,7 +48,7 @@ whilst the +=>+ operator is most useful when expected data structure is known be https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L48 puts "Connect with user '#{user}'" # 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: users = [{name: "Alice", age: 12}, {name: "Bob", age: 23}] @@ -65,12 +65,12 @@ Patterns can be: https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L65 * 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>) * 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. 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 @@ -92,6 +92,24 @@ while the hash matches even if there are other keys besides specified part: https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L92 end #=> "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>: case {a: 1, b: 2} @@ -122,7 +140,7 @@ Both array and hash patterns support "rest" specification: https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L140 end #=> "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] in Integer, Integer @@ -140,7 +158,7 @@ In +case+ (but not in +=>+ and +in+) expression, parentheses around both kinds o https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L158 end #=> "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] in [*, String, String, *] @@ -187,7 +205,7 @@ If no additional check is required, only binding some part of the data to a vari https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L205 end #=> "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} in a: @@ -235,17 +253,17 @@ Binding to variables currently does NOT work for alternative patterns joined wit https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L253 end # 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} - in {a: _} | Array - "matched: #{_}" + in {a: _, b: _foo} | Array + "matched: #{_}, #{_foo}" else "not matched" end # => "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 @@ -262,7 +280,7 @@ Due to variable binding feature, existing local variable can't be straightforwar https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L280 # expected: "not matched. expectation was: 18" # 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 case [1, 2] @@ -294,9 +312,9 @@ One important usage of variable pinning is specifying the same value should happ https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L312 end #=> "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 def initialize(x, y) @@ -315,7 +333,7 @@ As already mentioned above, hash and array/find patterns besides literal arrays https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L333 end case Point.new(1, -2) - in px, Integer # subpatterns and variable binding works + in px, Integer # sub-patterns and variable binding works "matched: #{px}" else "not matched" @@ -418,6 +436,7 @@ So, only subsequently loaded files or `eval`-ed code is affected by switching th https://github.com/ruby/ruby/blob/trunk/doc/syntax/pattern_matching.rdoc#L436 Alternatively, command-line key <code>-W:no-experimental</code> can be used to turn off "experimental" feature warnings. == Appendix A. Pattern syntax + Approximate syntax is: pattern: value_pattern -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/