You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: exercises/practice/reverse-string/.approaches/additional-approaches/content.md
+46-56Lines changed: 46 additions & 56 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,152 +4,142 @@ Below are some interesting strategies that are distinct from the canonical appro
4
4
While they do not offer particular performance boosts over the canonical approaches (_and some offer very large penalties_), they do explore interesting corners of Python.
5
5
6
6
7
-
## Convert the Input to a UTF-8 bytearray and use a Sliding Window to Reverse
8
-
7
+
## Convert the Input to a UTF-8 `bytearray` and use a Sliding Window to Reverse
This strategy encodes the string into a UTF-8 [`bytearray`][bytearray].
40
-
It then uses a `while` loop to iterate through the text, calculating the length of a sequence (or 'window') to slice from 'given' and prepend to 'output'.
41
-
The 'index' counter is then incremented by the length of the 'window'.
42
-
Once the 'index' is greater than the length of 'given', the 'output' bytearray is decoded into a UTF-8 string and returned.
43
-
This is (_almost_) the same set of operations as described in the code below, but operating on bytes in a bytearray, as opposed to text/codepoints in a `list`- although this strategy does not use `list.pop()` (_bytearray objects do not have a pop method_).
37
+
It then uses a `while` loop to iterate through the text, calculating the length of a sequence (or 'window') to slice from `given` and prepend to `output`.
38
+
The `index` counter is then incremented by the length of the 'window'.
39
+
Once the `index` is greater than the length of `given`, the `output``bytearray` is decoded into a UTF-8 string and returned.
40
+
This is (_almost_) the same set of operations as described in the next approach, but operating on bytes in a `bytearray`, as opposed to text/codepoints in a `list`— although this strategy does not use `list.pop()` (_`bytearray` objects do not have a pop method_).
44
41
45
-
This uses `O(n)` space for the output array.
42
+
This uses `O(n)` space for the output array.
46
43
It incurs additional runtime overhead by _prepending_ to the output array, which is an expensive operation that forces many repeated shifts.
47
44
Encoding to bytes and decoding to codepoints further slow this approach.
48
45
49
46
50
-
## Convert the Input to a list and use a While Loop to Pop and Append to a Second List
51
-
47
+
## Convert the Input to a `list` and use a While Loop to Pop and Append to a Second List
52
48
53
49
```python
54
50
defreverse(text):
55
-
codepoints, stniopedoc=list(text), []
51
+
codepoints, output=list(text), []
56
52
57
53
while codepoints:
58
-
stniopedoc.append(codepoints.pop())
59
-
60
-
return''.join(stniopedoc)
54
+
output.append(codepoints.pop())
55
+
56
+
return"".join(output)
61
57
```
62
58
63
59
This strategy uses two lists.
64
60
One `list` for the codepoints in the text, and one to hold the codepoints in reverse order.
65
-
First, the input text is turned into a the 'codepoints' `list`, and iterated over.
66
-
Each codepoint is `pop()`ped from 'codepoints' and appended to the 'stniopedoc' `list`.
67
-
Finally, 'stniopedoc' is joined via `str.join()` to create the reversed string.
68
-
69
-
While this is a straightforward and readable approach, it creates both memory and performance overhead, due to the creation of the lists and the use of `join()`.
70
-
This is much faster than the bytearray strategy or using string concatenation, but is still almost slower than the slicing strategy.
71
-
It also takes up `O(n)` auxiliary space with the stniopedoc list.
61
+
First, the input text is turned into the `codepoints``list`, and iterated over.
62
+
Each codepoint is `pop()`ped from `codepoints` and appended to the `output``list`.
63
+
Finally, `output` is joined via `str.join()` to create the reversed string.
72
64
65
+
While this is a straightforward and readable approach, it creates both memory and performance overhead, due to the creation of the lists and the use of `str.join()`.
66
+
This is much faster than the bytearray strategy or using string concatenation, but is still slightly slower than the slicing strategy.
67
+
It also takes up `O(n)` auxiliary space with the `output` list.
73
68
74
69
75
70
## Using Recursion Instead of a Loop
76
71
77
-
78
72
```python
79
73
defreverse(text):
80
74
iflen(text) ==0:
81
75
return text
82
-
else:
83
-
return reverse(text[1:]) + text[0]
76
+
return reverse(text[1:]) + text[0]
84
77
```
85
78
86
79
This strategy uses a slice to copy all but the leftmost part of the string, concatenating the codepoint at the first index to the end.
87
80
The function then calls itself with the (now shorter) text slice.
88
-
This slice + concatenation process continues until the `len()` is 0, and the reversed text is returned up the call stack.
81
+
This slice/concatenate process continues until the `len()` is 0, and the reversed text is returned up the call stack.
89
82
This is the same as iterating over the string backward in a `loop`, appending each codepoint to a new string, and has identical time complexity.
90
-
It also uses O(n) space, with the space being successive calls on the call stack.
83
+
It also uses `O(n**2)` space, as the space taken up by successive calls on the call stack builds up.
91
84
92
85
Because each recursive call is placed on the stack and Python limits recursive calls to a max of 1000, this code produces a `maximum recursion depth exceeded` error for any string longer than ~999 characters.
93
86
94
87
95
-
## Using `map()` and `lambbda` with `Join()` Instead of a Loop
88
+
## Using `map()` and a `lambda` with `str.join()` Instead of a Loop
This variation uses the built-in `map()` and a `lambda` to iterate over the string backward, constructing a `list`.
103
96
The `list` is then fed to `str.join()`, which unpacks it and turns it into a string.
104
97
This is a very non-performant way to walk the string backwards, and also incurs extra overhead due to the unneeded construction of an intermediary `list`.
105
98
106
-
`map()` can instead be directly fed to `join()`, which improves performance to `O(n)`:
99
+
`map()` can instead be directly fed to `str.join()`, which improves performance:
## Using a `lambda` that returns a Reverse Sequence Slice
115
108
116
-
117
109
```python
118
110
reverse =lambdatext: text[::-1]
119
111
```
120
112
121
-
122
113
This strategy assigns the name "reverse" to a `lambda` that produces a reverse slice of the string.
123
114
This looks quite clever and is shorter than a "traditional" function, but it is far from obvious that this line defines a callable named "reverse" that returns a reversed string.
124
-
While this code compiles to the same function definition as the first approach article, it is not clear to many programmers who might read through this code that they could call `reverse('some_string')` the way they could call other functions.
115
+
While this code compiles to the same function definition as the first approach article, it is not clear to many programmers who might read through this code that they could call `reverse("some_string")` the way they could call other functions.
125
116
126
117
127
-
This has the added disadvantage of creating troubleshooting issues since any errors will be attributed to `lambda` in the stack trace and not associated with an explicit function named `reverse`.
118
+
This has the added disadvantage of creating troubleshooting issues, since any errors will be attributed to `lambda` in the stack trace and not associated with an explicit function named `reverse`.
128
119
Help calls and `__repr__` calls are similarly affected.
129
-
This is not the intended use of `lambdas` (_which are for unnamed or anonymous functions_), nor does it confer any sort of performance boost over other methods, but _does_ create readability issues with anyone unfamiliar with `lambda` syntax and compilation.
120
+
This is not the intended use of `lambdas` (_which are for unnamed or anonymous functions_), nor does it confer any sort of performance boost over other methods, but it _does_ create readability issues with anyone unfamiliar with `lambda` syntax and compilation.
130
121
131
122
132
123
## Timings vs Reverse Slice
133
124
134
-
135
125
As a (very) rough comparison, below is a timing table for these functions vs the canonical reverse slice:
0 commit comments