C#相当于Python的步骤范围?

时间:2017-05-10 15:37:46

标签: c# python range ienumerable

是否有一个C#等价的Python rangestep

文档

  

对于正面step,范围r的内容由公式r[i] = start + step*i确定,其中i >= 0r[i] < stop

     

对于否定step,范围内容仍由公式r[i] = start + step*i确定,但约束为i >= 0r[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]

3 个答案:

答案 0 :(得分:4)

我们可以实现一个static实用程序类来处理这个问题。

为了完整性,此解决方案模仿Python对一个参数range)的stop行为,两个参数(startstop)和三个参数({{1 }},startstop):

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

答案 1 :(得分:3)

我会选择两种方法实现。第一个用于参数验证并提供默认值:

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表达式成员简化代码:

public static IEnumerable<int> Range(int stop) => RangeIterator(0, stop, 1);

您也可以使用C#6创建static methods available in global scope。假设具有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-case Python:)

答案 2 :(得分:0)

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

可能有更聪明的方法来做到这一点。