ÿØÿà JFIF    ÿÛ „  ( %!1!%)+//.383,7(-.+  -%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ  ¥2" ÿÄ     ÿÄ J    ! 1AQ"aq2‘#BR‚¡ÁÑ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ   ÿÄ *  !1AQa‘"2q3±ð#b¡ÿÚ   ? ¼QxJQaÍuò¸Zö Úü8,ÐÚú "SSn<rçù–´âE—^ªBÖ9À\†¸ÔÁT­ÃÛ5 ëd´³Í#Ý;Þ38œî ¶H£M:wÎ3…³…âpÔF&‚FK¸9„â4àGEõªfÿ ‘ñ(ßw­pŽF|È¥ù®häðÍѶ¹‘[ÒinÙW¶ùñY˜Q{›K"išÒ[Ú8žë\F¹@-?v"ÔU”,ìöžkÿ {I‡£šÍ?e ríV ?> ......................................... ............................................................................. ÿØÿà JFIF    ÿÛ „  ( %!1!%)+//.383,7(-.+  -%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ  ¥2" ÿÄ     ÿÄ J    ! 1AQ"aq2‘#BR‚¡ÁÑ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ   ÿÄ *  !1AQa‘"2q3±ð#b¡ÿÚ   ? ¼QxJQaÍuò¸Zö Úü8,ÐÚú "SSn<rçù–´âE—^ªBÖ9À\†¸ÔÁT­ÃÛ5 ëd´³Í#Ý;Þ38œî ¶H£M:wÎ3…³…âpÔF&‚FK¸9„â4àGEõªfÿ ‘ñ(ßw­pŽF|È¥ù®häðÍѶ¹‘[ÒinÙW¶ùñY˜Q{›K"išÒ[Ú8žë\F¹@-?v"ÔU”,ìöžkÿ {I‡£šÍ?e ríV ?> ......................................... ............................................................................. ???????????????????????????????????? ???????????????????????????????????? ÿØÿà JFIF    ÿÛ „  ( %!1!%)+//.383,7(-.+  -%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ  ¥2" ÿÄ     ÿÄ J    ! 1AQ"aq2‘#BR‚¡ÁÑ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ   ÿÄ *  !1AQa‘"2q3±ð#b¡ÿÚ   ? ¼QxJQaÍuò¸Zö Úü8,ÐÚú "SSn<rçù–´âE—^ªBÖ9À\†¸ÔÁT­ÃÛ5 ëd´³Í#Ý;Þ38œî ¶H£M:wÎ3…³…âpÔF&‚FK¸9„â4àGEõªfÿ ‘ñ(ßw­pŽF|È¥ù®häðÍѶ¹‘[ÒinÙW¶ùñY˜Q{›K"išÒ[Ú8žë\F¹@-?v"ÔU”,ìöžkÿ {I‡£šÍ?e ríV ?> ......................................... ............................................................................. ÿØÿà JFIF    ÿÛ „  ( %!1!%)+//.383,7(-.+  -%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ  ¥2" ÿÄ     ÿÄ J    ! 1AQ"aq2‘#BR‚¡ÁÑ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ   ÿÄ *  !1AQa‘"2q3±ð#b¡ÿÚ   ? ¼QxJQaÍuò¸Zö Úü8,ÐÚú "SSn<rçù–´âE—^ªBÖ9À\†¸ÔÁT­ÃÛ5 ëd´³Í#Ý;Þ38œî ¶H£M:wÎ3…³…âpÔF&‚FK¸9„â4àGEõªfÿ ‘ñ(ßw­pŽF|È¥ù®häðÍѶ¹‘[ÒinÙW¶ùñY˜Q{›K"išÒ[Ú8žë\F¹@-?v"ÔU”,ìöžkÿ {I‡£šÍ?e ríV ?> ......................................... ............................................................................. ???????????????????????????????????? ???????????????????????????????????? U:RDoc::TopLevel[ iI"!syntax/pattern_matching.rdoc:ETcRDoc::Parser::Simpleo:RDoc::Markup::Document: @parts[S:RDoc::Markup::Heading: leveli: textI"Pattern matching;To:RDoc::Markup::BlankLineo:RDoc::Markup::Paragraph;[I"Pattern matching is a feature allowing deep matching of structured values: checking the structure and binding the matched parts to local variables.;T@ o; ;[I"MPattern matching in Ruby is implemented with the +case+/+in+ expression:;T@ o:RDoc::Markup::Verbatim;[I"case ;TI"in ;TI" ... ;TI"in ;TI" ... ;TI"in ;TI" ... ;TI" else ;TI" ... ;TI" end ;T: @format0o; ;[I"T(Note that +in+ and +when+ branches can NOT be mixed in one +case+ expression.);T@ o; ;[I"nOr with the => operator and the +in+ operator, which can be used in a standalone expression:;T@ o;;[I" => ;TI" ;TI" in ;T;0o; ;[I"The +case+/+in+ expression is _exhaustive_: if the value of the expression does not match any branch of the +case+ expression (and the +else+ branch is absent), +NoMatchingPatternError+ is raised.;T@ o; ;[I"[Therefore, the +case+ expression might be used for conditional matching and unpacking:;T@ o;;[I"8config = {db: {user: 'admin', password: 'abc123'}} ;TI" ;TI"case config ;TI"Nin db: {user:} # matches subhash and puts matched value in variable user ;TI"* puts "Connect with user '#{user}'" ;TI"!in connection: {username: } ;TI". puts "Connect with user '#{username}'" ;TI" else ;TI"/ puts "Unrecognized structure of config" ;TI" end ;TI"+# Prints: "Connect with user 'admin'" ;T;0o; ;[I"whilst the => operator is most useful when the expected data structure is known beforehand, to just unpack parts of it:;T@ o;;[ I"8config = {db: {user: 'admin', password: 'abc123'}} ;TI" ;TI"Rconfig => {db: {user:}} # will raise if the config's structure is unexpected ;TI" ;TI"(puts "Connect with user '#{user}'" ;TI"+# Prints: "Connect with user 'admin'" ;T;0o; ;[I"{ in is the same as case ; in ; true; else false; end. ;TI"TYou can use it when you only want to know if a pattern has been matched or not:;T@ o;;[I"@users = [{name: "Alice", age: 12}, {name: "Bob", age: 23}] ;TI"Busers.any? {|user| user in {name: /B/, age: 20..} } #=> true ;T;0o; ;[I"@See below for more examples and explanations of the syntax.;T@ S; ; i; I" Patterns;T@ o; ;[I"Patterns can be:;T@ o:RDoc::Markup::List: @type: BULLET: @items[ o:RDoc::Markup::ListItem: @label0;[o; ;[I"iany Ruby object (matched by the === operator, like in +when+); (Value pattern);To;;0;[o; ;[I"jarray pattern: [, , , ...]; (Array pattern);To;;0;[o; ;[I"~find pattern: [*variable, , , , ..., *variable]; (Find pattern);To;;0;[o; ;[I"dhash pattern: {key: , key: , ...}; (Hash pattern);To;;0;[o; ;[I"Pcombination of patterns with |; (Alternative pattern);To;;0;[o; ;[I"variable capture: => variable or variable; (As pattern, Variable pattern);T@ o; ;[I"lAny pattern can be nested inside array/find/hash patterns where is specified.;T@ o; ;[I"{Array patterns and find patterns match arrays, or objects that respond to +deconstruct+ (see below about the latter). ;TI"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.;T@ o; ;[I"oAn important difference between array and hash pattern behavior is that arrays match only a _whole_ array:;T@ o;;[ I"case [1, 2, 3] ;TI"in [Integer, Integer] ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "not matched" ;T;0o; ;[I"Twhile the hash matches even if there are other keys besides the specified part:;T@ o;;[ I"case {a: 1, b: 2, c: 3} ;TI"in {a: Integer} ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;T;0o; ;[I"e{} is the only exclusion from this rule. It matches only if an empty hash is given:;T@ o;;[I"case {a: 1, b: 2, c: 3} ;TI" in {} ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "not matched" ;TI" ;TI" case {} ;TI" in {} ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;T;0o; ;[I"There is also a way to specify there should be no other keys in the matched hash except those explicitly specified by the pattern, with **nil:;T@ o;;[I"case {a: 1, b: 2} ;TI"Xin {a: Integer, **nil} # this will not match the pattern having keys other than a: ;TI" "matched a part" ;TI"(in {a: Integer, b: Integer, **nil} ;TI" "matched a whole" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched a whole" ;T;0o; ;[I"?Both array and hash patterns support "rest" specification:;T@ o;;[I"case [1, 2, 3] ;TI"in [Integer, *] ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;TI" ;TI"case {a: 1, b: 2, c: 3} ;TI"in {a: Integer, **} ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;T;0o; ;[I"@Parentheses around both kinds of patterns could be omitted:;T@ o;;[I" case [1, 2] ;TI" in Integer, Integer ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI" #=> "matched" ;TI" ;TI" case {a: 1, b: 2, c: 3} ;TI" in a: Integer ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI" #=> "matched" ;TI" ;TI"[1, 2] => a, b ;TI"[1, 2] in a, b ;TI" ;TI"{a: 1, b: 2, c: 3} => a: ;TI"{a: 1, b: 2, c: 3} in a: ;T;0o; ;[I"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:;T@ o;;[ I" case ["a", 1, "b", "c", 2] ;TI"in [*, String, String, *] ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;T;0S; ; i; I"Variable binding;T@ o; ;[I"zBesides deep structural checks, one of the very important features of the pattern matching is the binding of the matched parts to local variables. The basic form of binding is just specifying => variable_name after the matched (sub)pattern (one might find this similar to storing exceptions in local variables in a rescue ExceptionClass => var clause):;T@ o;;[I"case [1, 2] ;TI"in Integer => a, Integer ;TI" "matched: #{a}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1" ;TI" ;TI"case {a: 1, b: 2, c: 3} ;TI"in a: Integer => m ;TI" "matched: #{m}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1" ;T;0o; ;[I"|If no additional check is required, for only binding some part of the data to a variable, a simpler form could be used:;T@ o;;[I"case [1, 2] ;TI"in a, Integer ;TI" "matched: #{a}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1" ;TI" ;TI"case {a: 1, b: 2, c: 3} ;TI" in a: m ;TI" "matched: #{m}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1" ;T;0o; ;[I"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:;T@ o;;[ I"case {a: 1, b: 2, c: 3} ;TI" in a: ;TI" "matched: #{a}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1" ;T;0o; ;[I"/Binding works for nested patterns as well:;T@ o;;[ I"Fcase {name: 'John', friends: [{name: 'Jane'}, {name: 'Rajesh'}]} ;TI"2in name:, friends: [{name: first_friend}, *] ;TI"" "matched: #{first_friend}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: Jane" ;T;0o; ;[I"BThe "rest" part of a pattern also can be bound to a variable:;T@ o;;[I"case [1, 2, 3] ;TI"in a, *rest ;TI" "matched: #{a}, #{rest}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1, [2, 3]" ;TI" ;TI"case {a: 1, b: 2, c: 3} ;TI"in a:, **rest ;TI" "matched: #{a}, #{rest}" ;TI" else ;TI" "not matched" ;TI" end ;TI"$#=> "matched: 1, {b: 2, c: 3}" ;T;0o; ;[I"fBinding to variables currently does NOT work for alternative patterns joined with |:;T@ o;;[ I"case {a: 1, b: 2} ;TI"in {a: } | Array ;TI" "matched: #{a}" ;TI" else ;TI" "not matched" ;TI" end ;TI"A# SyntaxError (illegal variable in alternative pattern (a)) ;T;0o; ;[I"UVariables that start with _ are the only exclusions from this rule:;T@ o;;[ I"case {a: 1, b: 2} ;TI" in {a: _, b: _foo} | Array ;TI" "matched: #{_}, #{_foo}" ;TI" else ;TI" "not matched" ;TI" end ;TI"# => "matched: 1, 2" ;T;0o; ;[I"qIt is, though, not advised to reuse the bound value, as this pattern's goal is to signify a discarded value.;T@ S; ; i; I"Variable pinning;T@ o; ;[I"uDue to the variable binding feature, existing local variable can not be straightforwardly used as a sub-pattern:;T@ o;;[I"expectation = 18 ;TI" ;TI"case [1, 2] ;TI"in expectation, *rest ;TI"2 "matched. expectation was: #{expectation}" ;TI" else ;TI"6 "not matched. expectation was: #{expectation}" ;TI" end ;TI"4# expected: "not matched. expectation was: 18" ;TI"L# real: "matched. expectation was: 1" -- local variable just rewritten ;T;0o; ;[I"{For this case, the pin operator ^ can be used, to tell Ruby "just use this value as part of the pattern":;T@ o;;[ I"expectation = 18 ;TI"case [1, 2] ;TI"in ^expectation, *rest ;TI"2 "matched. expectation was: #{expectation}" ;TI" else ;TI"6 "not matched. expectation was: #{expectation}" ;TI" end ;TI",#=> "not matched. expectation was: 18" ;T;0o; ;[I"yOne important usage of variable pinning is specifying that the same value should occur in the pattern several times:;T@ o;;[I"Zjane = {school: 'high', schools: [{id: 1, level: 'middle'}, {id: 2, level: 'high'}]} ;TI"Bjohn = {school: 'high', schools: [{id: 1, level: 'middle'}]} ;TI" ;TI"case jane ;TI"bin school:, schools: [*, {id:, level: ^school}] # select the last school, level should match ;TI" "matched. school: #{id}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched. school: 2" ;TI" ;TI"Vcase john # the specified school level is "high", but last school does not match ;TI"5in school:, schools: [*, {id:, level: ^school}] ;TI" "matched. school: #{id}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "not matched" ;T;0o; ;[I"dIn addition to pinning local variables, you can also pin instance, global, and class variables:;T@ o;;[I"$gvar = 1 ;TI" class A ;TI" @ivar = 2 ;TI" @@cvar = 3 ;TI" case [1, 2, 3] ;TI"" in ^$gvar, ^@ivar, ^@@cvar ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI" #=> "matched" ;TI" end ;T;0o; ;[I"LYou can also pin the result of arbitrary expressions using parentheses:;T@ o;;[I" a = 1 ;TI" b = 2 ;TI" case 3 ;TI"in ^(a + b) ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;T;0S; ; i; I"IMatching non-primitive objects: +deconstruct+ and +deconstruct_keys+;T@ o; ;[I"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).;T@ o;;[&I"class Point ;TI" def initialize(x, y) ;TI" @x, @y = x, y ;TI" end ;TI" ;TI" def deconstruct ;TI"# puts "deconstruct called" ;TI" [@x, @y] ;TI" end ;TI" ;TI"" def deconstruct_keys(keys) ;TI"= puts "deconstruct_keys called with #{keys.inspect}" ;TI" {x: @x, y: @y} ;TI" end ;TI" end ;TI" ;TI"case Point.new(1, -2) ;TI"?in px, Integer # sub-patterns and variable binding works ;TI" "matched: #{px}" ;TI" else ;TI" "not matched" ;TI" end ;TI"## prints "deconstruct called" ;TI""matched: 1" ;TI" ;TI"case Point.new(1, -2) ;TI"in x: 0.. => px ;TI" "matched: #{px}" ;TI" else ;TI" "not matched" ;TI" end ;TI"1# prints: deconstruct_keys called with [:x] ;TI"#=> "matched: 1" ;T;0o; ;[I"+keys+ are passed to +deconstruct_keys+ to provide a room for optimization in the matched class: if calculating a full hash representation is expensive, one may calculate only the necessary subhash. When the **rest pattern is used, +nil+ is passed as a +keys+ value:;T@ o;;[ I"case Point.new(1, -2) ;TI"in x: 0.. => px, **rest ;TI" "matched: #{px}" ;TI" else ;TI" "not matched" ;TI" end ;TI"0# prints: deconstruct_keys called with nil ;TI"#=> "matched: 1" ;T;0o; ;[I"Additionally, when matching custom classes, the expected class can be specified as part of the pattern and is checked with ===;T@ o;;[I"class SuperPoint < Point ;TI" end ;TI" ;TI"case Point.new(1, -2) ;TI"!in SuperPoint(x: 0.. => px) ;TI" "matched: #{px}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "not matched" ;TI" ;TI" case SuperPoint.new(1, -2) ;TI"Din SuperPoint[x: 0.. => px] # [] or () parentheses are allowed ;TI" "matched: #{px}" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched: 1" ;T;0o; ;[I"=These core and library classes implement deconstruction:;T@ o;;;;[o;;0;[o; ;[I":MatchData#deconstruct and MatchData#deconstruct_keys;;To;;0;[o; ;[I"MTime#deconstruct_keys, Date#deconstruct_keys, DateTime#deconstruct_keys.;T@ S; ; i; I"Guard clauses;T@ o; ;[I"|+if+ can be used to attach an additional condition (guard clause) when the pattern matches in +case+/+in+ expressions. ;TI",This condition may use bound variables:;T@ o;;[I"case [1, 2] ;TI"in a, b if b == a*2 ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;TI" ;TI"case [1, 1] ;TI"in a, b if b == a*2 ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "not matched" ;T;0o; ;[I"+unless+ works, too:;T@ o;;[ I"case [1, 1] ;TI"in a, b unless b == a*2 ;TI" "matched" ;TI" else ;TI" "not matched" ;TI" end ;TI"#=> "matched" ;T;0o; ;[I"NNote that => and +in+ operator can not have a guard clause. ;TI"TThe following examples is parsed as a standalone expression with modifier +if+.;T@ o;;[I" [1, 2] in a, b if b == a*2 ;T;0S; ; i; I"Appendix A. Pattern syntax;T@ o; ;[I"Approximate syntax is:;T@ o;;[&I"pattern: value_pattern ;TI" | variable_pattern ;TI"" | alternative_pattern ;TI" | as_pattern ;TI" | array_pattern ;TI" | find_pattern ;TI" | hash_pattern ;TI" ;TI"value_pattern: literal ;TI" | Constant ;TI"$ | ^local_variable ;TI"' | ^instance_variable ;TI"$ | ^class_variable ;TI"% | ^global_variable ;TI"" | ^(expression) ;TI" ;TI" variable_pattern: variable ;TI" ;TI"2alternative_pattern: pattern | pattern | ... ;TI" ;TI"%as_pattern: pattern => variable ;TI" ;TI".array_pattern: [pattern, ..., *variable] ;TI"6 | Constant(pattern, ..., *variable) ;TI"6 | Constant[pattern, ..., *variable] ;TI" ;TI"8find_pattern: [*variable, pattern, ..., *variable] ;TI"@ | Constant(*variable, pattern, ..., *variable) ;TI"@ | Constant[*variable, pattern, ..., *variable] ;TI" ;TI"9hash_pattern: {key: pattern, key:, ..., **variable} ;TI"A | Constant(key: pattern, key:, ..., **variable) ;TI"A | Constant[key: pattern, key:, ..., **variable] ;T;0S; ; i; I"1Appendix B. Some undefined behavior examples;T@ o; ;[I"fTo leave room for optimization in the future, the specification contains some undefined behavior.;T@ o; ;[I"/Use of a variable in an unmatched pattern:;T@ o;;[I"case [0, 1] ;TI"in [a, 2] ;TI" "not matched" ;TI" in b ;TI" "matched" ;TI" in c ;TI" "not matched" ;TI" end ;TI"a #=> undefined ;TI"c #=> undefined ;T;0o; ;[I">Number of +deconstruct+, +deconstruct_keys+ method calls:;T@ o;;[I" $i = 0 ;TI"ary = [0] ;TI"def ary.deconstruct ;TI" $i += 1 ;TI" self ;TI" end ;TI"case ary ;TI"in [0, 1] ;TI" "not matched" ;TI" in [0] ;TI" "matched" ;TI" end ;TI"$i #=> undefined;T;0: @file@:0@omit_headings_from_table_of_contents_below0