17、模式匹配与Monad设计模式在C中的应用

模式匹配与Monad设计模式在C#中的应用

1. 十六进制字符串转换为整数

在编程中,我们常常需要将十六进制字符串转换为整数。当运行 GetIntFromHexString() 函数时,控制台会输出转换结果,该函数会将字符串中的每个十六进制字符转换为整数值,然后将所有结果相加。

要将十六进制字符转换为字节,可以使用 Parse TryParse 方法,或者使用 String.Format 进行格式化。这里有一个示例函数 HexCharToByte() ,不过它只是用于示例目的。

2. 简化模式匹配

我们之前成功使用 switch 语句实现了模式匹配,但 HexCharToByte() 函数在执行过程中会改变 res 变量的值,没有采用函数式编程方法。现在我们要对其进行重构,以应用函数式编程方法。以下是重构后的 HexCharToByteFunctional() 函数:

public partial class Program
{
  public static byte HexCharToByteFunctional(
    char c)
  {
    return c.Match()
      .With(ch =>ch == '1', (byte)1)
      .With(ch =>ch == '2', 2)
      .With(ch =>ch == '3', 3)
      .With(ch =>ch == '4', 4)
      .With(ch =>ch == '5', 5)
      .With(ch =>ch == '6', 6)
      .With(ch =>ch == '7', 7)
      .With(ch =>ch == '8', 8)
      .With(ch =>ch == '9', 9)
      .With(ch =>ch == 'A', 10)
      .With(ch =>ch == 'a', 10)
      .With(ch =>ch == 'B', 11)
      .With(ch =>ch == 'b', 11)
      .With(ch =>ch == 'C', 12)
      .With(ch =>ch == 'c', 12)
      .With(ch =>ch == 'D', 13)
      .With(ch =>ch == 'd', 13)
      .With(ch =>ch == 'E', 14)
      .With(ch =>ch == 'e', 14)
      .With(ch =>ch == 'F', 15)
      .With(ch =>ch == 'f', 15)
      .Else(0)
      .Do();
  }
}

这个函数有四个类似于 switch 语句或 if...else 条件语句的方法: Match() With() Else() Do() 。下面是 Match() 函数的实现:

public static class PatternMatch
{
  public static PatternMatchContext<TIn> Match<TIn>(
    this TIn value)
  {
    return new PatternMatchContext<TIn>(value);
  }
}

Match() 函数返回一个新的 PatternMatchContext 数据类型。 PatternMatchContext 类的实现如下:

public class PatternMatchContext<TIn>
{
  private readonly TIn _value;
  internal PatternMatchContext(TIn value)
  {
    _value = value;
  }

  public PatternMatchOnValue<TIn, TOut> With<TOut>(
    Predicate<TIn> condition,
    TOut result)
  {
    return new PatternMatchOnValue<TIn, TOut>(_value)
      .With(condition, result);
  }
}

Match() 函数生成 PatternMatchContext 的新实例时,其构造函数会将传入的参数值存储在 _value 私有变量中。在 PatternMatchContext 类中,还有一个 With() 方法,它会调用 PatternMatchOnValue 类中的 With() 方法。 PatternMatchOnValue 类的实现如下:

public class PatternMatchOnValue<TIn, TOut>
{
  private readonly IList<PatternMatchCase> _cases =
    new List<PatternMatchCase>();
  private readonly TIn _value;
  private Func<TIn, TOut> _elseCase;
  internal PatternMatchOnValue(TIn value)
  {
    _value = value;
  }

  public PatternMatchOnValue<TIn, TOut> With(
    Predicate<TIn> condition,
    Func<TIn, TOut> result)
  {
    _cases.Add(new PatternMatchCase
    {
      Condition = condition,
      Result = result
    });
    return this;
  }

  public PatternMatchOnValue<TIn, TOut> With(
    Predicate<TIn> condition,
    TOut result)
  {
    return With(condition, x => result);
  }

  public PatternMatchOnValue<TIn, TOut> Else(
  Func<TIn, TOut> result)
  {
    if (_elseCase != null)
    {
      throw new InvalidOperationException(
        "Cannot have multiple else cases");
    }
    _elseCase = result;
    return this;
  }

