DART 基础拾遗系列之模式匹配

Matching 匹配

模式总是针对值进行测试,以确定该值是否具有您所期望的形式。

许多模式都使用子模式,有时分别称为外模式和内模式。模式在其子模式上进行递归匹配。例如,任何集合类型模式的各个字段都可以是变量模式或常量模式:

const a = 'a';
const b = 'b';
switch (obj) {
  // List pattern [a, b] matches obj first if obj is a list with two fields,
  // then if its fields match the constant subpatterns 'a' and 'b'.
  case [a, b]:
    print('$a, $b');
}

Destructuring 解构

当对象和模式匹配时,模式就可以访问对象的数据,并将其部分提取出来。换句话说,模式会解构对象并赋值:

var numList = [1, 2, 3];
// List pattern [a, b, c] destructures the three elements from numList...
var [a, b, c] = numList;
// ...and assigns them to new variables.
print(a + b + c); // 6

你可以在解构模式中嵌套任何类型的模式。例如,这种情况模式匹配并解构一个具有两个元素的列表,其中第一个元素为 "a "或 "b "的:

var list = ['a', 2];
  switch (list) {
    case ['a' || 'b', var c]:
      print(c); // 2
  }

  var list = ['s', 2];
  switch (list) {
    case ['a' || 'b', var c]:
      print(c);
  } // 模式不匹配,没有输出

Places patterns can appear 可能出现模式匹配的地方

  • Local variable declarations and assignments 局部变量声明和赋值
  • for 和 for-in loops
  • if-case和 switch-case
  • 集合迭代控制流中

Variable declaration 变更声明

// Declares new variables a, b, and c.
var (a, [b, c]) = ('str', [1, 2]);

Variable assignment 变量赋值

var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap.
print('$a $b'); // Prints "right left".

Switch statements and expressions Switch 语名和表达式

解构的值为局部变量。它们的作用域仅限于该案例的主体。

 var obj = (1, 2);
  switch (obj) {
    // Matches if 1 == obj.
    case 1:
      print('one');
	// Matches if obj is a record with two fields,
    // then assigns the fields to 'a' and 'b'.
   case (var a, var b):
      print('a = $a, b = $b'); // a = 1, b = 2

    default:
    }
var obj = (1, 2);
  switch (obj) {
    // Matches if 1 == obj.
    case 1:
      print('one');
      
    // Matches if the value of obj is between the
    // constant values of 1 and 10.
     case >= 1 && <= 10:
       print('in range'); // in range

逻辑或 在多个case 条件符合共用一个 case 主体是通常比较有用:

switch (shape) {
  case Square(size: var s) || Circle(size: var s) when s > 0:
    print('Non-empty symmetric shape');
}

For and for-in 循环

可以在 for 循环和 for-in 循环中使用模式匹配来遍历和解构集合中的值。

  var hist = <String, int>{
    'a': 23,
    'b': 100,
  };

  for (var MapEntry(key: key, value: count) in hist.entries) {
    print('$key occurred $count times'); 
  }
	// a occurred 23 times
	// b occurred 100 times

因为解构使用的键(getter)和解构后的变量名相同,还可以这样简写:

 var hist = <String, int>{
    'a': 23,
    'b': 100,
  };

  for (var MapEntry(:key, :value) in hist.entries) {
    print('$key occurred $value times');
  }

解构多个返回值

Records 允许从单个函数调用中汇总和返回多个值。模式匹配使记返回记录字段可直接解构为本地变量中的能力,相当于解构后赋值。

// before
var info = userInfo(json);
var name = info.$1;
var age = info.$2;

//after
var (name, age) = userInfo(json);

解构类实例

对象的模式匹配允许你使用类中已公开的getter解构实类中的数据:

void main(List<String> args) {
  final Foo myFoo = Foo(one: 'one', two: 2);
  var Foo(:one, :two) = myFoo;
  print('one $one, two $two');
}

class Foo {
  final one;
  final two;
  Foo({required this.one, required this.two}); // one one, two 2
}

Algebraic data types

sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// Shape 的所有子类必须有一个switch 不然编译前的语法分析过不了
double calculateArea(Shape shape) => switch (shape) {
      Square(length: var l) => l * l,
      Circle(radius: var r) => math.pi * r * r
    };
    ```
### 验证传入的JSON

```dart
// before 
if (json is Map<String, Object?> &&
    json.length == 1 &&
    json.containsKey('user')) {
  var user = json['user'];
  if (user is List<Object> &&
      user.length == 2 &&
      user[0] is String &&
      user[1] is int) {
    var name = user[0] as String;
    var age = user[1] as int;
    print('User $name is $age years old.');
  }
}

// after 使用 if-case
if (json case {'user': [String name, int age]}) {
  print('User $name is $age years old.');
}

JSON 的解构赋值

  var user = {
    'user': ['lily', 12]
  };
  var {'user': [name as String, age as num]} = user;
  print('$name $age'); // lily 12

Pattern Types 模式种类

模式的优先级

  • 逻辑或< 逻辑与 < 关系运算(==, !=, <, >, <=, and >=.)
  • 后置(cast, null-check, and null-assert) 优先级相同
  • 其余主要模式的优先级最高。集合类型(Records、List和Map)和对象模式作为外层模式首先进行评估。

以上优先级依次升高,当然,可以使用括号来调整优先级。

逻辑或:
subpattern1 || subpattern2

var isPrimary = switch (color) {
  Color.red || Color.yellow || Color.blue => true,
  _ => false
};

逻辑与:
subpattern1 && subpattern2

  switch ((1, 2)) {
    // Error, both subpatterns attempt to bind 'b'.
    // The variable 'b' is already defined in this pattern. Try renaming the variable.
    case (var a, var b) && (var b, var c):
      print('$a$b$c');
    // ...
  }
}

