创建一个从0到 N 的列表

如何轻松创建省道中的连续整数范围? 例如:

// throws a syntax error :)
var list = [1..10];
97274 次浏览

据我所知,在达特没有与之相当的土著方法。但是,您可以创建自己的 Range类,或者使用 https://pub.dartlang.org/packages/range,如果您不介意依赖项的话。

Olov Lassus 曾经写过关于实现自己的 Range 类的 一篇文章

我刚想到的一个更好的办法:

Iterable<int> range(int low, int high) sync* {
for (int i = low; i < high; ++i) {
yield i;
}
}


void main() {
for(final i in range(1, 20)) {
print(i);
}
}

你可使用 生成构造函数:

var list = new List<int>.generate(10, (i) => i + 1);

你也可以选择使用发电机:

/// the list of positive integers starting from 0
Iterable<int> get positiveIntegers sync* {
int i = 0;
while (true) yield i++;
}
void main() {
var list = positiveIntegers
.skip(1)   // don't use 0
.take(10)  // take 10 numbers
.toList(); // create a list
print(list);   // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}

在 Dart 2.3之后,你可以使用 收集:

var list = [for (var i = 1; i <= 10; i++) i];

您还可以使用 Dart 的 Iterable.generate 函数来创建0.n-1之间的范围

var list = Iterable<int>.generate(10).toList()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

使用 < href = “ https://github.com/dart-lang/sdk/blob/3f477a6218654f377b863f0d831c2334b20e45db/CHANGELOG.md # Collection-for”rel = “ noReferrer”> dart 2.3.0 :

var list = [for(var i=0; i<10; i+=1) i];

我一直在使用 AlexandreArdhuin 的修改版本,它试图模仿 python编辑: 发现可选的位置参数是一个东西更新代码如下提供的 range ()

range(int stop, {int start: 0, int step: 1}){
if (step == 0)
throw Exception("Step cannot be 0");


return start < stop == step > 0
? List<int>.generate(((start-stop)/step).abs().ceil(), (int i) => start + (i * step))
: [];
}

示例用法:

range(16, start:-5, step: 8);
// [-5, 3, 11]
range(5);
// [0, 1, 2, 3, 4]

不幸的是,我没有完全模仿 python 的简单语法(range (start,stop [ ,step ]) ,因为 dart 没有运算符重载或可选的位置参数。

另一个选择是使用列表内涵,类似于玛丽安的解决方案

listCompRange(int start, int stop, int step) {
if (step == 0)
throw Exception("Step cannot be 0");
if (start == stop)
return [];
bool forwards = start < stop;
return forwards == step > 0
? forwards
? [for (int i = 0; i*step < stop-start; i++) start + (i * step)]
: [for (int i = 0; i*step > stop-start; i++) start + (i * step)]
: [];
}

示例用法:

listCompRange(0, 5, 1);
// [0, 1, 2, 3, 4]

我用以下方法对这两个选项进行了基准测试

benchMarkRange(){
List<List<int>> temp = List<List<int>>();
Stopwatch timer = Stopwatch();
timer.start();
for (int i = 0; i < 500; i++){
temp.add(range(-30, start: -10, step: -2));
}
timer.stop();
print("Range function\n${timer.elapsed}\n");
return temp;
}


benchMarkListComprehension(){
List<List<int>> temp = List<List<int>>();
Stopwatch timer = Stopwatch();
timer.start();
for (int i = 0; i < 500; i++){
temp.add(listCompRange(-10, -30, -2));
}
timer.stop();
print("List comprehension\n${timer.elapsed}\n");
return temp;
}

这些结果稍微有利于发电机。

Range function
0:00:00.011953
0:00:00.011558
0:00:00.011473
0:00:00.011615


List comprehension
0:00:00.016281
0:00:00.017403
0:00:00.017496
0:00:00.016878

然而,当我将函数的生成步骤从 -10改为 -30时,结果略微有利于列表内涵。

List comprehension
0:00:00.001352
0:00:00.001328
0:00:00.001300
0:00:00.001335


Range function
0:00:00.001371
0:00:00.001466
0:00:00.001438
0:00:00.001372

使用位置参数而不是命名参数更新代码

range(int a, [int stop, int step]) {
int start;


if (stop == null) {
start = 0;
stop = a;
} else {
start = a;
}


if (step == 0)
throw Exception("Step cannot be 0");


if (step == null)
start < stop
? step = 1    // walk forwards
: step = -1;  // walk backwards


// return [] if step is in wrong direction
return start < stop == step > 0
? List<int>.generate(((start-stop)/step).abs().ceil(), (int i) => start + (i * step))
: [];
}

用法: range (int a,[ int stop,int step ])

如果没有包含 stop,那么将变为 stop,start 将默认为0 如果 a 和 stop 都被提供,则 a 成为 start 如果没有提供步骤将默认为1或 -1,这取决于是开始还是停止较大

range(4);
// [0, 1, 2, 3]
range(4, 10);
// [4, 5, 6, 7, 8, 9]
range(4, 10, 2);
// [4, 6, 8]
range(-4);
// [0, -1, -2, -3]
range(10, 4);
// [10, 9, 8, 7, 6, 5]
range(10,10);
// []
range(1, 2, -1);
// []
range(x, y, 0);
// Exception

箭筒包中定义了许多类似 Python 的迭代器。

例如,使用 range()函数:

import 'package:quiver/iterables.dart';


print(range(10).toList().toString());

产出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

它在 for循环中也能正常工作:

for (var i in range(1, 11))
print('$i');

还提供了许多其他的 有用的迭代器

@ 木卫三:

这里有一个简单的 a.upTo (b)解决方案:

extension RangeExtension on int {
List<int> upTo(int maxInclusive) =>
[for (int i = this; i <= maxInclusive; i++) i];
}

或可选步长:


extension RangeExtension on int {
List<int> upTo(int maxInclusive, {int stepSize = 1}) =>
[for (int i = this; i <= maxInclusive; i += stepSize) i];
}

像这样使用最后一个:

void main() {
// [5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50]
print(5.upTo(50, stepSize: 3));
}

我的方法可能不是创建 N 的最优雅的解决方案。M 列表,但是它非常有用,并且实现起来非常简单:

void main() {
generateN2MList(2, 8);
generateN2MList(4, 10);
generateN2MList(1, 3);
generateN2MList(0, 13, true);
}


void generateN2MList(int n, int m, [bool excludeLimits = false]) {
final diff = m - n;
final times = excludeLimits ? diff - 1 : diff + 1;
final startingIdx = excludeLimits ? n + 1 : n;
List<int> generated =
List.generate(times, (i) => startingIdx + i);


print(generated);
}


如果我们尝试一下,结果是这样的:

[2, 3, 4, 5, 6, 7, 8]
[4, 5, 6, 7, 8, 9, 10]
[1, 2, 3]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

这是 DartPad 如果你想玩的话。

void main() {
print(List.generate(10, (index) => index));
}




[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

List.generate返回新列表

如果你想创建真正的范围,然后能够比较不同的范围与其他您可以考虑的 Range _ type

  final july = DateTimeRange.parse('[2022-07-01, 2022-08-01)');
final scheduleDate1 = DateTime(2022, 07, 02);
final scheduleDate2 = DateTime(2022, 08, 07);
final workingDays = DateTimeRange.parse('[2022-07-20, 2022-08-15)');
print('Is scheduleDate1 in July? ${july.containsElement(scheduleDate1)}');
print('Is scheduleDate2 in July? ${july.containsElement(scheduleDate2)}');
print('Is workingDays overlaps? ${july.overlap(workingDays)}');
print('workingDays intersection: ${july.intersection(workingDays)}');
print('workingDays union: ${july.union(workingDays)}');
print('july difference workingDays: ${july.difference(workingDays)}');

上面的方法确实很慢。基于 List的方法消耗额外的内存和时间,因为它实际上构造了一个完整的列表,而基于 yield的方法被测量为(例如)比 for(var i=0;i<N;++i)慢2倍

https://github.com/dart-lang/sdk/issues/50280#issuecomment-1288679644,@lrhn 提供了一个与 for(var i=0;i<N;++i)一样快的 range。代码:

class Range extends Iterable<int> {
final int start, end, step;


@pragma('dart2js:tryInline')
@pragma('vm:prefer-inline')
Range(this.start, this.end, [this.step = 1]);


@pragma('dart2js:tryInline')
@pragma('vm:prefer-inline')
@override
Iterator<int> get iterator => RangeIterator(start, end, step);
}


class RangeIterator implements Iterator<int> {
int _current;
final int _end, _step;


@pragma('dart2js:tryInline')
@pragma('vm:prefer-inline')
RangeIterator(int start, this._end, this._step) : _current = start - _step;


@override
@pragma('dart2js:tryInline')
@pragma('vm:prefer-inline')
bool moveNext() {
_current += _step;
return _current < _end;
}


@override
@pragma('dart2js:tryInline')
@pragma('vm:prefer-inline')
int get current => _current;
}


@pragma('dart2js:tryInline')
@pragma('vm:prefer-inline')
Iterable<int> range(int startOrLength, [int? end, int step = 1]) {
assert(step > 0);
int start = startOrLength;
if (end == null) {
end = start; // ignore: parameter_assignments
start = 0;
}
return Range(start, end, step);
}


void main() {
test('range', () {
expect(range(3), [0, 1, 2]);
expect(range(1, 4), [1, 2, 3]);
expect(range(-2, 2), [-2, -1, 0, 1]);


expect(range(0), <int>[]);
expect(range(5, 5), <int>[]);
expect(range(-1), <int>[]);
expect(range(5, 4), <int>[]);
});
}