  public PatternMatchOnValue<TIn, TOut> Else(
    TOut result)
  {
    return Else(x => result);
  }

  public TOut Do()
  {
    if (_elseCase != null)
    {
      With(x => true, _elseCase);
      _elseCase = null;
    }
    foreach (var test in _cases)
    {
      if (test.Condition(_value))
      {
        return test.Result(_value);
      }
    }
    throw new IncompletePatternMatchException();
  }

  private struct PatternMatchCase
  {
    public Predicate<TIn> Condition;
    public Func<TIn, TOut> Result;
  }
}

Do() 方法会将 _elseCase 变量(如果有的话)添加到 _cases 列表中,然后遍历 _cases 列表,找到匹配的条件并返回结果。如果没有匹配的条件,会抛出 IncompletePatternMatchException 异常。以下是该异常类的实现:

public class IncompletePatternMatchException :
  Exception
{
}

我们还可以修改 HexStringToInt() 函数来调用 HexCharToByteFunctional() 函数:

public partial class Program
{
  public static int HexStringToInt(
    string s)
  {
    int iCnt = 0;
    int retVal = 0;
    for (int i = s.Length - 1; i >= 0; i--)
    {
      retVal += HexCharToByteFunctional(s[i]) *
      (int)Math.Pow(0x10, iCnt++);
    }
    return retVal;
  }
}

不过, HexStringToInt() 函数没有采用函数式编程方法,我们可以将其重构为 HexStringToIntFunctional() 函数:

public partial class Program
{
  public static int HexStringToIntFunctional(
    string s)
  {
    return s.ToCharArray()
     .ToList()
     .Select((c, i) => new { c, i })
     .Sum((v) =>
       HexCharToByteFunctional(v.c) *
         (int)Math.Pow(0x10, v.i));
  }
}

以下是调用 HexStringToIntFunctional() 函数的 GetIntFromHexStringFunctional() 函数:

public partial class Program
{
  private static void GetIntFromHexStringFunctional()
  {
    string[] hexStrings = {
      "FF", "12CE", "F0A0", "3BD",
      "D43", "35", "0", "652F",
      "8DCC", "4125"
    };
    Console.WriteLine(
      "Invoking GetIntFromHexStringFunctional() function");
    for (int i = 0; i<hexStrings.Length; i++)
    {
      Console.WriteLine(
        "0x{0}\t= {1}",
        hexStrings[i],
        HexStringToIntFunctional(
          hexStrings[i]));
    }
  }
}

运行 GetIntFromHexStringFunctional() 函数,会得到与 GetIntFromHexString() 函数相同的输出,因为我们成功将其重构为函数式模式匹配。

为了更简单地进行模式匹配,可以使用 Simplicity NuGet包,在Visual Studio的包管理器控制台中输入 Install-PackageSimplicity 即可下载。

3. C# 7中的模式匹配特性

C# 7计划引入的语言特性包括对 is 运算符的扩展。现在可以在类型后面引入一个新变量,该变量会被赋值为 is 运算符左侧操作数,但类型为右侧指定的操作数。以下是C# 7之前和之后使用 is 运算符的代码对比:

C# 7之前

public partial class Program
{
  private static void IsOperatorBeforeCSharp7()
  {
    object o = GetData();
    if (o is String)
    {
      var s = (String)o;
      Console.WriteLine(
        "The object is String. Value = {0}",
          s);
    }
  }
}

public partial class Program
{
  private static object GetData(
      bool objectType = true)
  {
    if (objectType)
        return "One";
    else
        return 1;
  }
}

C# 7之后

public partial class Program
{
  private static void IsOperatorInCSharp7()
  {
    object o = GetData();
    if (o is String s)
    {
      Console.WriteLine(
          "The object is String. Value = {0}",
           s);
    }
  }
}

在C# 7中,我们可以在 if 语句中直接将 o 的值赋给 s 变量。这个特性也可以应用在 switch 语句中,如下所示:

