LINUX.ORG.RU

А правда ли, что...

 , ,


0

3

... в Python нет аналога Perl'овского оператора redo (который возвращает управление в начало блока, в котором он находится)?



Последнее исправление: CYB3R (всего исправлений: 2)

Как теперь жить! Язык без такого оператора - просто не язык!

anonymous
()

Команда redo

Команда redo используется для повторного выполнения операторов тела цикла, не инициализируя следующую итерацию. Если она должна выполниться (обычно в силу выполнения некоторого условия) во время выполнения очередной итерации цикла, то расположенные за ней операторы тела цикла не выполняются, а управление передается на первый оператор тела цикла, при этом новая итерация не инициализируется. Это означает, что ни выражение изменения цикла for, ни операторы блока continue, если он задан, вместе с выражением-условием циклов while/until не вычисляются, а в случае цикла foreach переменной цикла не присваивается значение следующего элемента списка. Таким образом, при выполнении команды redo просто снова начинают выполняться операторы тела цикла.

думаю не все знают perl, а следовательно нужно давать хоть примерное описание поведения требуемой языковой конструкции.

anonymous
()

Теперь выложи код с твоим redo. Я его использовал несколько раз и всегда оказывалось, что он не нужен.

gh0stwizard ★★★★★
()
Ответ на: комментарий от anonymous

следовательно нужно давать хоть примерное описание поведения требуемой языковой конструкции

В ОП же кратко и понятно написано:

возвращает управление в начало блока, в котором он находится

makyrros
() автор топика
Ответ на: комментарий от olegsov

это же почти goto! не нужно!

Почему? Почему?

bug
()

При желании можно сделать его аналог. А в перле можно сделать какой нить оператор backlines(n) возвращающий управление на n строк кода назад? Или оператор nextlines(i0, i1, ...) выполняющий только строки с номерами i0, i1, ... ? А в питоне можно. И можно сделать еще 100500 таких же прекрасных операторов, позволяющих писать очень изящный, лаконичный, и непонятный никому (включая автора) код, только непонятно зачем...

AIv ★★★★★
()
Ответ на: комментарий от bug

Ну и гкод, да еще не рабочий. Вот как должно быть:

#!/usr/bin/perl

use strict;
use warnings;

use common::sense;

for my $str (qw(1a1 aa2)) {
  say("Word") , redo if $str =~ /\G[a-z]/gc;
  say("Digit"), redo if $str =~ /\G\d/gc;
}

print "\n---\n";

for my $str (qw(1a1 aa2)) {
    for (split //, $str) {
       m/[a-z]/ and say("Word");
       m/\d/ and say("Digit");
    }
}

gh0stwizard ★★★★★
()

Единственный язык, в котором я встречал redo — ruby. При этом, я ни разу не видел его использования.

anonymous
()
Ответ на: комментарий от anonymous

redo позволяет на полпути понять, что епсти, оказывается это было не то, подправить инпут и перезапустить итерацию, прикинувшись, что ничего не было. При разборе нестрогих нетривиальных конструкций использую очень часто, насколько вообще можно говорить о «часто» для redo/next/last. Недавно переписывал огромный парсер мутных пометок юзера с перла на луа, вместо redo и next заюзал goto, иначе такая лестница говна получалась, за флоконтролом кода не видно было.

Предвкушаю гоутуненавистников, ненавидьте меня полностью :3

arturpub ★★
()
Ответ на: комментарий от gh0stwizard

Прости, я что-то говорил о готовом решении? Просто привёл пример, где использование redo выглядит элегантным.

bug
()
Ответ на: комментарий от bug

Просто привёл пример, где использование redo выглядит элегантным.

Твое предположение хорошее, спору нет. Вот только обычно в сложных приложениях таких элегантностей просто не получается делать. В простых же приложениях такие фишки обычно не нужны, ибо неважно, как придумана архитектура приложения, какие структуры ворочаются, пиши как получается ради упрощения. И этот redo еще не каждый школьник разберет :) В итоге, мега-скрипт будет неоценен по достоинству и переписан так, как его понимает другой человек.

gh0stwizard ★★★★★
()
Ответ на: комментарий от olegsov

это же почти goto! не нужно! используй while true/continue/break.

Хмм, интересный совет, вместо одного почти goto предлагается использовать другие почти goto. JFYI, вызов функции - это тоже почти goto :-)

Begemoth ★★★★★
()
Ответ на: комментарий от makyrros

