Існуючі відповіді вірні лише в тому випадку, якщо кластери Unicode Modifiers / Grapheme ігноруються. Я з цим розберуся пізніше, але спочатку погляньте на швидкість деяких алгоритмів розвороту:
list_comprehension : min: 0.6μs, mean: 0.6μs, max: 2.2μs
reverse_func : min: 1.9μs, mean: 2.0μs, max: 7.9μs
reverse_reduce : min: 5.7μs, mean: 5.9μs, max: 10.2μs
reverse_loop : min: 3.0μs, mean: 3.1μs, max: 6.8μs
list_comprehension : min: 4.2μs, mean: 4.5μs, max: 31.7μs
reverse_func : min: 75.4μs, mean: 76.6μs, max: 109.5μs
reverse_reduce : min: 749.2μs, mean: 882.4μs, max: 2310.4μs
reverse_loop : min: 469.7μs, mean: 577.2μs, max: 1227.6μs
Ви бачите, що час для розуміння списку ( reversed = string[::-1]
) у всіх випадках на сьогоднішній день найменший (навіть після виправлення мого друку).
Обертання рядків
Якщо ви дійсно хочете повернути рядок у здоровому розумінні, це ШЛЯХО складніше. Наприклад, візьміть наступну рядок ( коричневий палець вліво , жовтий палець вгору ). Це дві графеми, але 3 кодові точки Unicode. Додатковий - це модифікатор шкіри .
example = "👈🏾👆"
Але якщо повернути його будь-яким із заданих способів, ви отримаєте коричневий палець вгору , жовтий палець вліво . Причиною цього є те, що модифікатор "коричневого" кольору все ще знаходиться посередині і застосовується до того, що знаходиться перед ним. Так ми маємо
- U: палець вгору
- М: коричневий модифікатор
- L: вказівний палець ліворуч
і
original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)
Кластери Unicode Grapheme трохи складніше, ніж просто модифікаційні кодові точки. На щастя, є бібліотека для обробки графем :
>>> import grapheme
>>> g = grapheme.graphemes("👈🏾👆")
>>> list(g)
['👈🏾', '👆']
а значить, правильною буде відповідь
def reverse_graphemes(string):
g = list(grapheme.graphemes(string))
return ''.join(g[::-1])
який також є найбільш повільним:
list_comprehension : min: 0.5μs, mean: 0.5μs, max: 2.1μs
reverse_func : min: 68.9μs, mean: 70.3μs, max: 111.4μs
reverse_reduce : min: 742.7μs, mean: 810.1μs, max: 1821.9μs
reverse_loop : min: 513.7μs, mean: 552.6μs, max: 1125.8μs
reverse_graphemes : min: 3882.4μs, mean: 4130.9μs, max: 6416.2μs
Код
#!/usr/bin/env python
import numpy as np
import random
import timeit
from functools import reduce
random.seed(0)
def main():
longstring = ''.join(random.choices("ABCDEFGHIJKLM", k=2000))
functions = [(list_comprehension, 'list_comprehension', longstring),
(reverse_func, 'reverse_func', longstring),
(reverse_reduce, 'reverse_reduce', longstring),
(reverse_loop, 'reverse_loop', longstring)
]
duration_list = {}
for func, name, params in functions:
durations = timeit.repeat(lambda: func(params), repeat=100, number=3)
duration_list[name] = list(np.array(durations) * 1000)
print('{func:<20}: '
'min: {min:5.1f}μs, mean: {mean:5.1f}μs, max: {max:6.1f}μs'
.format(func=name,
min=min(durations) * 10**6,
mean=np.mean(durations) * 10**6,
max=max(durations) * 10**6,
))
create_boxplot('Reversing a string of length {}'.format(len(longstring)),
duration_list)
def list_comprehension(string):
return string[::-1]
def reverse_func(string):
return ''.join(reversed(string))
def reverse_reduce(string):
return reduce(lambda x, y: y + x, string)
def reverse_loop(string):
reversed_str = ""
for i in string:
reversed_str = i + reversed_str
return reversed_str
def create_boxplot(title, duration_list, showfliers=False):
import seaborn as sns
import matplotlib.pyplot as plt
import operator
plt.figure(num=None, figsize=(8, 4), dpi=300,
facecolor='w', edgecolor='k')
sns.set(style="whitegrid")
sorted_keys, sorted_vals = zip(*sorted(duration_list.items(),
key=operator.itemgetter(1)))
flierprops = dict(markerfacecolor='0.75', markersize=1,
linestyle='none')
ax = sns.boxplot(data=sorted_vals, width=.3, orient='h',
flierprops=flierprops,
showfliers=showfliers)
ax.set(xlabel="Time in ms", ylabel="")
plt.yticks(plt.yticks()[0], sorted_keys)
ax.set_title(title)
plt.tight_layout()
plt.savefig("output-string.png")
if __name__ == '__main__':
main()