public partial class Program
{
  private static void SwitchCaseInCSharp7()
  {
    object x = GetData(
        false);
    switch (x)
    {
      case string s:
          Console.WriteLine(
              "{0} is a string of length {1}",
              x,
              s.Length);
          break;
      case int i:
          Console.WriteLine(
              "{0} is an {1} int",
              x,
              (i % 2 == 0 ? "even" : "odd"));
          break;
      default:
          Console.WriteLine(
              "{0} is something else",
              x);
          break;
    }
  }
}

更多关于C# 7模式匹配特性的信息可以在官方Roslyn GitHub页面找到:https://github.com/dotnet/roslyn/blob/features/patterns/docs/features/patterns.md

4. Monad作为设计模式

在像C#这样的面向对象编程语言中,解释Monad比较困难。不过,在面向对象编程中,可以用设计模式的概念来解释Monad。设计模式是软件设计中复杂问题的可复用解决方案,就像建筑中的设计模式一样,软件设计中的设计模式也包含函数、类型、变量等元素,它们共同构建应用程序。

Monad是一种使用Monad模式的类型,Monad模式是一种类型的设计模式。在C#中,有一些类型自然地实现了Monad,包括 Nullable<T> IEnumerable<T> Func<T> Lazy<T> Task<T>

4.1 Nullable

Nullable<T> 可以将普通类型转换为可空类型,例如将 int 转换为可空的 int 。以下是一个将字符串表示的数字转换为整数的示例:

public partial class Program
{
  private static Nullable<int> WordToNumber(string word)
  {
    Nullable<int> returnValue;
    if (word == null)
    {
      return null;
    }
    switch (word.ToLower())
    {
      case "zero":
        returnValue = 0;
        break;
      case "one":
        returnValue = 1;
        break;
      case "two":
        returnValue = 2;
        break;
      case "three":
        returnValue = 3;
        break;
      case "four":
        returnValue = 4;
        break;
      case "five":
        returnValue = 5;
        break;
      default:
        returnValue = null;
        break;
    }
    return returnValue;
  }
}

public partial class Program
{
  private static void PrintStringNumber(
    string stringNumber)
  {
    if (stringNumber == null &&
      WordToNumber(stringNumber) == null)
    {
      Console.WriteLine(
        "Word: null is Int: null");
    }
    else
    {
      Console.WriteLine(
        "Word: {0} is Int: {1}",
        stringNumber.ToString(),
        WordToNumber(stringNumber));
    }
  }
}

public partial class Program
{
  private static void PrintIntContainingNull()
  {
    PrintStringNumber("three");
    PrintStringNumber("five");
    PrintStringNumber(null);
    PrintStringNumber("zero");
    PrintStringNumber("four");
  }
}

运行 PrintIntContainingNull() 函数,我们可以看到现在可以给 int 数据类型赋予 null 值,因为它自然地实现了Monad并通过类型放大器进行了扩展。

4.2 IEnumerable

IEnumerable<T> 可以扩展传入的类型 T 。例如,我们可以使用 IEnumerable<string> 来扩展字符串类型,使其可以枚举和排序:

public partial class Program
{
  private static void AmplifyString()
  {
    IEnumerable<string> stringEnumerable
      = YieldNames();
    Console.WriteLine(
      "Enumerate the stringEnumerable");
    foreach (string s in stringEnumerable)
    {
      Console.WriteLine(
        "- {0}", s);
    }
    IEnumerable<string> stringSorted =
      SortAscending(stringEnumerable);
    Console.WriteLine();
    Console.WriteLine(
      "Sort the stringEnumerable");
    foreach (string s in stringSorted)
    {
      Console.WriteLine(
        "- {0}", s);
    }
  }
}

public partial class Program
{
  private static IEnumerable<string> YieldNames()
  {
    yield return "Nicholas Shaw";
    yield return "Anthony Hammond";
    yield return "Desiree Waller";
    yield return "Gloria Allen";
    yield return "Daniel McPherson";
  }
}