потому что есть универсальный goto, который я везде и использую.

гнать тебя из программирования ссаными тряпками

q11q11 ★★★★★
()
Ответ на: комментарий от q11q11

Но в некоторых случаях без goto невозможно обойтись же. Вот, например, сейчас я лижу яйца занимаюсь чепухой-квестом, который заброшу через пару дней. Имеется следующий участок кода:

ELEONOR: print "Вы прибыли к Перевалу Смерти. У вас есть выбор - прямо сейчас пойти в Элеонор и спасти больше жителей (что даст увеличенное повышение авторитета в Люране, но при этом будут потеряны дружеские отношения с Охотником), либо дождаться Охотника, не успев спасти некоторое количество жизней (из-за этого повышение авторитета будет несколько меньшим, но зато Охотник останется с вами).\nВыбирайте.\n1 - пойти в Элеонор.\n2 - дождаться прибытия Охотника.\n";
$sm = <STDIN>;
if ($sm == 1) {
	print "Отправляемся в Элеонор, покараем банду разбойников!\n";
	sleep 1;
	&battle
}
elsif ($sm == 2) {
	print "Ждём Охотника...\n";
	sleep 2;
	&hunter
}
else {
	goto ELEONOR
};

И как же в данном случае обойтись без goto? Поместить весь этот фрагмент в функцию и вызвать её вместо goto ELEONOR? Это же костыль.

makyrros
() автор топика
Ответ на: комментарий от q11q11

Нет, тебя, если ты не понимаешь, где goto может быть действительно нужен.

buddhist ★★★★★
()
Ответ на: комментарий от makyrros

И как же в данном случае обойтись без goto?

Ты что, серьёзно? while.

x3al ★★★★★
()
Ответ на: комментарий от makyrros

Поместить весь этот фрагмент в функцию и вызвать её вместо goto ELEONOR? Это же костыль.

goto в твоем случае еще больший костыль. Выглядит как бейсик от zx spectrum. Впрочем для твоей игрулки неважен ни код, ни стиль, ни перформанс.

gh0stwizard ★★★★★
()
Ответ на: комментарий от makyrros

блин, ты на васике учился программить?
всё это можно переделать на человеческом while/continue/break
собственно как уже и советовали

q11q11 ★★★★★
()
Ответ на: комментарий от q11q11

можно переделать на человеческом while/continue/break

Но зачем, если можно использовать вариант с goto, который проще устроен и легче читается?

makyrros
() автор топика
Ответ на: комментарий от makyrros

Один из вариантов без использования функции:

#!/usr/bin/perl

use strict;
use warnings;

use utf8;
use open qw(:std :utf8);

my $intro = "Вы прибыли к Перевалу Смерти. У вас есть выбор - прямо сейчас пойти в Элеонор и спасти больше жителей (что даст увеличенное повышение авторитета в Люране, но при этом будут потеряны дружеские отношения с Охотником), либо дождаться Охотника, не успев спасти некоторое количество жизней (из-за этого повышение авторитета будет несколько меньшим, но зато Охотник останется с вами).\nВыбирайте.\n1 - пойти в Элеонор.\n2 - дождаться прибытия Охотника.\n";

print $intro;

while (my $sm = <STDIN>) {
  if ($sm == 1) {
	print "Отправляемся в Элеонор, покараем банду разбойников!\n";
	sleep 1;
	&battle;
  } elsif ($sm == 2) {
	print "Ждём Охотника...\n";
	sleep 2;
	&hunter;
  } else {
	print $intro;
  }
}

sub hunter {
  print "Вы встретили охотника\n";
  exit 0;
}

sub battle {
  print "Начало битвы\n";
  exit 0;
}

Вариант через do, если смущает вызов print вначале:

#!/usr/bin/perl

use strict;
use warnings;

use utf8;
use open qw(:std :utf8);

my $intro = "Вы прибыли к Перевалу Смерти. У вас есть выбор - прямо сейчас пойти в Элеонор и спасти больше жителей (что даст увеличенное повышение авторитета в Люране, но при этом будут потеряны дружеские отношения с Охотником), либо дождаться Охотника, не успев спасти некоторое количество жизней (из-за этого повышение авторитета будет несколько меньшим, но зато Охотник останется с вами).\nВыбирайте.\n1 - пойти в Элеонор.\n2 - дождаться прибытия Охотника.\n";
my $sm = 0;

