C# 等同于 Python 的步长范围?
C# equivalent of Python's range with step?
是否有 C# 等效于 Python 的 range 和 step
?
文档:
For a positive step
, the contents of a range r
are determined by the formula r[i] = start + step*i
where i >= 0
and r[i] < stop
.
For a negative step
, the contents of the range are still determined by the formula r[i] = start + step*i
, but the constraints are i >= 0
and r[i] > stop
.
示例:
>>> list(range(0, 10, 3))
[0, 3, 6, 9]
>>> list(range(0, -10, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
我们可以实现一个 static
实用程序 class 来处理这个问题。
为了完整起见,此解决方案模仿 Python 的 range
一个参数 (stop
)、两个参数 (start
、stop
) 的行为, 以及三个参数 (start
, stop
, step
):
using System;
using System.Collections.Generic;
public static class EnumerableUtilities
{
public static IEnumerable<int> RangePython(int start, int stop, int step = 1)
{
if (step == 0)
throw new ArgumentException("Parameter step cannot equal zero.");
if (start < stop && step > 0)
{
for (var i = start; i < stop; i += step)
{
yield return i;
}
}
else if (start > stop && step < 0)
{
for (var i = start; i > stop; i += step)
{
yield return i;
}
}
}
public static IEnumerable<int> RangePython(int stop)
{
return RangePython(0, stop);
}
}
使用步骤示例:
foreach (var i in EnumerableUtilities.RangePython(0, 10, 3))
{
Console.WriteLine(i);
}
输出:
0
3
6
9
我会采用两种方法实现。第一个用于参数验证和提供默认值:
public static IEnumerable<int> Range(int start, int stop, int step = 1)
{
if (step == 0)
throw new ArgumentException(nameof(step));
return RangeIterator(start, stop, step);
}
这是延迟执行的迭代器所必需的。否则,在执行迭代器之前,您不会验证参数。在您获得迭代器引用后很长一段时间内可能会发生这种情况。和迭代器本身(实际上在 C# 7 中你可以使用 local function 而不是创建单独的方法):
private static IEnumerable<int> RangeIterator(int start, int stop, int step)
{
int x = start;
do
{
yield return x;
x += step;
if (step < 0 && x <= stop || 0 < step && stop <= x)
break;
}
while (true);
}
要实现 Python 的范围行为,我们还需要一个只接受 stop 参数的方法。我们可以使用 C# 6 expression-bodied member 来简化代码:
public static IEnumerable<int> Range(int stop) => RangeIterator(0, stop, 1);
您也可以使用 C# 6 创建 static methods available in global scope。假设 class with Range 方法描述被命名为 PythonUtils:
using static YourNamespace.PythonUtils;
在代码中的用法将类似于
foreach(var i in Range(0, 10, 3))
Print(i);
您也可以使用默认值
Range(0, 10, 3) // [0,3,6,9]
Range(4, -3, -1) // [4,3,2,1,0,-1,-2]
Range(5) // [0,1,2,3,4]
Range(2, 5) // [2,3,4]
看起来像 Pascal 大小写 Python :)
受 Sergey 的解决方案启发并模仿 python 的范围。
static class Utils
{
public static IEnumerable<int> Range(int start, int stop, int step = 1)
{
if (step == 0)
throw new ArgumentException(nameof(step));
while (step > 0 && start < stop || step < 0 && start > stop)
{
yield return start;
start += step;
}
}
public static IEnumerable<int> Range(int stop) => Range(0, stop, 1);
}
void Main()
{
var ranges = new IEnumerable<int>[] {
Utils.Range(0, 10, 3), // [0,3,6,9]
Utils.Range(4, -3, -1), // [4,3,2,1,0,-1,-2]
Utils.Range(5), // [0,1,2,3,4]
Utils.Range(2, 5), // [2,3,4]
Utils.Range(1, -3, 2), // []
};
Array.ForEach(ranges, Console.WriteLine);
}
可以对 while 循环进行小的优化以获得更好的性能,我喜欢这样。
if (step > 0) while (start < stop)
{
yield return start;
start += step;
}
else while (start > stop)
{
yield return start;
start += step;
}
可能有更聪明的方法来做到这一点。
是否有 C# 等效于 Python 的 range 和 step
?
文档:
For a positive
step
, the contents of a ranger
are determined by the formular[i] = start + step*i
wherei >= 0
andr[i] < stop
.For a negative
step
, the contents of the range are still determined by the formular[i] = start + step*i
, but the constraints arei >= 0
andr[i] > stop
.
示例:
>>> list(range(0, 10, 3))
[0, 3, 6, 9]
>>> list(range(0, -10, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
我们可以实现一个 static
实用程序 class 来处理这个问题。
为了完整起见,此解决方案模仿 Python 的 range
一个参数 (stop
)、两个参数 (start
、stop
) 的行为, 以及三个参数 (start
, stop
, step
):
using System;
using System.Collections.Generic;
public static class EnumerableUtilities
{
public static IEnumerable<int> RangePython(int start, int stop, int step = 1)
{
if (step == 0)
throw new ArgumentException("Parameter step cannot equal zero.");
if (start < stop && step > 0)
{
for (var i = start; i < stop; i += step)
{
yield return i;
}
}
else if (start > stop && step < 0)
{
for (var i = start; i > stop; i += step)
{
yield return i;
}
}
}
public static IEnumerable<int> RangePython(int stop)
{
return RangePython(0, stop);
}
}
使用步骤示例:
foreach (var i in EnumerableUtilities.RangePython(0, 10, 3))
{
Console.WriteLine(i);
}
输出:
0
3
6
9
我会采用两种方法实现。第一个用于参数验证和提供默认值:
public static IEnumerable<int> Range(int start, int stop, int step = 1)
{
if (step == 0)
throw new ArgumentException(nameof(step));
return RangeIterator(start, stop, step);
}
这是延迟执行的迭代器所必需的。否则,在执行迭代器之前,您不会验证参数。在您获得迭代器引用后很长一段时间内可能会发生这种情况。和迭代器本身(实际上在 C# 7 中你可以使用 local function 而不是创建单独的方法):
private static IEnumerable<int> RangeIterator(int start, int stop, int step)
{
int x = start;
do
{
yield return x;
x += step;
if (step < 0 && x <= stop || 0 < step && stop <= x)
break;
}
while (true);
}
要实现 Python 的范围行为,我们还需要一个只接受 stop 参数的方法。我们可以使用 C# 6 expression-bodied member 来简化代码:
public static IEnumerable<int> Range(int stop) => RangeIterator(0, stop, 1);
您也可以使用 C# 6 创建 static methods available in global scope。假设 class with Range 方法描述被命名为 PythonUtils:
using static YourNamespace.PythonUtils;
在代码中的用法将类似于
foreach(var i in Range(0, 10, 3))
Print(i);
您也可以使用默认值
Range(0, 10, 3) // [0,3,6,9]
Range(4, -3, -1) // [4,3,2,1,0,-1,-2]
Range(5) // [0,1,2,3,4]
Range(2, 5) // [2,3,4]
看起来像 Pascal 大小写 Python :)
受 Sergey 的解决方案启发并模仿 python 的范围。
static class Utils
{
public static IEnumerable<int> Range(int start, int stop, int step = 1)
{
if (step == 0)
throw new ArgumentException(nameof(step));
while (step > 0 && start < stop || step < 0 && start > stop)
{
yield return start;
start += step;
}
}
public static IEnumerable<int> Range(int stop) => Range(0, stop, 1);
}
void Main()
{
var ranges = new IEnumerable<int>[] {
Utils.Range(0, 10, 3), // [0,3,6,9]
Utils.Range(4, -3, -1), // [4,3,2,1,0,-1,-2]
Utils.Range(5), // [0,1,2,3,4]
Utils.Range(2, 5), // [2,3,4]
Utils.Range(1, -3, 2), // []
};
Array.ForEach(ranges, Console.WriteLine);
}
可以对 while 循环进行小的优化以获得更好的性能,我喜欢这样。
if (step > 0) while (start < stop)
{
yield return start;
start += step;
}
else while (start > stop)
{
yield return start;
start += step;
}
可能有更聪明的方法来做到这一点。