public partial class Program
{
  private static IEnumerable<string> SortAscending(
    IEnumerable<string> enumString)
  {
    return enumString.OrderBy(s => s);
  }
}

运行 AmplifyString() 函数,我们可以看到字符串类型被扩展为可以存储多个值,并能进行枚举和排序。

4.3 Func

Func<T> 是一种封装方法,它可以返回指定类型的值而无需传递任何参数。以下是相关示例代码:

public partial class Program
{
  Func<int> MultipliedFunc;
}

public partial class Program
{
  private static Nullable<int> MultipliedByTwo(
    Nullable<int> nullableInt)
  {
    if (nullableInt.HasValue)
    {
      int unWrappedInt =
        nullableInt.Value;
      int multipliedByTwo =
        unWrappedInt * 2;
      return GetNullableFromInt(
        multipliedByTwo);
    }
    else
    {
      return new Nullable<int>();
    }
  }
}

public partial class Program
{
  private static Nullable<int> GetNullableFromInt(
    int iNumber)
  {
    return new Nullable<int>(
      iNumber);
  }
}

public partial class Program
{
  private static void RunMultipliedByTwo()
  {
    for (int i = 1; i <= 5; i++)
    {
      Console.WriteLine(
        "{0} multiplied by to is equal to {1}",
        i, MultipliedByTwo(i));
    }
  }
}

public partial class Program
{
  private static Func<int> GetFuncFromInt(
    int iItem)
  {
    return () => iItem;
  }
}

public partial class Program
{
  private static Func<int> MultipliedByTwo(
   Func<int> funcDelegate)
  {
    int unWrappedFunc =
      funcDelegate();
    int multipliedByTwo =
      unWrappedFunc * 2;
    return GetFuncFromInt(
      multipliedByTwo);
  }
}

通过这些代码示例,我们可以看到 Func<T> 也自然地实现了Monad。

总结

本文介绍了C#中的模式匹配和Monad设计模式。通过重构函数实现了函数式模式匹配,利用C# 7的新特性简化了类型判断和变量赋值。同时,介绍了几种自然实现Monad的类型,展示了它们如何扩展普通类型的功能。这些技术在实际编程中可以提高代码的可读性和可维护性。

以下是一个简单的流程图,展示了 HexStringToIntFunctional() 函数的处理流程:

graph TD;
    A[输入十六进制字符串] --> B[转换为字符列表];
    B --> C[选择字符和索引];
    C --> D[计算每个字符的整数值并乘以权重];
    D --> E[求和];
    E --> F[输出整数结果];

通过这些内容,我们可以更好地理解和应用C#中的模式匹配和Monad设计模式,提升编程能力。

模式匹配与Monad设计模式在C#中的应用

5. 模式匹配和Monad的实际应用场景

在实际的软件开发中,模式匹配和Monad设计模式有着广泛的应用场景,下面我们来详细探讨一下。

5.1 数据验证与转换

在处理用户输入或外部数据源时,常常需要进行数据验证和转换。模式匹配可以帮助我们简洁地处理不同类型的数据输入。例如,在一个表单处理系统中,用户可能输入不同类型的数据,我们可以使用模式匹配来验证和转换这些数据。

public partial class Program
{
    public static object ValidateAndConvert(object input)
    {
        switch (input)
        {
            case string s when int.TryParse(s, out int num):
                return num;
            case string s when double.TryParse(s, out double d):
                return d;
            case string s:
                return s;
            case int i:
                return i;
            case double d:
                return d;
            default:
                return null;
        }
    }
}

在这个例子中,我们使用 switch 语句进行模式匹配,根据输入的不同类型进行相应的验证和转换。如果输入是字符串,尝试将其转换为整数或双精度浮点数;如果输入本身就是整数或双精度浮点数,则直接返回;如果无法处理,则返回 null

5.2 错误处理与异常管理

Monad设计模式在错误处理和异常管理方面也非常有用。以 Nullable<T> 为例,它可以帮助我们优雅地处理可能为空的值,避免空引用异常。

public partial class Program
{
    public static Nullable<int> SafeDivide(int a, int b)
    {
        if (b == 0)
        {
            return null;
        }
        return a / b;
    }