// 如下可正常运行
 switch ((1, 2)) {
   case (var a, var _) && (var _, var c):
     print('$a$c'); // 12

// 使用case 表达式
  var f = switch ((1, 2)) {
    (var a, var b) && (var k, var c) => '$a $b $k $c',
  };
  print(f); // 1 2 1 2

关系型运算
== expression
< expression


  String asciiCharType(int char) {
    const space = 32;
    const zero = 48;
    const nine = 57;

    return switch (char) { 
      < space => 'control', 
      == space => 'space', 
      > space && < zero => 'punctuation', 
      >= zero && <= nine => 'digit',
       _ => 'fffffffff' };
  }

  print(asciiCharType(18)); // control
  print(asciiCharType(32)); // space
  print(asciiCharType(1132)); // fffffffff

Cast

foo as String

(num, Object) record = (1, 's');
var (i as int, s as String) = record;

Null-check

  String? maybeString = 'nullable with base type String';
  switch (maybeString) {
    case var s:
      print(s); // nullable with base type String
  }
  
// -----------
 String? maybeString;
  switch (maybeString) {
    case var s:
      print(s); // null
  }


// --------

  String? maybeString;
  switch (maybeString) {
    case var s?:
      print(1); // nothing stdout
  }

Null-assert

  List<String?> row = ['user', null];
  switch (row) {
    case ['user', var name!]: 
        // Unhandled exception:
        // Null check operator used on a null value
  }
 List<String?> row = ['user', 'cc'];
  switch (row) {
    case ['user', var name!]:
      print(name); // cc
  }
  List<String?> row = ['user', null];
  switch (row) {
    case ['user', var name]:
      print(name); // null
  }
// 非空断言
(int?, int?) position = (2, 3);
var (x!, y!) = position;

Constant 常量匹配

123, null, 'string', math.pi, SomeClass.constant, const Thing(1, 2), const (1 + 2)

Variable 变量

var bar, String str, final int _

Identifier 标识符, 同变量

foo, _

  const c = 1;
  switch (2) {
    case c:
      print('match $c');
    default:
      print('no match'); // Prints "no match".
  }

  switch ((1, 2)) {
    case (var a, var b):
      print('$a $b'); // 1 2
  }

  switch ((1, 2, 3)) {
    case (var a, var b):
      print('$a $b'); // 1 2
  }

  switch (1) {
    case (var a, var b):
      print('$a $b'); // 1 2
    default:
      print('none'); // Prints "none".
  }

List 列表匹配

[subpattern1, subpattern2]

  const a = 'a';
  const b = 'b';
  switch (['a', 'b']) {
    // List pattern [a, b] matches obj first if obj is a list with two fields,
    // then if its fields match the constant subpatterns 'a' and 'b'.
    case [a, b]:
      print('$a, $b'); // Prints a, b
  }

Rest element

var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 6 7".
print('$a $b $c $d');
  var [a, b, ...r, c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 6 7 [3, 4, 5]".
  print('$a $b $c $d $r');

Record

(subpattern1, subpattern2)
(x: subpattern1, y: subpattern2)

  var (myString: foo, myNumber: bar) = (myString: 'string', myNumber: 1);
  print('$foo, $bar'); // string, 1

  var record = (untyped: 'unknown', typed: 2);
  var (untyped: untyped, typed: int typed) = record;
  print('$untyped, $typed');  // unknown, 2

// 可简写
  var record = (untyped: 'unknown', typed: 2);
  var (:untyped, :int typed) = record;
  print('$untyped, $typed'); // unknown, 2
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值