2010-11-24 3 views
9

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

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

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


Кусочек. Он работает как нотация массива 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's slice, но с дополнительным аргументом.


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

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(); 
} 
+5

Что он должен делать? Я знаю, что смогу это исправить, но я чувствую себя немного ленивым ... – ChrisF 2010-11-24 19:54:46

+0

- это подстрока !? – Fredou 2010-11-24 19:56:03

+0

Я заинтригован, чтобы знать, для чего вы его используете? Шаг бит интригует. Я понимаю, что он делает, но какое практическое применение. Просто интересно. – 2010-11-24 19:56:15

ответ

2

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

С точки зрения API я бы рассмотрел optional arguments вместо нулевых значений ints.

Update

После близко чтения коды, я могу видеть, что дает «старт» и «конец» значение нуля, имеет особое значение при принятии «шага» во внимание, таким образом, они могли бы не могут быть представлены как необязательные параметры int только, однако они все еще могут быть необязательными параметрами.

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

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

1

Я могу видеть 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.Значение

изменение

var sb = new StringBuilder(length); 

в

StringBuilder sb = new StringBuilder(length); 

и большой вопрос, if it does what it need, why fixing it?


обновление, чтобы показать, как сделать это с помощью 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(); 
       } 
      } 

     } 
    } 
1

Когда я спрашиваю 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]); 
Смежные вопросы