Pull to refresh

Comments 19

Не знаю, как большинство, а я не слышал, чтобы кто-то называл это «нумератором». Общепринятое название «постраничная навигация», или pagination.
«постраничная навигация» — длинно, а pagination на русский красиво не переводиться. Нумератор это ИМХО наиболее точное определение того, что оно делает.
Нумератор лично у меня ассоциируется только с numerator/denominator (:
Думаю, краткость здесь не главный критерий.
Чтобы люди поняли о чем вы повествуете. Я вот по заголовку и первому абзацу не понял.
Можно сделать еще универсальнее. Я когда-то лепил подобное чудо, у меня было несколько блоков: FirstPage, LastPage, FirstNPages, LastNPages, AroundNCurrent, PageListN. Из них можно было составить навигацию практически любого вида путём нехитрой комбинации. Причем вызов был с параметрами, например:

[% PROCESS Paginator pager=p join_by='.' use_first_last_n=1 n_pages=5 n_around=5 show_first_last=1 show_next_prev=1 %]

Так он действительно универсальный :)
Тут не трудно эти аргументы дописать, это уже ИМХО на вкус и цвет (может кто будет <br /> в место точки делать). Кроме того тут важна ещё скорость…
Кстати, по поводу скорости. abs() далеко не безобидная в этом плане функция :)
<zanooda_mode>
Кроме того, если немного позанудствовать, у меня каждый блок делает своё дело без if-ов. В основном блоке Paginator присутствует несколько if-ов вида:

[% IF use_first_last %][% PROCESS FirstPage %][% END %]
[% IF use_next_prev %][% PROCESS PrevPage pager=pager %][% END %]
[% IF use_first_last_n %][% PROCESS FirstNPages pager=pager n=n_pages %][% END %]

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

Далее, мне непонятны два последних условия в этом длинном if-е (строки 27 и 32). В каждом из них выполняется continue, что вызывает переход к строке 13 без инкрементирования в строке 39 с последующим зацикливанием. Либо я недопонял, либо пример с ошибкой.

Далее, первая, вторая и четвертая ветки if-а выполняют одно и тоже действие, так же, как третья и пятая ветки, следовательно их можно объединить в одно условие, чем существенно облегчить жизнь процессору.

В итоге, если разбить длинный цикл на несколько более простых циклов, сократить количество условий, вынести условия снаружи циклов, то можно достаточно весомо повысить эффективность алгоритма в различных ситуациях, что и нужно для универсальности.

P.S. Ну и если мы говорим об универсальности, то в моём случае я могу из разных блоков составлять разные виды навигации, например вида << < 1 > >>, либо вида: < 3 4 5 >, либо просто: 1 2 3 4 5. Чем и достигается универсальность.
</zanooda_mode>
Ваш подход имеет место быть но сильно бы раздул код от размера которого тоже зависит скорость, да и хоть ифы с наружи — их много. Тут проблема if не стоит именно из-за 27 и 32 строки :) этот нумератор проставляет нумера, без полного перебора страниц, и да пример полностью рабочий. У нас просто 2 пустых места это после первых 5 цифр и после 4 цифр+3 точки относительно текущей страницы, и мы эти пустоты проскакиваем в место итерации. В итоге цикл максимум 27 раз итерирует.
На счёт abs с вами не согласен — это смена всего навсего одно бита, причём без сравнения, это достаточно быстрая операция.
На счёт одних и тех же действий — это кок-раз для скорости, тут лишний вызов функции мог бы замедлить работу, а выразительность не улучшилась (одна строчка).
На счёт универсальности — у меня это можно так же сделать причём дописывается это на раз-два — в этом и универсальность (это всё же шаблонная функция, а не просто функция, каждый может немного подкрутить и получить, что нужно).
В целом, посоветую вам посмотреть что-нибудь по оптимизации if/while/for. Дело в том, что у вас на каждую итерация в среднем выполняется 2-3 условия (if-elif-elif). Например, зачем для первых пяти страниц каждый раз выполнять сравнение, если можно его выполнить всего раз и потом просто выполнить простой цикл. Так же для последних, и так же для средних. Нет смысла каждый раз выполнять условные операторы для уже известных ситуаций, это чистый overhead. В общем, если вам интересна тема эффективности, то почитайте. А так же про оптимизацию по скорости и размеру (это касается вашей первой фразы). Вы узнаете много нового и полезного :)

Вот простые примеры: ru.wikipedia.org/wiki/Цикл_(программирование) (предпоследний раздел «Методы оптимизации циклов»), а конкретно вот это: ru.wikipedia.org/wiki/Loop_unswitching
Не поленитесь разобраться в теме, потому как у вас пока очень слабые представления о работе разных операторов, оптимизации и выполнении реального кода на процессоре.

Например, вынеся условие «if current_page < 5 && page_number >= 5» за пределы большого цикла и применив развёртку цикла, можно полностью от него отказаться. То есть выполнив два сравнения и одну логическую операцию можно выбросить 5 итераций и 5 сравнений в вашем случае, а это существенная оптимизация. Так же можно поступить с последними страницами. И даже со средними. Это даст существенный прирост эффективности.