do {
  if ($sm == 1) {
	print "Отправляемся в Элеонор, покараем банду разбойников!\n";
	sleep 1;
	&battle;
  } elsif ($sm == 2) {
	print "Ждём Охотника...\n";
	sleep 2;
	&hunter;
  } else {
	print $intro;
  }
} while ($sm = <STDIN>);

sub hunter {
  print "Вы встретили охотника\n";
  exit 0;
}

sub battle {
  print "Начало битвы\n";
  exit 0;
}

gh0stwizard ★★★★★
()
Последнее исправление: gh0stwizard (всего исправлений: 2)
Ответ на: комментарий от makyrros

который проще устроен и легче читается?

ага, только вот с гоуту нужно быть более внимательным с точки зрения архитектуры программы
плюс его тяжелее дебажить

q11q11 ★★★★★
()
Ответ на: комментарий от makyrros

Он тебе предлагает

while (1) {
    print(...):
    $sm = ...;
    if ($sm == 1) {
        ...
        last;
    }
    elsif ($sm == 2) {
        ...
        last;
    }
}
Или вариацию с условием, что, по его мнению, гораздо понятнее и правильнее, чем твое гоуту. По мне так тот же хер, только в другой руке, сбитый с толку циклом, особенно если вариантов много. Но вообще, если это текстовый квест с состояниями, имеет смысл сделать функцию eleonor, которая в случае else вызовет сама себя.

То гоуту, которое критиковалось в свое время, могло впрыгивать в любое место невесть откуда, т.к. юзалось для организации всего флоконтрола (тот же бейсик с номерами строк). Сейчас в произвольный скоп нельзя вскочить, т.к. область видимости меток совпадает с переменными, а где можно — это не наш случай. И метка всегда дает намек на точку входа, в отличие от. Глупые хейтеры.

arturpub ★★
()
Ответ на: комментарий от q11q11

И в чем же надо внимательнее? С точки зрения архитектуры программы?

arturpub ★★
()
Ответ на: комментарий от q11q11

На вот тебе задачку на внимание, а также попробуй привести это к while-break-continue виду и сравни читаемость.

local date = context.currentDate
local date_set = false

for si = 1, (rhs and 2 or 1) do
    ::redo_outer::
    local side = (si == 1 and lhs or rhs)

    local r = re1stMatch([[\b(?i:дата)\b]], side)
    if r then
        if date_set then
            addError(context, nil, "syntax", "Дата повторяется")
            goto last_outer
        end

        for i = 0, #monthRe do
            local currentRe = i < 1 and recordDateRe or recordMonthRe[i]
            local r = re1stMatch(currentRe, side)
            if r then
                local day, month = tonumber(r[1]), (i < 1 and tonumber(r[2]) or i)
                local year = tonumber(r[4]) or context.currentYear

                side = reSubstitute(currentRe, side, "")
                if si == 1 then lhs = side
                else            rhs = side end

                if not year then
                    addError(context, nil, "struct", "Какой здесь год?")
                    goto last_outer
                end

                if year < 100 then
                    year = year + 2000
                end

                date = makeDate(year, month, day)

                if date < context.currentDate then
                    date = makeDate(year + 1, month, day)
                end

                date_set = true
                goto redo_outer -- find duplicates
            end
        end

        addError(context, nil, "syntax", "Дата неразборчиво")
        goto last_outer
    end
end
::last_outer::

arturpub ★★
()
Ответ на: комментарий от makyrros

Но зачем, если можно использовать вариант с goto, который проще устроен и легче читается?

Твой вариант устроен сложнее и странно читается. goto иногда нужно, но здесь — вообще нечто странное.

Кстати, у тебя весь код выглядит так же? В смысле твои &hunter и &battle примерно так же выглядят (печатают вступление, спрашивают цифру, в зависимости от неё с каким-то сообщением вызывают точно так же выглядящую функцию)? Сдаётся мне, что у тебя goto — далеко не самая страшная проблема.

Используя либастрал для доступа к остальному твоему коду, я бы, в порядке очерёдности:

1. Выделил в функцию вот этот кусок:

	print "Отправляемся в Элеонор, покараем банду разбойников!\n";
	sleep 1;
	&battle

Это должно делаться одной функцией. Параметры — сообщение, задержка, куда перейти. Очевидно же.

2. Выделил в функцию кусок:

