Любой способ улучшить этот метод среза строки? - PullRequest
9 голосов
/ 24 ноября 2010

Я написал это строковое расширение некоторое время назад, и я на самом деле получаю от него немного пользы.

public static string Slice(this string str, int? start = null, int? end = null, int step = 1)
{
    if (step == 0) throw new ArgumentException("Step cannot be zero.", "step");

    if (start == null)
    {
        if (step > 0) start = 0;
        else start = str.Length - 1;
    }
    else if (start < 0)
    {
        if (start < -str.Length) start = 0;
        else start += str.Length;
    }
    else if (start > str.Length) start = str.Length;

    if (end == null)
    {
        if (step > 0) end = str.Length;
        else end = -1;
    }
    else if (end < 0)
    {
        if (end < -str.Length) end = 0;
        else end += str.Length;
    }
    else if (end > str.Length) end = str.Length;

    if (start == end || start < end && step < 0 || start > end && step > 0) return "";
    if (start < end && step == 1) return str.Substring((int)start, (int)(end - start));

    int length = (int)(((end - start) / (float)step) + 0.5f);
    var sb = new StringBuilder(length);
    for (int i = (int)start, j = 0; j < length; i += step, ++j)
        sb.Append(str[i]);
    return sb.ToString();
}

Так как оно сейчас во всех моих проектах, мне интересно, смогу ли ясделали это лучше.Более эффективен, или это приведет к неожиданным результатам в любом случае?


Slice.Он работает как нотация массива Python.

 "string"[start:end:step]

Во многих других языках тоже есть что-то подобное.string.Slice(1) эквивалентно string.Substring(1).string.Substring(1,-1) обрезает первый и последний символ.string.Substring(null,null,-1) перевернет строку.string.Substring(step:2) вернет строку с каждым другим символом ... также похожим на фрагмент JS , но с дополнительным аргументом.


Повторно пересмотрен на основе ваших предложений:

public static string Slice(this string str, int? start = null, int? end = null, int step = 1)
{
    if (step == 0) throw new ArgumentException("Step size cannot be zero.", "step");

    if (start == null) start = step > 0 ? 0 : str.Length - 1;
    else if (start < 0) start = start < -str.Length ? 0 : str.Length + start;
    else if (start > str.Length) start = str.Length;

    if (end == null) end = step > 0 ? str.Length : -1;
    else if (end < 0) end = end < -str.Length ? 0 : str.Length + end;
    else if (end > str.Length) end = str.Length;

    if (start == end || start < end && step < 0 || start > end && step > 0) return "";
    if (start < end && step == 1) return str.Substring(start.Value, end.Value - start.Value);

    var sb = new StringBuilder((int)Math.Ceiling((end - start).Value / (float)step));
    for (int i = start.Value; step > 0 && i < end || step < 0 && i > end; i += step)
        sb.Append(str[i]);
    return sb.ToString();
}

Ответы [ 3 ]

2 голосов
/ 24 ноября 2010

Если у вас много тестовых случаев, то обнаружение неожиданных результатов не должно быть проблемой, если вы хотите экспериментировать с различными реализациями.

С точки зрения API я бы рассмотрел необязательные аргументы , а не обнуляемые целые числа.

Обновление

Внимательно прочитав код, я вижу, что присвоение значениям "start" и "end" значения null имеет особое значение, когда учитывается "step", поэтому они не могут быть представлены как необязательные параметры int однако они могут быть необязательными параметрами.

Если присмотреться к коду более внимательно, он станет немного странным API, поскольку значения отдельных параметров влияют друг на друга. Мой предыдущий комментарий ссылается на это. Вы действительно должны знать реализацию, чтобы решить это, как правило, не очень хороший аспект API. И, возможно, затрудняет чтение.

Я вижу, как «шаг» может быть использован для обращения строки, что потенциально полезно. Но разве метод обратного расширения не будет лучше для этого? Гораздо более читабельный и менее быстрый умственный удар.

1 голос
/ 25 ноября 2010

Когда я спрашиваю Python для "abcdefghijklmn"[::6], он возвращает 'agm', но когда я запрашиваю у вашей функции "abcdefghijklmn".Slice(step:6), он возвращает "ag".

Я бы порекомендовал удалить неправильный length расчет и просто выполнить ваш цикл следующим образом:

var sb = new StringBuilder((end - start).Value / step);
for (int i = start.Value; step > 0 && i < end || step < 0 && i > end; i += step)
    sb.Append(str[i]);
1 голос
/ 24 ноября 2010

Я могу видеть 3 вещи, очень незначительный

изменить внутренний, если на троичный как

        if (start == null)
        {
            start = step > 0 ? 0 : str.Length - 1;
        }
        else if (start < 0)
        {
            start = start < -str.Length ? 0 : str.Length + start;
        }
        else if (start > str.Length) 
            start = str.Length; 

возможно изменить (int) int? в int.Value

изменить

   var sb = new StringBuilder(length);

в

   StringBuilder sb = new StringBuilder(length);

и большойвопрос в том, если он делает то, что ему нужно, зачем его исправлять?


обновление, чтобы показать, как это сделать с помощью LINQ, намного медленнее (есть ли способ ускорить его??)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Stopwatch sw;
                string str;

                sw = Stopwatch.StartNew();
                for (int i = 0; i < 1000000; i++)
                    str = "Step cannot be zero.".Slice(null, null, -3, true);
                sw.Stop();
                Console.WriteLine("LINQ " + sw.Elapsed.TotalSeconds.ToString("0.#######") + " seconds");

                sw = Stopwatch.StartNew();
                for (int i = 0; i < 1000000; i++)
                    str = "Step cannot be zero.".Slice(null, null, -3, false);
                sw.Stop();
                Console.WriteLine("MANUAL " + sw.Elapsed.TotalSeconds.ToString("0.#######") + " seconds");

                Console.ReadLine();
            }
        }

       static class  test
        {
            public static string Slice(this string str, int? start, int? end, int step, bool linq)
            {
                if (step == 0) throw new ArgumentException("Step cannot be zero.", "step");

                if (linq)
                {

                    if (start == null) start = 0;
                    else if (start > str.Length) start = str.Length;

                    if (end == null) end = str.Length;
                    else if (end > str.Length) end = str.Length;

                    if (step < 0)
                    {
                        str = new string(str.Reverse().ToArray());
                        step = Math.Abs(step);
                    }
                }
                else
                {
                    if (start == null)
                    {
                        if (step > 0) start = 0;
                        else start = str.Length - 1;
                    }
                    else if (start < 0)
                    {
                        if (start < -str.Length) start = 0;
                        else start += str.Length;
                    }
                    else if (start > str.Length) start = str.Length;

                    if (end == null)
                    {
                        if (step > 0) end = str.Length;
                        else end = -1;
                    }
                    else if (end < 0)
                    {
                        if (end < -str.Length) end = 0;
                        else end += str.Length;
                    }
                    else if (end > str.Length) end = str.Length;


                }

                if (start == end || start < end && step < 0 || start > end && step > 0) return "";
                if (start < end && step == 1) return str.Substring(start.Value, end.Value - start.Value);

                if (linq)
                {
                    return new string(str.Skip(start.Value).Take(end.Value - start.Value).Where((s, index) => index % step == 0).ToArray ());;
                }
                else
                {
                    int length = (int)(((end.Value - start.Value) / (float)step) + 0.5f);
                    var sb = new StringBuilder(length);
                    for (int i = start.Value, j = 0; j < length; i += step, ++j)
                        sb.Append(str[i]);
                    return sb.ToString();
                }
            }

        }
    }
...