英文:
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<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
};
}
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: > 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
};
It seems like I'm getting closer, but only by trial and error without really understanding the language syntax.
答案1
得分: 3
你可以切换到包含计数的元组并使用元组模式。 (我将 as
和 bs
重命名为 a
和 b
,因为 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 {
( > 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
};
}
Note that the discard _
means "does not matter". As an example, the pattern ( > 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 {
( > 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
};
}
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<int> a, List<int> b);
generates this deconstructor:
public void Deconstruct(out List<int> a, out List<int> b)
{
a = this.a;
b = this.b;
}
With positional pattern:
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
};
}
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: > 5, Capacity: < 100 }, _)
{ a.Count: > 5 }
{ a.Count: > 0, b.Count: > 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论