class RevStr { // Вывести символьную строку в обратном порядке public void DisplayRev(string str) { if (str.Length > 0) DisplayRev(str.Substring(1, str.Length-1)); else return; Console.Write(str[0]); } } class RevStrDemo { static void Main() { string s = "Это тест"; , RevStr rsOb = new RevStr (); Console.WriteLine("Исходная строка: " + s); Console.Write("Перевернутая строка: "); rsOb.DisplayRev(s); Console.WriteLine() ; } } 

I can not understand how here the line is displayed in the reverse order? How does recursion work here? Why here:

 DisplayRev(str.Substring(1, str.Length-1)); 

border starts with 1?

  • 3
    do you know anything about recursion / understand? Perhaps it makes sense for you to just read what it is and how it works? An example in general elementary - DreamChild

1 answer 1

See it.

Let's recursively define what “print a line in reverse order” is. It's simple

  1. separate the initial character (because it must be printed last)
  2. print the rest of the line in reverse order
  3. print a separated initial character.

For example, the line "ABCD" :

  1. initial character is 'A'
  2. print the remainder "BCD" in reverse order, DCB will be printed
  3. we print 'A' , as a result it is printed DCBA .

str.Substring(1, str.Length-1) is exactly the tail of a string, starting from the character with index 1 (the initial character of the string has index 0), and 1 less than the length of the string.


Note that the Shildt example is bad in many ways.

  1. The use of recursion is not justified here, since long lines (more than 1000 characters) are not uncommon, and this can lead to stack overflow.
  2. The task has a simple iterative solution, the use of recursion is much slower and more expensive in terms of resources.
  3. The code uses the RevStr class, which does not have “reverse line” semantics. This is just a helper class, and it must be static, since the instance does not carry an independent meaning. (And his name reflects nothing.)
  4. The string is copied many times, which is inefficient. It is easier to pass the initial index of an existing row for processing.
  5. Uses an improper overload of string.Substring , which forces you to calculate the length manually. There is a simpler overload (from a given index to the end of the line).
  6. The code is still bad, because the RevStr class RevStr too many responsibilities: it expands the string and prints it out. As a result, flexibility is lost: we cannot invert a line with this class and use it further without output.
  7. Mixing model semantics (flipping a line) and presentation semantics (output to the console) in one method is bad.

A better code would be (this option takes into account points 3-7):

 static class StringHelper { public string Reverse(string s) { var sb = new StringBuilder(); ReverseImpl(sb, 0, s); return sb.ToString(); } private void ReverseImpl(StringBuilder sb, int startIdx, string s) { if (startIdx >= s.Length) return; ReverseImpl(sb, startIdx + 1, s); sb.Append(s[startIdx]); } } class StringReverseDemo { static void Main() { string s = "Это тест"; , Console.WriteLine("Исходная строка: " + s); Console.WriteLine("Перевернутая строка: " + StringHelper.Reverse(s)); } } 

A variant in which tail recursion is possible, which means that it partially solves the problem of item 2:

 static class StringHelper { public string Reverse(string s) { var sb = new StringBuilder(); ReverseImpl(sb, s.Length - 1, s); return sb.ToString(); } private void ReverseImpl(StringBuilder sb, int endIdx, string s) { if (startIdx < 0) return; sb.Append(s[endIdx]); ReverseImpl(sb, endIdx - 1, s); } } 

The same code, but the recursion is folded into an iteration, thereby solving the problems of paras. 1 and 2:

 static class StringHelper { public string Reverse(string s) { var sb = new StringBuilder(); for (var endIdx = s.Length - 1; endIdx >= 0; endIdx--) sb.Append(s[endIdx]); return sb.ToString(); } } 

Now, instead of all logic, you can simply convert the data view, and use the quick library function:

 static class StringHelper { public string Reverse(string s) { var array = s.ToCharArray(); Array.Reverse(array); return new string(array); } } 

However, the latter option is also not entirely accurate, as it does not take into account combining Unicode accents. Jon Skeet wrote an excellent article about this (and much more): OMG Ponies !!! To solve the problem, we need to rearrange not the characters , but clusters of graphemes :

 static class StringHelper { private IEnumerable<string> GetGraphemeClusters(string s) { var iter = StringInfo.GetTextElementEnumerator(s); while (iter.MoveNext()) yield return iter.GetTextElement(); } public string Reverse(string s) { var array = GetGraphemeClusters().ToArray(); Array.Reverse(array); return string.Concat(array); } } 

See how far the solution in the book is far from correct?

  • Thank you for such a detailed answer. I will delve into - Timothy
  • @Timothy: Please! The last example is complex, and you quite have the right not to fully understand it, the rest are quite accessible in principle. - VladD
  • At the beginning of your post, I don’t understand very much. For example, the string "ABCD": the initial character - 'A' prints the rest of the "BCD" in reverse order, DCB will print, print 'A', and DCBA will be printed as a result. how BCD is printed in reverse order - Timothy
  • and then it is joined by A. - Timothy
  • Well, this is a recursion. We divide the task into one character and everything else. - VladD