我可以使用模式匹配在一个动态构造的本地元组上吗?

huangapple go评论70阅读模式
英文:

Can I use pattern matching on a dynamically constructed local tuple?

问题

这个示例代码无法编译:

record Thing(List<int> xs, List<int> ys);

int DoStuff(Thing thing)
{
    return (thing.xs, thing.ys) switch
    {
        xs.Count > 5 => xs.First(),
        ys.Count > 5 => ys.Last(),
        xs.Count + ys.Count > 5 => xs.First() + ys.Last(),
        (xs.Count > 0, ys.Count > 0) => Math.Max(xs.First(), ys.Last()),
        _ => -1
    };
}

C#(.Net 6或.Net 7)提供了一种语法来实现我在尝试的功能吗?还是这样推动了模式匹配语法的限制?

我以为使用{}语法(关系模式?)可以有所进展,但这仍然不起作用,因为捕获返回值超出了上下文。

return (thing.xs, thing.ys) switch
{
    { xs.Count: > 5 } => xs.First(),
    { ys.Count: > 5 } => ys.Last(),
    { xs.Count + ys.Count: > 5 } => xs.First() + ys.Last(),
    { xs.Count: > 0, ys.Count: > 0 } => Math.Max(xs.First(), ys.Last()),
    _ => -1
};

似乎我离成功越来越近,但只是通过反复尝试,而不真正理解语言语法。

英文:

This example code doesn't compile:

record Thing(List&lt;int&gt; xs, List&lt;int&gt; ys);

	int DoStuff(Thing thing)
	{
 		return (thing.xs, thing.ys) switch
 		{
  			xs.Count &gt; 5 =&gt; xs.First(),
  			ys.Count &gt; 5 =&gt; ys.Last(),
  			xs.Count + ys.Count &gt; 5 =&gt; xs.First()+ys.Last(),
  			(xs.Count &gt; 0, ys.Count &gt; 0) =&gt; Math.Max(xs.First(),ys.Last()),
  			_ =&gt; -1
 		}; 
	}

Does C# (.Net 6 or .Net 7) provide a syntax to do what I'm trying here or is this pushing the pattern matching syntax too far?

I thought I'd got somewhere using the {} syntax (relational patterns?) but this still doesn't work since capturing the return value is out of context.

return (thing.xs, thing.ys) switch
{
    {xs.Count: &gt; 5} =&gt; xs.First(),
    {ys.Count: &gt; 5} =&gt; ys.Last(),
    {xs.Count + ys.Count: &gt; 5} =&gt; xs.First()+ys.Last(),
    {xs.Count: &gt; 0 , ys.Count: &gt; 0) =&gt; Math.Max(xs.First(),ys.Last()),
    _ =&gt; -1
}; 

It seems like I'm getting closer, but only by trial and error without really understanding the language syntax.

答案1

得分: 3

你可以切换到包含计数的元组并使用元组模式。 (我将 asbs 重命名为 ab,因为 as 是保留关键字):

int DoStuff(Thing thing)
{
    return (thing.a.Count, thing.b.Count) switch {
        (> 5, _) => thing.a.Count,
        (_, > 5) => thing.b.Count,
        _ when thing.a.Count + thing.b.Count > 5 => thing.a.Count + thing.b.Count,
        (> 0, > 0) => Math.Max(thing.a.Count, thing.b.Count),
        _ => -1
    };
}

请注意,废弃的 _ 表示 "不重要"。例如,模式 (> 5, _) 意味着:元组的第一个项目 (thing.a.Count) 必须大于 5,第二个项目 (thing.b.Count) 无关紧要。换句话说,如果你切换到一个元组,你必须使用元组模式。

由于不能在模式中使用加法操作,我使用 when 子句引入了一个标准布尔条件。

这种方法的进一步改进是创建一个具有命名元组以进一步简化:

int DoStuff(Thing thing)
{
    var t = (ac: thing.a.Count, bc: thing.b.Count);
    return t switch {
        (> 5, _) => t.ac,
        (_, > 5) => t.bc,
        _ when t.ac + t.bc > 5 => t.ac + t.bc,
        (> 0, > 0) => Math.Max(t.ac, t.bc),
        _ => -1
    };
}

