【问题标题】:Simplest implementation of ClosestIndexOfClosestIndexOf 的最简单实现
【发布时间】:2015-06-05 23:40:24
【问题描述】:

我想编写一个 C# 字符串扩展方法 ClosestIndexOf(char, index),它可以让我在提供的索引周围的字符串中找到最接近的某个字符的索引。

让我们用我的输入字符串检查一些示例:

0         1         2         3         4         5    
01234567890123456789012345678901234567890123456789012345
--------------------------------------------------------
Lorem ipsum dolor sit amet, consectetur adipiscing elit.

输入字符串长度为 56(我添加了以 0 开头的索引位置。

示例结果调用:

input.ClosestIndexOf(' ', 30); // 27
input.ClosestIndexOf(' ', 35); // 39
input.ClosestIndexOf(' ', 50); // 50
input.ClosestIndexOf(' ', 19); // 17 & 21 have same offset, return 21
input.ClosestIndexOf(' ', 60); // OutOfRangeException
input.ClosestIndexOf('x', 30); // -1

到目前为止我已经写了这个,但它需要更多的测试,而且很丑陋,条件太多。

// index is out of range
if (index > value.Length)
    throw new ArgumentOutOfRangeException();

// get closest index below and above specified "index" position
int below = result.LastIndexOf('-', index - 1);
int above = result.IndexOf('-', index);

// followed by conditions

我希望这个问题或多或少是一个数学问题/表达式,以便我可以避免条件并使其更简单。

This is initial code 供您使用。正如您从结果中看到的那样,当发现特定字符上下或两者都没有时,我的初始代码有效。但是如果每个条件都具有值-1,我将不得不引入其他条件。我没有添加这些,因为这正是我想要优化的。

您会对初始代码进行哪些优化,以使其更短、性能更好并且条件更少?

【问题讨论】:

  • @Robert:我不确定index >= value.Length...我认为应该是index > value.Length"X".IndexOf('X', 1) 是 -1(而 LastIndexOf 抛出)。如果你改变它,那么你可以删除if (...),然后先移动IndexOf
  • 作为旁注,我不会那样做代码。如所写,如果您有“0123456789”并且您从 char 9 的索引 8 开始执行最接近,则将检查字符串 01234567,而您可以在两步后停止(啊……我不知道怎么解释)
  • @xanatos:你是对的。我在 DotNetFiddle 上的实际代码示例使用了大于比较。让我对我的问题进行相同的编辑。是的,你是对的我不知道你在第二条评论中的意思

标签: c# optimization conditional-statements


【解决方案1】:

获取您的搜索字符的所有出现次数以及该出现次数的found_index

计算您的search_indexfound_index 之间的距离。

  d = abs(s - f);

以最小的距离保持发生。

【讨论】:

  • 那不是真正的优化是吗?假设你有一个很长的字符串。枚举所有匹配项会浪费资源,同时仅搜索某个索引上方和下方最近的索引,无论字符串长度如何,该索引始终使用相同数量的资源。
【解决方案2】:

可能不是 最好的方法,但这应该避免大多数不必要的检查。

public static int ClosestIndexOf(this string input, char c, int index)
{
    if (input == null)
    {
        throw new ArgumentNullException("input");
    }
    if (index < 0 || index >= input.Length)
    {
        throw new ArgumentOutOfRangeException("index");
    }

    var lowerInRange = true;
    var higherInRange = true;

    for (var i = 0; i < input.Length; i++)
    {
        if (lowerInRange)
        {
            var lower = index - i;
            lowerInRange = lower >= 0;
            if (lowerInRange && input[lower] == c)
            {
                return lower;
            }
        }

        if (higherInRange)
        {
            var higher = index + i;
            higherInRange = higher < input.Length;
            if (higherInRange && input[higher] == c)
            {
                return higher;
            }
        }

        if (!lowerInRange && !higherInRange)
        {
            break;
        }
    }
    return -1;
}

【讨论】:

  • 从索引中迭代并在其中一个匹配时立即返回非常聪明。尽管当 high 和 lower 都在与 index 相同的偏移量处匹配时,您的方法总是返回较低的索引。我正在做相反的事情,但这可以通过切换 lowerInRangehigherInRange is 语句的顺序来轻松完成。
【解决方案3】:

这应该易于理解、高效且正确:')

public static int ClosestIndexOf(this string input, char c, int startIndex)
{
    var inputLength = input.Length;
    if (inputLength == 0)
        return -1;

    if (startIndex < 0 || startIndex >= inputLength)
        throw new ArgumentOutOfRangeException("startIndex");

    int leftIndex = startIndex;
    int rightIndex = startIndex;

    bool canCheckLeftIndex = true;
    bool canCheckRightIndex = true;

    do
    {
        if (canCheckLeftIndex && input[leftIndex] == c) return leftIndex;
        if (canCheckRightIndex && input[rightIndex] == c) return rightIndex;

        leftIndex--;
        rightIndex++;

        canCheckLeftIndex = leftIndex >= 0;
        canCheckRightIndex = rightIndex < inputLength;
    } while (canCheckLeftIndex || canCheckRightIndex);

    return -1;
}

您的初始代码:不要做任何数学技巧/尽量聪明,我不想在生产中看到该代码。很难看出你的代码是正确的。

【讨论】:

  • Math.Max 可以很容易地被内联 if 语句替换:result = above &lt; 0 ? below : above
  • 您的代码实际上是@Corak 代码的缩短版本,与您的代码相比,他使用了冗长的if 代码块,并执行for 循环,其中do-while 更合适
【解决方案4】:

我已经消除了尽可能多的条件,同时保持整个代码的可维护性和易读性,这也是主要目标之一,因为维护不可读的代码与从头开始开发一样糟糕。

public static int ClosestIndexOf(this string input, string value, int startIndex)
{
    // get closest index below and above specified "startIndex" position
    int above = input.IndexOf(value, startIndex);
    int below = input.LastIndexOf(value, startIndex);

    int middle = (below + above) / 2;

    int result;
    if (middle > startIndex)
        result = below == -1 ? above : below;
    else
        result = above == -1 ? below : above;

    return result;
}

我已经删除了无效的 startIndex 检查,因为 IndexOfLastIndexOf 已经这样做了。其他所有内容都非常易读易懂。

【讨论】:

  • 对于以前没有遇到过这个问题的人来说,这是不可能轻松阅读的(mid 嗯?)。该代码应该让我相信它只适用于阅读一次。只是我的友好意见。保重。
  • @ChrisEelmaa:我将mid 重命名为middle,因此没有歧义。我纯粹用它来巧妙地确定哪个索引(abovebelow)更接近startIndex。真的很难理解吗?好消息是,我现在可以获得字符串的最接近索引,而不仅仅是特定字符。我可以使用它来确定最近的段落结尾——在我想缩短的一些 HTML 字符串中搜索&lt;/p&gt;。搜索单个字符会使这变得相当困难。
  • @RobertKoritnik,当前实现不正确:string test = "012345"; ClosestIndexOf(test, "5", 1); // returns -1, not 5
  • @ASh:谢谢。这实际上是一个错误,我必须解决它。
猜你喜欢
  • 2010-11-09
  • 2010-09-20
  • 2011-02-16
  • 1970-01-01
  • 1970-01-01
  • 2010-12-01
  • 2012-02-15
  • 2014-07-02
  • 1970-01-01
相关资源
最近更新 更多