    public static void HandleDivision()
    {
        Nullable<int> result = SafeDivide(10, 2);
        if (result.HasValue)
        {
            Console.WriteLine($"Result: {result.Value}");
        }
        else
        {
            Console.WriteLine("Division by zero!");
        }
    }
}

SafeDivide 函数中,我们使用 Nullable<int> 来表示可能的除法结果。如果除数为零,则返回 null ;否则返回计算结果。在 HandleDivision 函数中,我们检查结果是否有值,避免了空引用异常。

5.3 数据处理与转换管道

IEnumerable<T> Func<T> 可以用于构建数据处理和转换管道。例如,我们可以使用 IEnumerable<T> 来处理一系列数据,并使用 Func<T> 来定义处理逻辑。

public partial class Program
{
    public static IEnumerable<int> ProcessData(IEnumerable<int> data, Func<int, int> processor)
    {
        foreach (int item in data)
        {
            yield return processor(item);
        }
    }

    public static void RunDataProcessing()
    {
        IEnumerable<int> input = new List<int> { 1, 2, 3, 4, 5 };
        Func<int, int> square = x => x * x;
        IEnumerable<int> output = ProcessData(input, square);
        foreach (int result in output)
        {
            Console.WriteLine(result);
        }
    }
}

在这个例子中, ProcessData 函数接受一个整数序列和一个处理函数,对序列中的每个元素应用处理函数,并返回处理后的序列。 RunDataProcessing 函数演示了如何使用这个管道来计算输入序列中每个元素的平方。

6. 模式匹配和Monad的性能考虑

虽然模式匹配和Monad设计模式可以提高代码的可读性和可维护性,但在某些情况下,它们可能会对性能产生影响。

6.1 模式匹配的性能

模式匹配通常会引入一些额外的开销,尤其是在复杂的模式匹配场景中。例如,使用 switch 语句进行模式匹配时,编译器需要对每个 case 进行检查,这可能会增加执行时间。因此,在性能敏感的场景中,需要谨慎使用模式匹配。

6.2 Monad的性能

Monad设计模式通常会引入一些额外的封装和函数调用,这也可能会对性能产生一定的影响。例如, Nullable<T> 需要额外的空间来存储 HasValue 标志, Func<T> 需要额外的函数调用开销。在性能要求较高的场景中,需要权衡使用Monad的利弊。

为了优化性能,可以考虑以下几点:
- 减少不必要的模式匹配 :只在必要的地方使用模式匹配,避免过度使用。
- 缓存中间结果 :对于一些重复计算的结果,可以进行缓存,避免重复计算。
- 使用更高效的数据结构 :选择合适的数据结构可以提高性能,例如使用数组代替 List<T> 在某些情况下可以提高访问速度。

7. 总结与展望

本文详细介绍了C#中的模式匹配和Monad设计模式,包括它们的基本概念、实现方式和实际应用场景。通过模式匹配,我们可以更简洁地处理不同类型的数据和条件;通过Monad设计模式,我们可以扩展普通类型的功能,处理可能的空值、错误和异常。

在未来的软件开发中,模式匹配和Monad设计模式有望得到更广泛的应用。随着编程语言的不断发展,可能会引入更多的模式匹配特性和更强大的Monad实现。同时,开发者也需要不断学习和掌握这些技术,以提高代码的质量和可维护性。

以下是一个表格,总结了本文介绍的模式匹配和Monad相关的类型和方法:
| 类型/方法 | 描述 |
| — | — |
| HexCharToByteFunctional | 将十六进制字符转换为字节的函数式实现 |
| HexStringToIntFunctional | 将十六进制字符串转换为整数的函数式实现 |
| Match With Else Do | 用于模式匹配的方法 |
| Nullable<T> | 可空类型,实现了Monad,用于处理可能为空的值 |
| IEnumerable<T> | 可枚举类型,实现了Monad,用于扩展类型的功能 |
| Func<T> | 封装方法,实现了Monad,用于返回指定类型的值 |

