Creating sum, min, max properties on iterators in Dart

社会主义新天地 提交于 2021-01-28 08:55:21

问题


I have the following, which works for 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);
}

I wanted to make this more general to include decimals and created this:

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);
}

These mostly fail, using the following tests:

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
}

The error returned for the decimal case is:

type '(num, num) => num' is not a subtype of type '(double, double) => double' of 'combine'

It's similar for int, referring to int instead of double in the message.

The pattern seems to be that if there is a mixture of ints and decimals in the list then it's fine. If it's all ints or all decimals, it fails.

I've redefined the properties (and added multiply) using fold and this works:

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);
}

Can someone explain why the first version of IterableNum (using reduce) does not work?


回答1:


It does not work because you create lists of higher levels (subclasses of num type).

Your (extension) code should be more generic (more universal).
Like this code: 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);
}

Result:

List<num>
List<double>

Correct code as follow:

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);
}

Result:

List<num>
List<double>
9.7
5
1.2
9.7
3
1
6



回答2:


The issue is that reduce is very picky about its parameter function.

A List<int>'s reduce requires a function of type int Function(int, int). Nothing else will suffice. If you cast this List<int> to List<num>, you have something which seems like it expects to get a num Function(num, num), but actually requires an int Function(int, int). That's very hard to satisfy both of these (you need an int Function(num, num), which (a, b) => a + b is not. (This is all because Dart class generics is unsafely covariant. A List<int> is always considered a subtype of List<num>, even though some of the functions aren't actually usable at List<num>).

Use fold is a good solution. It allows you to specify a return type different from the element type. It also requires you to have a value of that type to begin with, which is why it isn't always possible to use fold instead of reduce.



来源:https://stackoverflow.com/questions/65739652/creating-sum-min-max-properties-on-iterators-in-dart

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!