在 Dart 中的迭代器上创建 sum、min、max 属性
Posted
技术标签:
【中文标题】在 Dart 中的迭代器上创建 sum、min、max 属性【英文标题】:Creating sum, min, max properties on iterators in Dart 【发布时间】:2021-04-20 16:37:13 【问题描述】:我有以下内容,适用于int
s:
extension IterableInt on Iterable<int>
int get max => reduce(math.max);
int get min => reduce(math.min);
int get sum => reduce((a, b) => a + b);
我想让它更通用以包含小数并创建了这个:
extension IterableNum on Iterable<num>
num get max => reduce(math.max);
num get min => reduce(math.min);
num get sum => reduce((a, b) => a + b);
这些大多失败,使用以下测试:
void main(List<String> args)
print([1.2, 1.3, 5, 2.2].sum); //works
print([1.2, 1.3, 5, 2.2].max); //works
print([1.2, 1.3, 5, 2.2].min); //works
print([1.2, 1.3, 5.0, 2.2].sum); //does not work
print([1, 2, 3].max); //does not work
print([1, 2, 3].min); //does not work
print([1, 2, 3].sum); //does not work
decimal
案例返回的错误是:
type '(num, num) => num' is not a subtype of type '(double, double) => double' of 'combine'
int
类似,在消息中指的是int
而不是double
。
模式似乎是,如果列表中有int
s 和decimal
s 的混合,那就没问题了。如果全部为int
s 或全部为decimal
s,则失败。
我已经使用fold
重新定义了属性(并添加了multiply
),这很有效:
extension IterableNum on Iterable<num>
num get max => fold(first, math.max);
num get min => fold(first, math.min);
num get sum => fold(0, (a, b) => a + b);
num get multiply => fold(1, (a, b) => a * b);
谁能解释为什么IterableNum
的第一个版本(使用reduce
)不起作用?
【问题讨论】:
【参考方案1】:问题在于reduce
对其参数功能非常挑剔。
List<int>
的 reduce
需要 int Function(int, int)
类型的函数。没有其他的就足够了。如果你将这个List<int>
转换为List<num>
,你就会得到一个看起来像是希望得到num Function(num, num)
,但实际上需要int Function(int, int)
的东西。这两个都很难满足(你需要int Function(num, num)
,(a, b) => a + b
不是。
(这都是因为 Dart 类泛型是不安全协变的。List<int>
始终被认为是List<num>
的子类型,即使某些函数实际上在List<num>
上并不可用)。
使用fold
是一个很好的解决方案。它允许您指定与元素类型不同的返回类型。它还要求您以该类型的值开头,这就是为什么并非总是可以使用fold
而不是reduce
。
【讨论】:
你能澄清最后一句话吗?当不能你使用fold
而不是reduce
?
在某些情况下,您可能需要“减少”特殊对象列表,即没有中性元素开始的对象。在这里,您将max
实现为fold(first, math.max)
,但这只是因为看到first
两次并不重要。假设您必须找到“单词”列表的长度总和。开头没有“空词”,因此您不能使用fold
,因为您没有第一个参数。
谢谢 - 明白了。我已经恢复到@mezoni 的解决方案。【参考方案2】:
它不起作用,因为您创建了更高级别的列表(num
类型的子类)。
您的(扩展)代码应该更通用(更通用)。
喜欢这个代码:extension IterableNum<T extends num> on Iterable<T>
。
void main(List<String> args)
print([1.2, 1.3, 5, 2.2].runtimeType);
print([1.2, 1.3, 5.0, 2.2].runtimeType);
结果:
List<num>
List<double>
正确代码如下:
import 'dart:math' as math;
void main(List<String> args)
print([1.2, 1.3, 5, 2.2].runtimeType);
print([1.2, 1.3, 5.0, 2.2].runtimeType);
print([1.2, 1.3, 5, 2.2].sum); //works
print([1.2, 1.3, 5, 2.2].max); //works
print([1.2, 1.3, 5, 2.2].min); //works
print([1.2, 1.3, 5.0, 2.2].sum); // WORKS!!!
print([1, 2, 3].max); // WORKS!!!
print([1, 2, 3].min); // WORKS!!!
print([1, 2, 3].sum); // WORKS!!!
extension IterableNum<T extends num> on Iterable<T>
T get max => reduce(math.max);
T get min => reduce(math.min);
T get sum => reduce((a, b) => a + b as T);
结果:
List<num>
List<double>
9.7
5
1.2
9.7
3
1
6
【讨论】:
这行得通,我了解了更多关于泛型的知识:)runTimeType
很好地说明了这个问题。以上是关于在 Dart 中的迭代器上创建 sum、min、max 属性的主要内容,如果未能解决你的问题,请参考以下文章