总的来说,模式匹配和Monad设计模式是C#中非常有用的技术,它们可以帮助我们编写更简洁、更健壮的代码。通过不断学习和实践,我们可以更好地应用这些技术,提升软件开发的效率和质量。

下面是一个mermaid流程图,展示了数据验证与转换的流程:

graph TD;
    A[输入数据] --> B{数据类型};
    B -->|字符串| C{是否可转换为整数};
    C -->|是| D[返回整数];
    C -->|否| E{是否可转换为双精度浮点数};
    E -->|是| F[返回双精度浮点数];
    E -->|否| G[返回字符串];
    B -->|整数| H[返回整数];
    B -->|双精度浮点数| I[返回双精度浮点数];
    B -->|其他| J[返回null];

通过这些内容,我们对C#中的模式匹配和Monad设计模式有了更深入的理解,并且可以在实际开发中更好地应用它们。

数据集介绍:垃圾分类检测数据集 一、基础信息 数据集名称:垃圾分类检测数据集 图片数量: 训练集:2,817张图片 验证集:621张图片 测试集:317张图片 总计:3,755张图片 分类类别: - 金属:常见的金属垃圾材料。 - 纸板:纸板类垃圾,如包装盒等。 - 塑料:塑料类垃圾,如瓶子、容器等。 标注格式: YOLO格式,包含边界框和类别标签,适用于目标检测任务。 数据格式:图片来源于实际场景,格式为常见图像格式(如JPEG/PNG)。 二、适用场景 智能垃圾回收系统开发: 数据集支持目标检测任务,帮助构建能够自动识别和分类垃圾材料的AI模型,用于自动化废物分类和回收系统。 环境监测废物管理: 集成至监控系统或机器人中,实时检测垃圾并分类,提升废物处理效率和环保水平。 学术研究教育: 支持计算机视觉环保领域的交叉研究,用于教学、实验和论文发表。 三、数据集优势 类别覆盖全面: 包含三种常见垃圾材料类别,覆盖日常生活中主要的可回收物类型,具有实际应用价值。 标注精准可靠: 采用YOLO标注格式,边界框定位精确,类别标签准确,便于模型直接训练和使用。 数据量适中合理: 训练集、验证集和测试集分布均衡,提供足够样本用于模型学习和评估。 任务适配性强: 标注兼容主流深度学习框架(如YOLO等),可直接用于目标检测任务,支持垃圾检测相关应用
根据原作 https://pan.quark.cn/s/a4b39357ea24 的源码改编 MySQL数据库在维护数据完整性方面运用了多种策略,其中包括日志机制和两阶段提交等手段。 接下来将深入阐述这些机制。 1. **日志机制** - **Redo Log**:重做日志记录了数据页内数据行的变更,存储的是更新后的数据。 它遵循预先写入日志的策略,确保在数据库非正常重启后可以恢复数据。 Redo log采用循环写入方式,其容量可以通过`innodb_log_file_size`和`innodb_log_files_in_group`参数进行调整。 日志首先被写入内存中的redo log buffer,随后根据`innodb_flush_log_at_trx_commit`参数的设定决定何时写入硬盘: - 当设置为0时,每分钟由后台进程执行一次刷新操作。 - 当设置为1时,每次事务完成时立即持久化到存储设备,提供最高级别的安全性,但可能会对运行效率造成影响。 - 当设置为2时,事务完成仅将日志写入日志文件,不强制执行磁盘刷新,由操作系统决定何时进行刷新,这样可能会在服务器异常时造成数据丢失。 - **Binlog**:二进制日志记录了所有更改数据库状态的操作,不包括查询和展示类操作,主要用于数据恢复、复制和审核。 Binlog存在Statement、Row和Mixed三种格式,其中Row格式因为记录了行级别变化,能够确保数据不丢失,因此被普遍推荐使用。 Binlog的写入过程redo log相似,先写入binlog cache,然后在事务提交时写入binlog文件。 `sync_binlog`参数控制了binlog何时写入磁盘,其设定方式`innodb_flush_log_at_t...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值