Проблема в том, что С/С++ компиляторы умеют оптимизировать циклы и условия. Но как это делает ваш интерпретатор — неизвестно (скорей всего никак), потому вам необходимо выполнить это самому.

По поводу abs: graphics.stanford.edu/~seander/bithacks.html#IntegerAbs
Обратите внимание на сложность проблемы. Тут стоит разобраться как это реализовано в вашем интерпретаторе. Но вы ошибались как минимум в том, что это одна простая битовая операция.

P.S. Запись:

if (expr1) && (expr2)
    some_job
endif

выразительнее, как минимум не менее эффективнее в работе и точно эффективнее в сопровождении, чем запись:

if (expr1)
    some_job
elif (expr2)
    some_job
endif

Так как при сопровождении вам не придётся менять одно и тоже в трёх местах, это раз. Запись всех условий в одном месте на распыляет внимание и не даёт потерять смысл алгоритма, это два, так как в последствии можно не заметить третью (четвёртую, пятую, ...) ветвь условия. И три, это то, что один IF и логическая функция AND как минимум не хуже двух IF-ов, а зачастую лучше.
Да, конечно же в P.S. операция OR и if (expr1) || (expr2). Подумал одно, написал другое.
Спасибо конечно за совет, но я хорошо осведомлён о оптимизации циклов в том числе у компилируемых языков (в том числе развороты FOR на дифайнах в C). Если будет время я напишу вторую версию этой функции и вынесу ифы наружу, но как я уже говорил проблем с производительностью тут нету… тем более у меня там не просто if а конструкция elif (аналог switch в C).
Главное, что бы выразительность не поменялась…
На счёт abs — там описаны какие то половые проблемы с патентами… :( Я конечно посмотрю реализацию в python но факт есть факт если нам надо получить abs мы должны поменять на 0 первый или последний бит в зависимости от LE или BE.
Только последний идиот будет делать через умножение и возведение в степень. (как проверю отпишусь)
В python abs реализован через сравнение и отрицание. В целом тоже не сильно нагруженная операция. (там просто ещё некоторые проверки для безопасности идут)
> В целом тоже не сильно нагруженная операция
Ага :) Помимо вашего if-а еще в каждом abs-е куча сравнений. Я об этом и говорил, что интерпретатор может подкинуть свинью. Вместо средних 2-3 сравнений на итерацию вы получаете 5-6.

> но как я уже говорил проблем с производительностью тут нету…
а выше фраза
>> Кроме того тут важна ещё скорость…

Я ж почему и начал занудствовать, потому как увидел эту фразу про скорость, так как ваша реализация очень далека от эффективной в плане скорости.

> Главное, что бы выразительность не поменялась…
Она только улучшится. Например, Макконнелл в Совершенном коде нечто подобное советовал, если не ошибаюсь.

> тем более у меня там не просто if а конструкция elif (аналог switch в C)
Я то как раз изначально С-шник, и с позиции С как раз таки и смотрю. Кстати, elif не совсем аналог switch-case, всё таки поведение у них разное.

Я всё к чему это нудил. Дело в том, что вы упомянули важность скорости. Сюда можно добавить нагрузку в виде пользователей и мы получим вот какое дело. Оптимизация вашего шаблона может дать дополнительные несколько миллисекунд выигрыша в рендеринге страницы, а при больших нагрузках это может означать тысячи дополнительных клиентов в секунду. Ясное дело это достаточно сложно проверить на таких маленьких кусках кода. Но если у вас весь проект написан в таком стиле, то это уже серьёзный звоночек о том, что вы зазря расходуете такты процессора, которые зачастую не бесплатны, и забыли про оптимизацию. Для специалиста такой код недопустим ;)
Код допустим там где он допустим. :)
Нет смысла оптимизировать то, что и так пока хорошо работает.
Надеюсь вы согласитесь, что излишняя оптимизация может порубить весь проект? Тут главное, что это вынесено в функцию и её легко будет переписать только в одном месте, что бы всё работало быстрее.
То, что я сказал про скорость — это отличие от тупых нумераторов которые пробегают ВСЕ страницы, тонкий тюнинг возможен почти везде, вплоть до асма.

ЗЫ у меня по рендрингу шаблона пока наверное 100 кратный запас… у меня всё в БД упирается.
Я ж и говорю, что всё это нудистика, зачастую академическая, да. Но с другой стороны, о чем еще подискутировать, как не о мелочах разных? :)

> В glibc к слову так же: return i < 0? -i: i;

В С/С++ проще, там можно хоть макросом, хоть inline функцией написать свою версию и радоваться жизни :) В интерпретируемых так уже не получится.

По поводу abs()-а, если интересно, не так всё просто в стандартных библиотеках.
stackoverflow.com/questions/2878076/what-is-different-about-c-math-h-abs-compared-to-my-abs

P.S. Крайняя степень занудства :) это функция replace() для строк. Тоже дорогостоящая, но это уже когда совсем скучно.
Сегодня попробую помучить функцию если, что выйдет опубликую.
В glibc к слову так же: return i < 0? -i: i;
Sign up to leave a comment.

Articles