хорошо, так что я сел и сделал математику. Пожалуйста, не сердитесь на меня, я отвечаю, конкретно обсуждая решение ΤΖΩΤΖΙΟΥ, но это было бы довольно сложно заключить в комментарий, поэтому позвольте мне сделать это так. На самом деле я также выскажу некоторые соображения, которые имеют отношение к вопросу ФП.
Прежде всего, я обсуждал с ΤΖΩΤΖ элегантность, правильность и жизнеспособность его подхода. Оказывается, это выглядит как предложение, хотя оно использует (изначально неупорядоченный) словарь в качестве регистра для хранения пар подстановки, фактически последовательно возвращает правильные результаты, где я утверждал, что это не так. это потому, что вызов itertools.starmap()
в строке 11 ниже получает в качестве второго аргумента итератор для пар одиночных символов / байтов (подробнее об этом позже), который выглядит как [ ( 'h', 'h', ), ( 'e', 'e', ), ( 'l', 'l', ), ... ]
. эти пары символов / байтов - то, с чем первый аргумент, replacer.get
, вызывается неоднократно. нет никакого шанса столкнуться с ситуацией, когда сначала '>'
преобразуется в '>'
, а затем непреднамеренно снова в '>'
, поскольку каждый символ / байт рассматривается только один раз для подстановки. так что эта часть в принципе хороша и алгоритмически верна.
следующий вопрос - это жизнеспособность, и это будет включать в себя взгляд на производительность. если жизненно важное задание выполняется правильно за 0,01 с использованием неудобного кода, но 1 с использованием удивительного кода, то неудобное может считаться предпочтительным на практике (но только в том случае, если потеря в 1 секунду фактически недопустима). вот код, который я использовал для тестирования; он включает в себя ряд различных реализаций. он написан на python 3.1, так что мы можем использовать греческие буквы юникода для идентификаторов, что само по себе удивительно (zip
в py3k возвращает то же самое, что itertools.izip
в py2):
import itertools #01
#02
_replacements = { #03
'&': '&', #04
'<': '<', #05
'>': '>', #06
'"': '"', #07
"'": ''', } #08
#09
def escape_ΤΖΩΤΖΙΟΥ( a_string ): #10
return ''.join( #11
itertools.starmap( #12
_replacements.get, #13
zip( a_string, a_string ) ) ) #14
#15
def escape_SIMPLE( text ): #16
return ''.join( _replacements.get( chr, chr ) for chr in text ) #17
#18
def escape_SIMPLE_optimized( text ): #19
get = _replacements.get #20
return ''.join( get( chr, chr ) for chr in text ) #21
#22
def escape_TRADITIONAL( text ): #23
return text.replace('&', '&').replace('<', '<').replace('>', '>')\ #24
.replace("'", ''').replace('"', '"') #25
это временные результаты:
escaping with SIMPLE took 5.74664253sec for 100000 items
escaping with SIMPLE_optimized took 5.11457801sec for 100000 items
escaping TRADITIONAL in-situ took 0.57543013sec for 100000 items
escaping with TRADITIONAL took 0.62347413sec for 100000 items
escaping a la ΤΖΩΤΖΙΟΥ took 2.66592320sec for 100000 items
говорит о том, что первоначальный постер обеспокоен тем, что «традиционный» метод становится «ужасно быстрым и, по-видимому, имеет плохую алгоритмическую производительность», он выглядит частично необоснованным, если рассматривать его в этом контексте. это на самом деле работает лучше всего; когда мы спрятаны в вызове функции, мы получаем 8% -ное снижение производительности («вызов методов стоит дорого», но в целом вам все равно следует это делать). для сравнения, реализация ΤΖΩΤΖΙΟΥ занимает примерно в 5 раз больше времени, чем традиционный метод, что, учитывая его более высокую сложность, должно конкурировать с давно отточенными, оптимизированными строковыми методами, что неудивительно.
здесь есть еще один алгоритм, ПРОСТОЙ. насколько я вижу, это в значительной степени делает именно то, что делает метод ΤΖΩΤΖΙΟΥ: он перебирает символы / байты в тексте и выполняет поиск каждого из них, затем объединяет все символы / байты вместе и возвращает полученный экранированный текст. Вы можете видеть, что если один из способов сделать это включает в себя довольно длинную и таинственную формулировку, реализация SIMPLE на самом деле понятна с первого взгляда.
Что меня действительно удивляет, так это то, насколько плохо работает ПРОСТОЙ подход: он примерно в 10 раз медленнее традиционного, а также вдвое медленнее, чем метод ΤΖΩΤΖΙΟΥ. я в полном недоумении, может кто-нибудь придумает, почему так должно быть. он использует только самые основные строительные блоки python и работает с двумя неявными итерациями, поэтому он избегает создания одноразовых списков и всего, но он все еще медленный, и я не знаю почему.
позвольте мне завершить этот обзор кода замечанием о достоинствах ΤΖΩРешение. я достаточно ясно дал понять, что нахожу код трудным для чтения и слишком сложным для выполнения поставленной задачи. однако более критично то, что я нахожу способ, которым он обращается с персонажами и следит за тем, чтобы для данного небольшого диапазона символов они вели себя как байты, немного раздражая. конечно, это работает для поставленной задачи, но как только я повторюсь, например. над строкой «ΤΖΩΤΖΙΟΥ» я перебираю соседние байты, представляющие отдельные символы. в большинстве ситуаций это именно то, чего вам следует избегать; Именно поэтому в py3k «строки» теперь являются «объектами Юникода» старых, а «строки» старых становятся «байтами» и «байтовыми массивами». если бы я назначил одну особенность py3k, которая могла бы гарантировать возможно дорогостоящую миграцию кода из серии 2 в серию 3, это было бы это единственное свойство py3k. С тех пор 98% всех моих проблем с кодировкой только что исчезли, и нет никакого хитрого взлома, который мог бы заставить меня серьезно усомниться в моем движении. указанный алгоритм не является «концептуально чистым 8-битным и безопасным в Юникоде», что для меня является серьезным недостатком, учитывая, что это 2010. - 1025 *