Я пытался не повторять один и тот же код в разных блоках переключения и не мог понять, как разбить сценарий переключения, кроме как вернуться и выйти из процедуры.
Мой вопрос: является ли перенос переключателя в цикл while просто для того, чтобы сломать его, когда это необходимо, ужасной вещью, и есть ли лучший способ?
Это заняло у меня некоторое время, но я понимаю, что перерыв не нужен, потому что можно установить переменную в catch и протестировать вне ее, чтобы определить, когда запускается остальная часть кода под opt_2 (как во втором примере кода ниже); но сделать перерыв более понятно, особенно когда реальный код немного сложнее, чем в этом примере.
Спасибо.
% proc SwitchBreak {condition valid} {
set ui_state 1
while { 1 } {
switch -- $condition {
opt_1 {
chan puts stdout opt_1
} opt_2 {
chan puts stdout opt_2
if { [info exists ui_state] && $ui_state == $valid } {
if { [catch { throw {Database Error} {Failed to Write} } result]} {
chan puts stdout "${result}. Rolled back"
set rv 1
set response {Operation Failed}
break
}
}
chan puts stdout {Opt_1 code to run only when\
no valid ui_state or valid state succeeds.}
set rv 0
set response {Successful write.}
} opt_3 {
chan puts stdout opt_3
} default {
chan puts stdout opt_4
}
}
break
}
chan puts stdout {Code after the switch.}
chan puts stdout "rv: $rv; response: $response"
}
% SwitchBreak opt_2 1
opt_2
Failed to Write. Rolled back
Code after the switch.
rv: 1; response: Operation Failed
% SwitchBreak opt_2 0
opt_2
Opt_1 code to run only when no valid ui_state or valid state succeeds.
Code after the switch.
rv: 0; response: Successful write.
Ни петли, ни разрыва.
proc SwitchBreak {condition valid} {
set ui_state 1
switch -- $condition {
opt_1 {
chan puts stdout opt_1
} opt_2 {
chan puts stdout opt_2
set rv 0
if { [info exists ui_state] && $ui_state == $valid } {
if { [catch { throw {Database Error} {Failed to Write} } result]} {
chan puts stdout "${result}. Rolled back"
set rv 1
set response {Operation Failed}
}
}
if { $rv == 0 } {
chan puts stdout {Opt_1 code to run only when\
no valid ui_state or valid state succeeds.}
set response {Successful write.}
}
} opt_3 {
chan puts stdout opt_3
} default {
chan puts stdout opt_4
}
}
chan puts stdout {Code after the switch.}
chan puts stdout "rv: $rv; response: $response"
}
% %
% Switch opt_2 1
opt_2
Failed to Write. Rolled back
Code after the switch.
rv: 1; response: Operation Failed
% SwitchBreak opt_2 0
opt_2
Opt_1 code to run only when no valid ui_state or valid state succeeds.
Code after the switch.
rv: 0; response: Successful write.
Просто слежу, чтобы посмотреть, какую помощь вы получите. За исключением нескольких тривиальных expect
скриптов, я не использовал Tcl для чего-то столь же сложного, как то, что вы делаете, более 30 лет. Мне интересно, вы это делаете в рамках expect
программы или независимо от нее? И если последнее, то почему, ведь почти наверняка есть варианты получше?
SQLite имеет привязки для многих популярных языков, включая Go и Python (я использовал SQLite с обоими). Я рекомендую использовать любой из этих языков, который вы предпочитаете, а не привязку Tcl.
@KurtisRader Спасибо за рекомендацию. Я никогда не использовал ни один из этих языков; и в целом у вас нет большого опыта программирования. Тем не менее (и, пожалуйста, знайте, что я не пытаюсь показаться грубым или затеять спор), Tcl мне очень понравился; и чем больше я узнаю об этом, тем больше мне это нравится. Если бы я был молодым человеком, интересующимся информатикой в своей карьере, я бы изучал исходный код Tcl и SQLite.
Если вы неопытный программист, изучение Tcl бесполезно. Вам следует изучать это только в том случае, если вам это абсолютно необходимо. Например, если вы собираетесь внести изменения в проект SQLite. Инвестиции в изучение современных языков, таких как Go, Rust и т. д. (все они имеют привязки к SQLite), в долгосрочной перспективе принесут вам гораздо больше пользы. Tcl был создан в 1986 году; через десять лет после того, как я начал учиться программировать. Он имеет множество недостатков и сейчас используется лишь в нескольких нишевых приложениях, таких как tkinter и SQLite. Опять же, вам лучше выучить другой язык.
Я не знаю о недостатках Tcl и индустрии программирования, но, честно говоря, Tcl, должно быть, все еще активен. Например, второе издание книги Оустерхаута (основателя Tcl) о Tcl/Tk было опубликовано в 2010 году; Ашок Надкарни написал текст большего размера только на Tcl в 2017 году; и вышло обновление до 8.6.1425 марта этого года. Это может быть ниша, поскольку объявления о вакансиях, в которых упоминается Tcl, предназначены только для компьютерных гениев, а не для новичков вроде меня.
@Gary Я думаю, будет справедливо сказать, что вы можете игнорировать фразы «Я не знаю ответа на ваш вопрос, используйте библиотеку, о которой я кое-что знаю». Хотя я также не являюсь экспертом в tcl, я бы изучил сопрограммы , поскольку tcl изначально поддерживает цикл событий, я не уверен, действительно ли вам нужно зацикливаться на этом.
@Gary FORTRAN и COBOL все еще активны (раньше я хорошо владел этими языками). Это не значит, что я бы порекомендовал новому программисту приложить усилия для изучения этих языков. :-) Это не имеет ничего общего с тем, что Tcl предназначен только для «компьютерных гениев, а не для новичков». Tcl — это просто интересный язык, предназначенный для определенной цели (встраиваемый в приложения для управления их поведением) и никогда не получивший широкого распространения в качестве языка общего назначения. Он, конечно, не умер, но мне кажется, что ОП было бы лучше взаимодействовать с SQLite, используя другой язык.
@Thingamabobs Будет интересно, сколько времени понадобится кому-то, чтобы ответить на вопрос ОП. Вот почему я скрываюсь и призываю ОП использовать другую языковую привязку для взаимодействия с SQLite. Кроме того, я не предлагаю ОП использовать «библиотеку, о которой я что-то знаю». Я предлагаю им использовать другую языковую привязку. Tcl — это просто одна из многих языковых привязок для библиотеки SQLite. Хотя эта привязка рассматривается проектом как особая из-за ее использования в модульных тестах.
switch
не является циклом, поэтому, конечно, конструкции управления циклом, такие как break
, с ним не работают. Может быть, немного меньше кода и немного больше объяснений того, чего вы пытаетесь достичь (не как; что)?
@KurtisRader Это будет не единственный вопрос, на который нет ответов по SO. Если бы OP хотел работать с огромной базой данных и имел проблемы с производительностью, а tcl просто достиг своего предела, поскольку ядро tcl не предназначено для работы с огромными базами данных, и кто-то рекомендует использовать для этого Python, это было бы разумно сделать это. Поскольку ОП не достигает каких-либо пределов и просто хочет перейти к уже известной технологии ОП, просто неразумно предлагать другой язык в этом контексте.
(Я бы предпочел использовать tcl для работы с базой данных sqlite, чем с Python)
@Shawn Код OP использует break
внутри switch
внутри while
цикла. Во многих (большинстве?) языках break
завершает цикл while
. Разве это не относится к Tcl? Опять же, я говорю это как человек, который три десятилетия назад был минимально компетентным программистом на Tcl и с тех пор использовал его только для написания сценариев в инструменте expect
. ОП также, кажется, адекватно объяснил, что они пытаются сделать. Так что меня немного смутил ваш комментарий.
@KurtisRader Да, это вырвется из цикла. Но цель ОП, похоже, состоит в том, чтобы как можно скорее завершить тело переключателя. Вопрос в том, что они делают, что заставляет их хотеть это сделать? Возможно, существует лучший способ размещения кода, но для того, чтобы сказать наверняка, нужно более четкое представление о цели. По сути, мне интересно, является ли это проблемой XY.
Есть ли правильный способ «вырваться» из сценария переключения и остаться в процедуре, кроме зацикливания? По моему мнению, это на самом деле требует сопрограмм, сохраняющих контекст при возврате. Но опять же, я не эксперт в tcl и не могу предоставить какое-либо кодовое решение для этой проблемы.
catch throw
кажется дорогим способом установить переменную...
Хотя это законно, поскольку вы можете написать это, и оно будет выполняться правильно, мне это кажется очень некрасивым; простой if
...else
, казалось бы, мог бы выразить намерение, но на самом деле был бы более ясным. Я бы даже сказал, что обычно я ожидаю, что это будет правдой.
# Just the bit inside the switch case
chan puts stdout opt_2
if {
[info exists ui_state] && $ui_state == $valid
&& [catch {
# This is a stand-in for something real, right? Right?
throw {Database Error} "Failed to Write"
} result]
} then {
chan puts stdout "${result}. Rolled back"
set rv 1
set response "Operation Failed"
} else {
chan puts stdout "Opt_1 code to run only when\
no valid ui_state or valid state succeeds."
set rv 0
set response "Successful write."
}
Будет ли случай, когда лучше всего проделать эту запутанную вещь с помощью однократного запуска while
? Я сомневаюсь в этом... но никогда не говори никогда.
Однократный запуск for
будет немного лучше, если вы поместите break
в предложение приращения. Таким образом, вверху будет яснее, что вы делаете.
Или вы можете использовать try
с предложением on break
.
Спасибо. Да, throw является заменой довольно простой транзакции upsert. Во втором примере используется if
, но не так, как в вашем. Думаю, я был сосредоточен на том, при каких условиях выполнять транзакцию с базой данных, и не смог увидеть, что успех этой транзакции может быть частью этого единственного if
. Если сценарий if
сложнее, я попробую try
с on break
или просто вынесу это в отдельную процедуру; и избегайте любой формы цикла однократного запуска.
Я не знал, что в операторе then
есть необязательный if
. Обычно я ссылаюсь на два печатных текста по Tcl, а не на Интернет; и если кто-то об этом пишет, то я это пропустил. Однако это ясно на странице руководства Tcl.
Что ж, онлайн-документы и страницы руководства (обе взяты из одного и того же источника) являются исчерпывающими справочными материалами. Книги — это учебный материал, но не нормативный документ.
Не уверен насчет вашего случая, но в некоторых языках среди общепринятых методов обработки сложных переходов эффективным способом является перенос частей в вызовы функций.