#tcl
#tcl
Вопрос:
После создания потенциально очень большой строки я собираюсь сделать много изменений отдельных символов в ней (или байтов, если необходимо) на другой символ.
На самом деле, мой скрипт создает кроссворд, поэтому строка не будет очень длинной, но мой вопрос общий:
Как я могу использовать тот факт, что я не изменяю длину строк (или любого другого типа данных, который лучше), чтобы ускорить процесс?
Я предполагаю, что часть того, что я ищу, — это способ отправки указателя или ссылки на строку или, в случае Tcl, имя переменной.
Мой другой вопрос заключается в том, что происходит внутри кода C.
Будет ли этот вызов копировать всю нулевую строку, один или даже два раза?
set index [expr {$row * $width $col}]
set puzzle [string replace $puzzle $index $index "E"]
Комментарии:
1. Если вы вносите много изменений в какое-либо значение, работайте в процедуре, лямбде или методе. Локальные переменные намного быстрее глобальных.
Ответ №1:
string replace
Операция произведет изменение на месте при условии выполнения двух условий:
- Вставляемая строка должна быть той же длины, что и удаляемая строка. Я предполагаю, что это для вас очевидно.
- Строка должна находиться в неразделенной ссылке, чтобы ничто другое не могло наблюдать изменяемое значение. (Это важная часть того, как работают все ссылки Tcl; общие ссылки не могут быть изменены на месте.)
Этот вызов, как написано, будет скопирован. Это предсказуемо, основываясь на простом рассмотрении обработки ссылок для строки; проблема в том, что старая версия строки остается puzzle
до string replace
завершения ( set
требуется, чтобы результат работал). Чтобы исправить это, мы делаем эту немного странную вещь:
set puzzle [string replace $puzzle[set puzzle {}] $index $index "E"]
Да, это странно, но это работает хорошо, потому что конкатенация с заведомо пустой строкой является явно оптимизированным случаем, предполагая, что здесь вы имеете дело с неотслеживаемыми переменными. (Это будет работать с отслеживаемыми переменными, но двойная запись заметна, и трассировки могут делать сложные вещи, поэтому вы теряете возможности оптимизации.)
Если бы вы делали обширные изменения, которые иногда меняют длину вещей, переход на использование списков и lset
был бы более эффективным. Все эквивалентные операции со списками используют одну и ту же общую справочную и локальную семантику, но работают с элементами списка вместо символов.
Разборка
Оптимизация, о которой я говорю, находится в strcat
коде операции и strreplace
знает, что нужно делать на месте, когда это возможно, но вы не видите информацию на уровне байт-кода; практически все операции это знают.
% tcl::unsupported::disassemble lambda {{puzzle index} {
set puzzle [string replace $puzzle[set puzzle {}] $index $index "E"]
}}
ByteCode 0x0x7fbff6021c10, refCt 1, epoch 17, interp 0x0x7fbff481e010 (epoch 17)
Source "n set puzzle [string replace $puzzle[set puzzle {}]..."
Cmds 3, src 74, inst 18, litObjs 2, aux 0, stkDepth 4, code/src 0.00
Proc 0x0x7fbff601cc90, refCt 1, args 2, compiled locals 2
slot 0, scalar, arg, "puzzle"
slot 1, scalar, arg, "index"
Commands 3:
1: pc 0-16, src 5-72 2: pc 0-14, src 17-71
3: pc 2-5, src 40-52
Command 1: "set puzzle [string replace $puzzle[set puzzle {}] $inde..."
Command 2: "string replace $puzzle[set puzzle {}] $index $index "E..."
(0) loadScalar1 %v0 # var "puzzle"
Command 3: "set puzzle {}..."
(2) push1 0 # ""
(4) storeScalar1 %v0 # var "puzzle"
(6) strcat 2
(8) loadScalar1 %v1 # var "index"
(10) loadScalar1 %v1 # var "index"
(12) push1 1 # "E"
(14) strreplace
(15) storeScalar1 %v0 # var "puzzle"
(17) done
Комментарии:
1. Итак, установка переменной в пустую строку удаляет эту ссылку на нее? Есть ли какие-либо планы по созданию команды на месте, или это в некоторых местах нарушит то, что обозначает Tcl? В любом случае, спасибо за информативный ответ, мне нравится этот язык.
2. Встроенная команда, которая работает с переменной, содержащей строку? Никаких конкретных планов. Возражений тоже нет; самым сложным будет выбрать имя.
3. Очевидно, что имя должно отражать то, что вы ищете как программист или сценарист. Тот факт, что Tcl внутренне оптимизирован, чтобы избежать перераспределения памяти, если это возможно, в некоторых командах (или во всех), определенно не должен загрязнять документацию.
4. Я (серьезно) предлагаю имена
poke
иpeek
. Конечно, под контролемfconfigure
команды, что является одной из многих сильных сторон Tcl. Открытие потока в памяти, это что-то новое в языках?