ELEONOR: print "Вы прибыли к Перевалу Смерти. У вас есть выбор - прямо сейчас пойти в Элеонор и спасти больше жителей (что даст увеличенное повышение авторитета в Люране, но при этом будут потеряны дружеские отношения с Охотником), либо дождаться Охотника, не успев спасти некоторое количество жизней (из-за этого повышение авторитета будет несколько меньшим, но зато Охотник останется с вами).\nВыбирайте.\n1 - пойти в Элеонор.\n2 - дождаться прибытия Охотника.\n";
$sm = <STDIN>;

Это должно делаться одной функцией (ask или как-нибудь). Вывести сообщение и запросить ответ. После этого уже можно while (answer=ask(сообщение)), ну или как там в перле.

3. Выкинул диалоги из кода к чёрту, описал квест декларативно и оставил на перле небольшую программу, которая читает из описания квеста, что спросить и какие варианты куда ведут. Либастрал подсказывает, что программа такой сложности после этого влезет на 1 экран, описание квеста к ней — насколько хватит фантазии. Разделение логики и данных — рулез.

4. Понял, что изобрёл примитивную версию INSTEAD с квадратными колёсами.

x3al ★★★★★
()
Ответ на: комментарий от x3al

3. Выкинул диалоги из кода к чёрту, описал квест декларативно и оставил на перле небольшую программу, которая читает из описания квеста, что спросить и какие варианты куда ведут.

+1 gettext есть в перле

gh0stwizard ★★★★★
()
Ответ на: комментарий от arturpub

while (1)

Вообще, нет, иначе получится (как и в исходном варианте), что единственный способ выйти — ^C. Так не пишут же.

x3al ★★★★★
()
Ответ на: комментарий от arturpub

я не знаю что такое (rhs and 2 or 1)
но по мне ты сам напсал тот-же while (true) с кучей условий и выходом
все goto last_outer - break
goto redo_outer - continue

q11q11 ★★★★★
()
Ответ на: комментарий от x3al

1.

Да, действительно, стоит это сделать.

2.

Таких кусков планируется всего лишь два-три десятка (как-никак, это всего лишь поделка, созданная на коленке за полчаса just for fun и не претендующая на что-либо серьёзное), думаю, нерационально выносить их в отдельную функцию.

3.

Аналогично, ↑.

Но за советики в любом случае спасибо.

makyrros
() автор топика
Ответ на: комментарий от gh0stwizard

+1 gettext есть в перле

gettext — шаг 2,5. Когда в коде последовательность квеста, но все диалоги уже снаружи. А шаг 3 — когда ещё и отношения между диалогами выкидываются из кода и от программы остаётся (более или менее абстрактный) интерпретатор квестов, естественно следующий за выкидыванием локализованных строк из кода.

x3al ★★★★★
()
Ответ на: комментарий от q11q11

я не знаю что такое (rhs and 2 or 1)

(rhs ? 2 : 1), но это не существенно.

все goto last_outer - break
goto redo_outer - continue

Лол без права на апелляцию.

arturpub ★★
()
Ответ на: комментарий от true_admin

может, регистрировать трассирующую функцию (sys.settrace) со счетчиком? как счетчик наберет необходимую величину, как-то хитро вертеть ушами. как — не могу сообразить :-)

Virtuos86 ★★★★★
()

Вот как-то с плюсов повелось, что все эти redo можно разрулить банальным i--

for(int=0;i<10;i++) {
    if (i==5) i--;
}

bvn13 ★★★★★
()
Ответ на: комментарий от Virtuos86

Отлично, пусть покажет хоть костыльную реализацию. Мне будет интересно на это посмотреть.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Сб, мне лень... заворачиваем ф-ю в декоратор, достаем в нем исходный код через интроспекцию и дальше ваяем из него че хотим.

AIv ★★★★★
()
Ответ на: комментарий от bvn13

Вот как-то с плюсов повелось, что все эти redo можно разрулить банальным i--

В плюсах у тебя в цикле запросто может оказаться итератор без обратного хода :)

annulen ★★★★★
()
Ответ на: комментарий от AIv

А что ты называешь «оператором»?

достаем в нем исходный код через интроспекцию и дальше ваяем из него че хотим.

Так можно говорить практически про любой ЯП. Только в одних это делается из коробки, а в других лучше даже не начинать. Вот питон относится ко второму типу.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

а что называет оператором ТС?

Лучше даже не начинать в плюсах например. А в питоне такие вещи вполне изящно выходят, я делал;-)

AIv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.