在 Dart 中的迭代器上创建 sum、min、max 属性

Posted

技术标签:

【中文标题】在 Dart 中的迭代器上创建 sum、min、max 属性【英文标题】:Creating sum, min, max properties on iterators in Dart 【发布时间】:2021-04-20 16:37:13 【问题描述】:

我有以下内容,适用于ints:

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

模式似乎是,如果列表中有ints 和decimals 的混合,那就没问题了。如果全部为ints 或全部为decimals,则失败。

我已经使用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&lt;int&gt;reduce 需要 int Function(int, int) 类型的函数。没有其他的就足够了。如果你将这个List&lt;int&gt; 转换为List&lt;num&gt;,你就会得到一个看起来像是希望得到num Function(num, num),但实际上需要int Function(int, int) 的东西。这两个都很难满足(你需要int Function(num, num)(a, b) =&gt; a + b 不是。 (这都是因为 Dart 类泛型是不安全协变的。List&lt;int&gt; 始终被认为是List&lt;num&gt; 的子类型,即使某些函数实际上在List&lt;num&gt; 上并不可用)。

使用fold 是一个很好的解决方案。它允许您指定与元素类型不同的返回类型。它还要求您以该类型的值开头,这就是为什么并非总是可以使用fold 而不是reduce

【讨论】:

你能澄清最后一句话吗?当不能你使用fold而不是reduce 在某些情况下,您可能需要“减少”特殊对象列表,即没有中性元素开始的对象。在这里,您将max 实现为fold(first, math.max),但这只是因为看到first 两次并不重要。假设您必须找到“单词”列表的长度总和。开头没有“空词”,因此您不能使用fold,因为您没有第一个参数。 谢谢 - 明白了。我已经恢复到@mezoni 的解决方案。【参考方案2】:

它不起作用,因为您创建了更高级别的列表(num 类型的子类)。

您的(扩展)代码应该更通用(更通用)。 喜欢这个代码:extension IterableNum&lt;T extends num&gt; on Iterable&lt;T&gt;

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 属性的主要内容,如果未能解决你的问题,请参考以下文章

如何在迭代器上循环?

BeautifulSoup 在迭代器上执行 find()

Power BI DAX函数のSUMX函数

在 jQuery 日期选择器上收听 onChange?

AtCoder Grand Contest 039 F: Min Product Sum

AtCoder Grand Contest 039 F: Min Product Sum