还有另一种可能性是使用嵌套属性模式的位置模式。位置模式有效,因为记录会自动生成一个 解构器。记录的解构器按照它们在主构造函数中定义的顺序呈现元素。记录 record Thing(List<int> a, List<int> b); 生成这个解构器:

public void Deconstruct(out List<int> a, out List<int> b)
{
    a = this.a;
    b = this.b;
}

使用位置模式:

int DoStuff2(Thing thing)
{
    return thing switch {
        ({ Count: > 5 }, _) => thing.a.Count,
        (_, { Count: > 5 }) => thing.b.Count,
        _ when thing.a.Count + thing.b.Count > 5 => thing.a.Count + thing.b.Count,
        ({ Count: > 0 }, { Count: > 0 }) => Math.Max(thing.a.Count, thing.b.Count),
        _ => -1
    };
}

这种方法允许您切换到任何属性,甚至多个属性。切换到 thing 时,这些都是有效的模式:

  • ({ Count: > 5, Capacity: < 100 }, _)
  • { a.Count: > 5 }
  • { a.Count: > 0, b.Count: > 0 }
    或者使用位置模式和嵌套列表模式相同
    ([_, ..], [_, ..])

因此,有许多不同的方法来制定模式。

另请参见:模式匹配概述

英文:

You could switch on a tuple containing the counts and use tuple patterns. (I renamed as and bs to a and b since as is a reserved keyword):

int DoStuff(Thing thing)
{
    return (thing.a.Count, thing.b.Count) switch {
        ( &gt; 5, _) =&gt; thing.a.Count,
        (_, &gt; 5) =&gt; thing.b.Count,
        _ when thing.a.Count + thing.b.Count &gt; 5 =&gt; thing.a.Count + thing.b.Count,
        ( &gt; 0, &gt; 0) =&gt; Math.Max(thing.a.Count, thing.b.Count),
        _ =&gt; -1
    };
}

Note that the discard _ means "does not matter". As an example, the pattern ( &gt; 5, _) means: The 1st item of the tuple (thing.a.Count) must be greater than 5 and the 2nd item (thing.b.Count) does not matter. I.e., if you switch on a tuple, you must use tuple patterns.

Since we cannot use an addition operation in a pattern, I used a when clause to introduce a standard Boolean condition.

A refinement of this approach is do create a named tuple for further simplification:

int DoStuff(Thing thing)
{
    var t = (ac: thing.a.Count, bc: thing.b.Count);
    return t switch {
        ( &gt; 5, _) =&gt; t.ac,
        (_, &gt; 5) =&gt; t.bc,
        _ when t.ac + t.bc &gt; 5 =&gt; t.ac + t.bc,
        ( &gt; 0, &gt; 0) =&gt; Math.Max(t.ac, t.bc),
        _ =&gt; -1
    };
}

Yet another possibility is to use a positional pattern with nested property patterns. The positional pattern works because records generate a Deconstructor automatically. A record's deconstructor presents the elements in the same order as they are defined in the primary constructor. The record record Thing(List&lt;int&gt; a, List&lt;int&gt; b); generates this deconstructor:

public void Deconstruct(out List&lt;int&gt; a, out List&lt;int&gt; b)
{
    a = this.a;
    b = this.b;
}

With positional pattern:

int DoStuff2(Thing thing)
{
    return thing switch {
        ( { Count: &gt; 5 }, _) =&gt; thing.a.Count,
        (_, { Count: &gt; 5 }) =&gt; thing.b.Count,
        _ when thing.a.Count + thing.b.Count &gt; 5 =&gt; thing.a.Count + thing.b.Count,
        ( { Count: &gt; 0 }, { Count: &gt; 0 }) =&gt; Math.Max(thing.a.Count, thing.b.Count),
        _ =&gt; -1
    };
}

This approach allows you to switch on any property and even on more than one property. Switching on thing these are valid patterns as well:

  • ( { Count: &gt; 5, Capacity: &lt; 100 }, _)
  • { a.Count: &gt; 5 }
  • { a.Count: &gt; 0, b.Count: &gt; 0 }
    or the same but with a positional pattern and nested list patterns
    ([_, ..], [_, ..])

So , there many different ways to formulate patterns.

See also: Pattern matching overview

huangapple
  • 本文由 发表于 2023年2月24日 01:52:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75548551.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定