Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
На говнохабре http://habrahabr.ru/post/269875/ очередная хуита написана, вот типа напишите сортировку не используя if. Но в том коде есть циклы, любой настоящий программист знает, что циклы это по сути то же самое, что и if(условие) goto куда-тотам. Как написано в одном фундаментальном труде http://www.lib.ru/ANEKDOTY/non_pas.txt - поскольку в Фортране отсутствуют структурные операторы IF,
REPEAT ... UNTIL или CASE, настоящим программистам не
нужно беспокоиться, что они их не используют; кроме того
эти операторы можно при необходимости симулировать с
помощью присваиваемых GOTO.
Пользуясь этой вселенской мудростью, а так же зная про наличие присваиваемых goto в gcc, решил я сей код написать.
Проблема в том что это gccцизм.
Но вот функции лучше.
Во-1 это законченные блоки кода, с неймспейсом и стеком.
Во-2 рекурсивной вложенности можно избежать передав функции адрес тела (указатель на функцию) и условия выхода.
>Во-1 это законченные блоки кода, с неймспейсом и стеком.
Это плохо. Если вызывать функцию по указателю, она отжирает стек. Даже если нет рекурсивной вложенности, все равно на эти создания стека для функции тратится время. Да еще при call пишется адрес возврата, который потом по ret из стека вытаскивается. Неймспейс я могу сам запросто сделать, завернув куски кода в {} чтобы ограничить область видимости, и через goto передавать туда управление. А потом в конце этого {} обратно сделать goto на кусок кода, занимающийся обработкой этого самого шитого кода. http://habrahabr.ru/company/intel/blog/261665/ вот кстати статья на хабре про интерпретаторы, там эта фича с присваиваемыми goto используется. Кроме того, там используется еще вот эта вот https://gcc.gnu.org/onlinedocs/gcc/Global-Register-Variables.htmlзамечательная GCC фича. Настоящие программисты должны оценить
Ну тут трудно что-то утверждать. Насколько я знаю, в интел процыках есть особый механизм кеширования при вызовах процедур: http://www.agner.org/optimize/microarchitecture.pdf page 34 The P1 has no return stack buffer, but uses the same method for returns as for indirect jumps. Later processors have a return stack buffer. The size of this buffer is 4 in the PMMX, 8 in Atom, 12 in AMD k8, 16 in PPro, P2, P3, P4, P4E, PM, Core2 and Nehalem, and 24 in AMD k10. This size may seem rather small, but it is sufficient in most cases because only the innermost subroutines matter in terms of execution time. The return stack buffer may be insufficient, though, in the case of a deeply nesting recursive function. In order to make this mechanism work, you must make sure that all calls are matched with returns. Never jump out of a subroutine without a return and never use a return as an indirect jump.
И я подозреваю, что именно благодаря этому, там эти вызовы процедур оказались быстрее. На каком-нибудь древнем интель-проце или на дохлом арме ситуация может оказаться прямо противоположной
Ну у ARM'ов один уровень этого кеширования тоже есть. Адрес возврата обычно валяется в регистре LR, а не на вершине стека. Если компилятор додумается сохранить LR один раз, а не перед каждым вызовом - пары вызов-возврат будут юзать только LR, даже не прикасаясь к стеку...
Ну даже если и циклы и goto забанены, а tail-call optimization не пашет, можно за небольшую глубину стека сделать дохуя операций (правда количество операций будет ограничено 2^n). В boost preprocessor примерно так сделано невозможное - циклы на макросах.
template <int n>
void loop(void (*action)())
{
loop<n-1>(action);
loop<n-1>(action);
}
template <>
void loop<0>(void (*action)())
{
action();
}
void action()
{
// в action() исполняем один шаг шитого кода, к примеру
}
int main()
{
// код жрёт максимум 64 фрейма стека, но исполняет action() 2**64 раз, чего хватит для любых разумных применений...
// кому мало - можно поставить 128
// для прерывания придётся вызвать exit() или кинуть исключение, если исключения не забанены.
loop<64>(&action);
return 0;
}
Идея: реализовать ветвления с помощью функции Хевисайда. Осталось только её определить, например, как θ(x) = (abs(x) + x) / (2 * x) или θ(x) = round(1 / (1 + exp(-100500 * x)) θ(x) = !!(x > 0).
Тогда max(x, y) = y + (x - y) * θ(x - y); min(x, y) = y + (x - y) * θ(y - x).
Доклад интересный, но не сильно глубокий. Облом в том что у него `One + One` не возвращает `Two`. И все остальное обламывается в этом же подобном духе. И не сильно ново/оригинально - это более или менее переложение мат. языка на язык программирования. Lambda calculus уже слишком высокоурвнево.
Я думаю что тот чудак про формальные языки не слышал, потому что это именно то что он переизобретает, углубляясь в доисторические уровнии ОО, когда оно было теоретической моделью для экспертных систем ИИ.
> > `One + One` не возвращает `Two`
> Ну и пофиг. Оно же возвращает объект, эквивалентный Two. Этого достаточно.
Достаточно, но криво. Потому что Two это не класс, а объект. Потому что Two не знает что оно Two.
С буквами он догадался что не возможно - а вот с числами не догадался. Концепция чисел настолько примитивна, что семантика и синтакс у большинства в голове перемешаны. А вот кодировки и чар-сеты тема более высокоуровневая и там он как бы и споткнулся. Но на самом деле концептуальная проблема та же самая что и с числами: двойка не знает что она двойка, двойка есть двойка потому что это то семантика которую люди приписывают слову "два".
ЗЫ Extreme OO как оно там излагается на самом деле большее Extreme O, потому что там "объектно-ориентированость" отсутствует, и используется почти исключительно "объектность".
Читай выше: "Думаю что short-circuit все же нельзя использовать т.к. это почти то же, что if", "Но в том коде есть циклы, любой настоящий программист знает, что циклы это по сути то же самое, что и if(условие) goto куда-тотам".
Твоя фантазия недостаточно извращена, чтобы реализовать всё это вообще без циклов, условий, свичей, тернарников и short-circuit операторов, guest?
P.S. Да и вместо флагоёбства можно было просто break сделать.
> Как все настоящие программисты знают, единственной полезной структурой данных является массив.
> Если вы не можете выполнить эти работы на Фортране, выполните их на ассемблере. Если же их нельзя выполнить на ассемблере, их не стоит делать вообще.
/s/Фортран/Сишк/
If you ignore the fact that it's structured, even C programming can be appreciated by the Real Programmer: after all, there's no type checking, variable names are seven (ten? eight?) characters long, and the added bonus of the Pointer data type is thrown in. It's like having the best parts of FORTRAN and assembly language in one place. (Not to mention some of the more creative uses for #define.)
Давно читал крео, это помню там за сишку было.
Вообще он прав. Фортран — охуенный язык, из которого произошёл бейсик. алгол и кобол — сакс.
Ещё понравилось: «...компиляторы с проверкой границ массива; эти компиляторы душат творчество, запрещая наиболее интересные варианты оператора EQUIVALENCE и препятствуют модификации операционной системы с помощью отрицательных индексов массивов».
Да... Там каждое второе предложение можно в граните выливать.
Еще доставляет пассаж про правки двоичного кода
Из-за этого настоящие программисты неохотно редактируют уже работающие программы. Они считают более простым непосредственно латать двоичный объектный код, используя прекрасную программу под названием SuperZap (или ее эквивалент на не-IBM машинах). Этот метод настолько хорош, что многие программы, работающие на ЭВМ фирмы ИБМ, не имеют ничего общего со своим собственным текстом на Фортране. В большом количестве случаев первоначальный символьный текст программы вообще не существует. Когда наступает время подправить такого рода программу, никакой администратор даже не думает послать на эту работу кого-либо, кроме настоящего программиста - никакой сосунок (структурный программист) не будут знать даже с чего начать. Это называется защита от несанкционированного доступа.
Таким методом, кстати, можно модифицировать программы на Java, не имея исходников, поскольку она неплохо декомпилируется. И на языках .NET в принципе тоже.
jad очень хорошо умеет восстанавливать из байт-кода исходную программу, даже имена переменных.
Разница с java том что однажды пропатчив слинкованный бинарник получается невосстановимый пиздец, который уже никак не декомпилировать в ЯВУ. После этого просто необходимо иметь в штате такого гения, который будет его сопровождать.
>Но ведь из Алгола произошли Паскаль и Си. Большинство структурированных императивных языков — потомки Алгола.
Эммм. Самое ключевое их отличие было статическая тупизация vs динамическая. Это раз. Ну и компилятор vs интерпретатор — два.
Сишка всё-таки где-то посередине, ближе к алголу (точки с запятой, какие-никакие типы, компилируемость).
Ключевые отличия алгола от фортрана: точки с запятой, типы, структуры данных.
Идейными последователями фортрана я считаю: Бейсик, Питон, Js.
Хоть это и на первый взгляд абсурдно звучит. Но речь именно об идее. И тут они родственны фортрану: в отношении к типам, краткости, простоте, порогу вхождения и отсутствию точек с запятой (js может без них). И все изначально были интерпретируемыми.
Вот все кто пёрлись в своё время от фортрана и бейсика сейчас котируют js и питон.
В JS на первом месте фигурные скобки — он не может быть последователем Фортрана. Плюс JSON и всякая объектно-ориентированная питушня, без которой его уже нельзя представить.
Хотя лет 15-20 назад на JS и пытались писать, как на Фортране (вспомним сайты с document.writeln).
В Питоне тоже на первом месте структурированность кода, хотя там и отступы вместо фигурных скобок. Плюс функциональщина, без которой программиста питонистом считать не будут.
>В Питоне тоже на первом месте структурированность кода, хотя там и отступы вместо фигурных скобок.
Так в Фортране тоже отступы! 6 пробелов.
Идентация и структурированность!
Но только по горизонтали, как в Ассемблере. В Ассемблере код тоже разбивается на вертикальные колонки.
Но вертикальной структурированности в классическом Фортране нет! В Фортране 66, как и в классическом Бейсике, не было этих всяких end if, а ветки разруливали с помощью меток и GOTO.
Когда в Фортране 90 вместо DO <метка конца цикла> ... ввели DO ... END DO, вместо PRINT <метка строки с форматом>, ... ввели PRINT "строковая константа", ..., добавили модульность (в том смысле, как в Модуле), а в реализациях Фортрана при вызове подпрограмм стал использоваться стек (как в Си и в Паскале), что наконец-то позволило совершать рекурсивный вызов, старые фортранщики это посчитали предательством.
*****
Но вообще в Фортране было что-то, что в Паскале и в Си долгие годы было недоступным. Это, например, неявные циклы (когда в некоторые выражения на вход вместо скаляра подаётся массив), «арифметический» IF с тремя ветками (это вам не тернарник с двумя!), а с Фортрана 90 ещё и стандартные функции над массивами. А ещё работа с комплексными числами из коробки!!!11адын-адын-адын
> Как все настоящие программисты знают, единственной полезной структурой данных является массив.
Кстати, у нас есть две крайности:
1. С одной стороны, Царь, который считает, что никакие структуры, кроме одномерного массива не нужны, потому что оперативка представляет собой одномерный массив.
2. С другой стороны, математик-теоретик wxv (не помню, как дальше), который считает, что с помощью массивов можно решить далеко не любую задачу (а факт, что все эти списки, деревья, очереди, стеки, множества и прочие объекты в конце концов в библиотеке языка реализуются с помощью массивов, игнорирует).
Интересно было бы найти то же самое, но наоборот, в wxv-стиле, чтобы сравнить ощущения.
>который считает, что с помощью массивов можно решить далеко не любую задачу
Чушь же. Все структуры — это просто сахар над одномерным массивом.
>Интересно было бы найти то же самое, но наоборот, в wxv-стиле, чтобы сравнить ощущения.
При всём уважении к wvxvw наоборот вряд ли получится.
Там речь о том, что Real Programmer настолько гениален что ему не нужны проверки типов для написания безошибочных программ.
Ну разве что обратная херня: не поближе к машинной реализации. А наоборот абсурдно далёко от неё и запредельно абстрактно.
Где так: настоящий программист пишет программы только на Лиспе и только в EMacs, транслируя их в нумералы Чёрча, которые он потом правит руками.
Кстати, а если на крестах замутить нумералы чёрча вместо базовых типов? Т.е. отбросить не только if, while и т.п., но и вообще все базовые типы и операции над ними. И запилить всё через лямбды.
Проблему пока вижу в общении с внешним миром - линуксовое ядро эти нумералы в syscall'аъ не принимает 🙁
так нумералы Чёрча на то и лямбды, что их можно к вызову любой функции прикрутить
линукс пропатчить, чтобы он указатель на функцию вместо чисел принимал, и применял бы эту функцию к обычному инкременту
вот кусок кода ввода-вывода одной VM, которая поддерживает только комбинаторы и один примитивный тип RealWorld
-- Church to int conversion
s (F f :. N i) = N $ f i
s (F f :. F g) = F $ f . g
-- IO
s (Â' :. N i :. ψ @ (Ψ {})) = ψ {σ = toEnum i : σ ψ}
-- Using  → Â' to indicate that Church → Int conversion has been started
s (Â :. n :. ψ @ (Ψ {})) = Â' :. (n :. F (+1) :. N 0) :. ψ
как видно, мы лифтим хаскельные инкремент, (.), ($) и 0 в VM, а потом позволяем ей доделать всё остальное согласно обычной семантике
Ходишь на лекции по матану, ходишь, ничего толком и не меняется. Интегралы, пространства, подпространства, отображения, ряды... Сплошнейший застой и скука.
А как одну пропустил -- так сразу Хуанос застрелил Альберто, Мария беременна, Чиполлино объелся чеснока, а Чёрч - так вообще s (Â :. n :. ψ @ (Ψ {})) = Â' :. (n :. F (+1) :. N 0) :. ψ.
Еще одна цитатка, с небольшой заменой актуальна и ныне:
Из собственного опыта, я думаю, можно смело сказать, что будущее прекрасно для настоящих программистов. Ни Linux, ни Сишка не высказывают ни каких признаков отмирания, несмотря на усилия программистов на С++. Даже такие изощренные уловки, как добавление RAII, конструкций объекто-ориентированного и функционального программирования в Сишку, провалились. Да, конечно, некоторые выпустили компиляторы С++, но каждый из них
оставил возможность перейти в режим компилятора С89 с помощью добавления одного флага — чтобы управлять памятью как предписано Богом.
сишка не умрет до тех пор, пока:
а. остается написанный на ней актуальный код
б. остаются упрямые кодеры, которым легче всю жизнь вручную гонять байты, чем освоить ооп и шаблоны
Итого, сишка будет жить вечно, потому что кодеры из (б) будут писать код из (а), называя всех с++-кодеров крестоблядьми
Шаблоны - говно. Если вы пишите некую компилтайм-херню на этих шаблонах, например сортировку пузырьком на этапе компиляции, вы эту херню нигде кроме как в компилтайме в плюсах применить не сможете. И если надо не в компилтайме сортировать некую хрень, надо будет писать сортировку заново. И возникает вполне логичный вопрос - а нахрена вообще писать эти шаблоны? Ведь если надо компилтайм, можно написать обычный (не-компилтайм) код, и использовать его тупо как дополнение к препроцессору. Можно вызывать произвольную консольную хрень из m4 https://www.gnu.org/software/m4/manual/m4.html#Syscmd
Шаблоны - это стандартный механизм введения компайлтайм-абстракции. Введенный, чтобы каждый из умников типа вас не городил свой костыльный препроцессор, чтобы потом не приходилось их запускать десятками перед сборкой любого более-менее большого проекта. Чтобы повысить переиспользуемость кода. И говорить, что шаблоны это плохо только потому, что в других языках подобного механизма нет как минимум глупо. Например, в скриптовых языках вся инфомация о типах уходит в рантайм, а в Go препроцессинг как раз таки реализован как вы предполагаете - костыльно.
Шаблоны - это стандартный но хуевый механизм введения компилтайм-абстракций. Хуевый т.к. код надо для компилтайма и рантайма дублировать.
Вот https://ideone.com/RHz5vW С++ (если б не было говношаблонов(например в чистом Си), я мог бы нагерить код функций через genfunctadd). А вот пример на языке D http://melpon.org/wandbox/permlink/22k35NmuFvFuVGKu где это делается одним единственным кодом.
Всмысле "дублировать"? Зачем вам одно и то же в компайл и ран тайме? Если это посчитано на этапе компиляции, незачем считать это в рантайме. Более того, начиная с c++11 есть специальный модификатор constexpr, который позволит посчитать результат той или иной функции в компайл-тайме, если для этого хватает данных.
Приведенные вами примеры генерации кода намного хуже, чем шаблоны.
@j123123 имеет в виду, что полезно использовать обычные функции языка при метапрограммировании на этом языке. В C++ для этого нужно реализовывать компайл-тайм версии и рантайм-версии отдельно: первые - метапрограммируя на шаблонах, вторые - на обычном языке. Загляните в Boost.MPL, там полно алгоритмов из STL реализовано заново только для того, чтобы ими можно было пользоваться в компайл-тайме.
Проблема известна, и недавнее введение в язык constexpr направлено как раз на её частичное устранение.
В лиспе, к примеру, компилятор может использовать в компайл-тайме встроенную или пользовательскую функцию, работающую со списками, для трансформации AST.
https://ideone.com/RHz5vW
Этот пример даже не я сочинил, а спорящий со мной @j123123. Просто сравните читабельность двух процедур. А если надо написать что-то сложное?
>Зачем вам одно и то же в компайл и ран тайме?
Как зачем? Нужны конкретные примеры? Ну хорошо, допустим я хочу сделать некую хеш-таблицу
{"Петр Петрович", "$200", hash1}
{"Петр Иллич", "$1488", hash1}
{"Иван Петрович", "$400", hash2}
{"Петр Иванович", "$700", hash3}
{"Владимир Владимирович", "$10000000", hash2}
"hash?" это некое числовое значение(корзина), и у меня есть еще некая функция, которой если передать "Петр Петрович" то оно вернет число hash1, и потом я могу быстро найти. В начале у меня есть просто набор:
"Петр Петрович", "$200"
"Петр Иллич", "$1488"
"Иван Петрович", "$400"
"Петр Иванович", "$700"
"Владимир Владимирович", "$10000000"
без всяких хешей. Я хочу в компилтайме получить уже инициализированную хеш-таблицу. Я могу сделать это на этих гребаных шаблонах в компилтайме, но если я хочу еще в процессе работы править эту хеш-таблицу, например добавлять новых людей, то разве мне этот компилтайм-код поможет? Мне придется писать код для работы в рантайме, который дублирует функционал шаблонного кода. А нафиг мне вообще эти самые шаблоны, если я своим рантайм кодом могу накодогенерировать что мне надо, и просто заинклудить это в нужное место?
Да запросто. Берем какую-нибудь готовую сортировку, например сортировку пузырьком на Си, пытаемся через constexpr этот код адаптировать, чтобы я мог инициализировать массив таким образом, т.е. чтобы сама сортировка происходила на этапе компияции, и сортированные числа просто записывались в память. Например вот этот вот код http://rosettacode.org/wiki/Sorting_algorithms/Bubble_sort#C
Вот что получилось: http://goo.gl/bhmKTO
Это
.L16:
test r8d, r8d
jne .L15
test r13b, r13b
jne .L80
.L18:
cmp BYTE PTR [rsp+15], 0
je .L19
mov DWORD PTR [rsp+64], ebx
.L19:
cmp BYTE PTR [rsp+7], 0
je .L20
mov DWORD PTR [rsp+72], edi
.L20:
cmp BYTE PTR [rsp+6], 0
je .L21
mov DWORD PTR [rsp+76], r10d
.L21:
cmp BYTE PTR [rsp+5], 0
je .L22
mov DWORD PTR [rsp+80], esi
.L22:
cmp BYTE PTR [rsp+12], 0
je .L23
mov DWORD PTR [rsp+84], ecx
нихрена не компилтайм, это говно какое-то
А с более старым стандартом эта сортировка вообще не хотела компилироваться в constexpr. Если вы мне покажете нормально отрабатывающую сортировку пузырьком, честно отрабатывающую в рантайме и сделанную на основе (и с минимальными изменениями) кода http://rosettacode.org/wiki/Sorting_algorithms/Bubble_sort#C я буду удивлен
Да, серьезно. Представьте что у меня есть некий готовый код на Си, который делает нечто полезное, и я хочу этот код вызывать в компилтайме для генерации каких-то данных и/или кода
Или я например хочу один раз написать некий код, делающий что-то полезное, и при этом хочу уметь его использовать и в чистом Си для рантайма, и в этих constexpr-ах
http://habrahabr.ru/post/228181/
Пример о том, как вам достичь желаемого и не только. Правда, использовать придется с++14. А написать один раз, чтобы сразу двух зайцев - ну, мб, дефайны помогут, хотя проще будет написать два куска для разных случаев
Проще написать один кусок для рантайма, скомпилировать его и потом использовать для генерации кода, через printf, std::cout << или любым другим удобным способом.
Нет, я хочу сказать что мне хочется написать код ОДИН РАЗ, а не писать отдельный вариант для компилтайма(с этими constexpr и дурацкими ограничениями) и отдельный для рантайма. Шаблоны и constexpr мне такой возможности не предоставляют.
вы хотите написать один код, который будет работать в ПЛЮСАХ в компайлтайме и в СИ в рантайме. Напишите мне один код, который будет в лиспе работать в компайлтайме, а в питоне - в рантайме.
А причем тут лисп и питон вообще? Я вот взял код, на СИ (сортировка пузырьком) который в рантайме работает, собрал его ПЛЮСОВЫМ компилятором, и он у меня отлично заработал и на плюсах. Таким образом, этот код на СИ является вполне корректным кодом на ПЛЮСАХ
То есть, если есть некоторое свежее мнение, которое противоречит мнению толпы, то человек сразу не разобрался в вопросе?
Послушайте, просто послушайте, что Вам говорит j123123, подумайте над этим.
1. В языке C++ есть возможность писать код, который работает во время компиляции и код, который работает во время исполнения
2. В принципе, на шаблонах можно написать всё то же самое (тьюрингова питушня и всё такое) за исключением ввода-вывода и с ограничением на вложенность шаблонов (Простого -ftemplate-depth хватает GCC для решения проблемы останова, над которой Тьюриг, CHayT и их апостолы бьются в математических спорах вот уже тысячелетия), но заново, что в нашем мире библиотек и переиспользования неприемлемо.
3. C++ и шаблоны C++ - это не python и lisp, это не два языка, это один язык, поэтому логично было бы желать иметь единообразное описание программ.
При чем тут "свежее мнение"? Человек объясняет, что шаблоны и другие фичи в плюсах говно только потому, что их нет в си. Логично? Нет. Фича, которая его интересует (constexpr), есть в плюсах? Есть. Но, плюсы говно, потому что фичи нет в си. Логично? Нет.
При правильном использовании, функция, результат которой будет вычисляем на этапе компиляции, будет отличаться от обычной функции только словом constexpr. Логично? Да. Человек приводит пример НАМЕРЕННОЕ нарушение требований constexpr и вообще банальной человеческой логики (constexpr void не несёт смысловой нагрузки) как аргумент против его использования. Логично? Нет. Это как жаловаться на язык потому что компилятор не умеет разбирать заведомо некорректную конструкцию.
Последнее: шаблоны - прослойка поверх синтаксиса плюсов, отвечающая за метапрограммирование. Логично? Да. И это лучше, чем запихивать код в строки и редактировать его вручную.
> Человек объясняет, что шаблоны и другие фичи в плюсах говно только потому, что их нет в си
Не объясняет. Речь об ограниченности шаблонов в C++ и проблемах C++.
> Человек приводит пример НАМЕРЕННОЕ нарушение <...> банальной человеческой логики
Знаете, вот есть "по закону", а есть "по совести". Есть банальная человеческая логика, а есть здравый смысл.
Логика часто надеется на то, что программа выполнится за конечное и крайне малое время. В реальности же возникает бюрократия. Справка о том, что нужна справка, с подтверждением личности по паспорту, удостоверениям и кипе документов - понятие логичное, но крайне бессмысленное.
Кто-то логичный сказал: "Давайте я решу на песке уравнение и пойму, кто из людей более ценный, чтобы его спасти", а кто-то здравомыслящий успел спасти, из-за него кто-то не утонул.
Также логика точна, а здравый смысл работает с вероятностными величинами. Можно долго спорить о иррациональных числах и бесконечное количество времени вычислять число Пи, это логично, но можно работать с погрешностью. 3.14+-0.01 - всё то же старое Пи, заданное корректно с погрешностью, уместной в нашей задаче.
Вы смотрите на проблему с точки зрения логики, посмотрите на неё с точки зрения здравого смысла.
компьютер всегда работает исходя из логики, а не здравого смысла. Более того, если ваш здравый смысл противоречит логике, возможно, он не такой уж и "здравый"?
Логика - упрощённая модель здравого смысла. Грубо говоря, компьютер распознаёт лица по логике, а человек узнаёт, пользуясь здравым смыслом.
В общем-то затем и нужен программист, чтобы аппроксимировать здравый смысл в бинарную логику.
Если здравый смысл противоречит логике, значит эта модель (логика) здесь неприменима или недостаточна, нужно придумать логику получше.
> http://habrahabr.ru/post/228181/
Вообще, очень показательный пример. Предположим что уже есть некая готовая кем-то написанная обычным способом функция на С/C++(уверен что есть), делающая разбор выражений вида "(4^2-9)/8+2/3". НО использовать ее в этих constexpr нельзя, и надо заново это все переписывать с учетом ограничений. Так почему бы вместо того чтобы переписывать, не сделать генерацию через эту функцию? Притом этот код на constexpr будет ТРУДНЕЕ для понимания, чем код, написанный в обычном стиле на тех же плюсах.
Тут пришлось изменить сигнатуру функции, это достаточно существенное изменение. Если вместо сортировки пузырьком взять некий реальный код, делающий нечто полезное, придется очень много переделывать. Надо ведь учитывать, что в реальном сишном коде(который при этом успешно соберется с++ компилятором, поэтому он будет и с++ кодом) вполне могут быть вызовы функций из стандартной библиотеки языка Си, например strcpy, memcpu, malloc итп. которые constexpr-ами не являются и которым придется искать замену, чтобы это работало в constexpr. Кроме того, из этого кода могут вызываться другие не-библиотечные функции, которые constexpr-ами тоже не являются, и для всех для них придется менять сигнатуру таким образом, чтобы их можно было объявить constexpr.
Я уж не говорю о таких "мелочах", как то, что этот constexpr-код сортировки в компилтайме отрабатывает очень долго по сравнению с тем, как если эту сортировку скомпилировать обычным образом и передать ей на вход массив, который надо отсортировать. При сортировке больших массивов он отрабатывает так долго, что clang начитает предполагать что там бесконечный цикл, и прекращает сортировку компиляцию, а gcc падает с internal compiler error(судя по всему от нехватки памяти).
си знает классы, лямбды, шаблоны и пр.? Нет. С++ код не является си кодом. Я не менял сигнатуру ФУНКЦИИ, я менял сигнатуру ПРОЦЕДУРЫ, а процедура не может иметь смысл как constexpr, и это потому что это логично, а не потому что язык говно. А обновлять и расширять легаси код всегда сложно, опять же, независимо от языка
>си знает классы, лямбды, шаблоны и пр.? Нет. С++ код не является си кодом.
С++ код в общем случае не является С кодом. Но С код почти всегда можно собрать С++ компилятором(есть конечно пара-тройка несовместимостей, но это мелочи, можно поправить) и он будет корректно работать. Таким образом, очень большое количество С кода является С++ кодом.
>Я не менял сигнатуру ФУНКЦИИ, я менял сигнатуру ПРОЦЕДУРЫ
В языке СИ нет деления на процедуры, подпрограммы и функции, здесь вся программа строится только из функций. Есть понятие функции, возвращающей void.
>а процедура не может иметь смысл как constexpr, и это потому что это логично, а не потому что язык говно.
А меня не волнует. Допустим у меня есть работающий код из функций(или процедур если угодно), написанных в обычном Си стиле. Когда писался этот код, никаких constexpr-ов в плюсах не было, и естественно код писался в обычном стиле, а не так, чтобы потом если вдруг в плюсах появятся constexpr-ы то чтобы оно на них легко портанулось. Очевидно, что этот код просто так не засовывается в этот constexpr, и чтобы его туда засунуть, надо очень много переписывать, а заниматься такой ерундой никто не собирается. Так что если я вдруг захочу использовать этот код в компилтайме для генерации неких данных, я просто скомпилирую этот код обычным образом, прикрутив к нему вывод через printf или std::cout <<, запущу его и потом просто использую этот полученный вывод.
Питон разумеется был спроектирован на основе си, добавляя в него такие нужные вещи, как классы и шаблоны, и сохраняя обратную совместимость насколько возможно
В Вашем комменте Питон заменить на с++, смысл не изменится. Может хватит проектировать языки на основе уебищного си, а может стоит посмотреть в сторону паскаля скажем.
1. Абстрактный код, который вы хотите переделать, писался не на плюсах. Это не "стиль", это ДРУГОЙ язык программирования. Читайте это циклом пока не поймете.
2. Функция, которая ничего не возвращает назвыается процедурой. void - это как раз таки "ничто".
3.
> >а процедура не может иметь смысл как constexpr
> А меня не волнует.
"Я хочу чтобы void в компайлтайме пересчитался в отсортированный массив" - это финиш
>Это не "стиль", это ДРУГОЙ язык программирования.
Почему же другой? Если он корректно компилируется С++ компилятором и корректно(так же, как если бы его компилировали С компилятором) выполняется, то этого разве недостаточно?
>Функция, которая ничего не возвращает назвыается процедурой. void - это как раз таки "ничто".
Хорошо, если вам такая терминология больше нравится, будем называть функцию, возвращающую void процедурой. В СИ это всего лишь частный случай функции, не более.
>"Я хочу чтобы void в компайлтайме пересчитался в отсортированный массив" - это финиш
А что не так? По-вашему, сделать это не представляется возможным? Или это как-то "методологически неправильно" ?
другой потому, что си не поддерживает парадигму ооп.
Сколько информации может нести переменная типа void? 0, черт подери, 0!, КАРЛ! Мы говорим о языке со СТАТИЧЕСКОЙ типизацией, где, в отличие всяких лиспов/питонов/жабаскриптов нельзя вместо true/false засунуть хеш или обратиться к 10-му элементу null-а и не получить ошибки.
если я пишу std::vector<int> я уже гарантированно пишу не на си. Если я пишу constexpr я гарантированно пишу не на си. Если я пишу template я гарантированно пишу не на си. Да какого хрена вы вообще припрели си? Он ни при чем. Более новый функционал не может поддерживаться более старой версией языка. Хотите пользоваться новым функционалом - переписывайте код.
Речь вообще была о с++. В с++ есть интересующий вас функционал? Да. Неумение им пользоваться - ваш личный геморрой. Всё. Вопрос закрыт.
Неумение? Это функционал хреновый т.к. не позволяет мне переиспользовать уже написанный код. И вынуждает возвращать что-то через return вместо того, чтобы менять через указатели, переданные в аргументах.
Функционал позволяет переиспользовать уже написанный код. Просто для этого нужно этот функционал использовать, причем использовать правильно.
Функция, меняющая состояние окружения в принципе не может являться выражением времени компиляции, т.к. логика работы программы зависит от времени вызова такой функции. И это правильно. То, с чем вы сравниваете, можно сделать хоть на си - написать препроцессор, который будет компилироваться на основе тех же файлов и заменять код основной программы перед её компиляцией.
> Функция, меняющая состояние окружения в принципе не может являться выражением времени компиляции, т.к. логика работы программы зависит от времени вызова такой функции. И это правильно. То, с чем вы сравниваете, можно сделать хоть на си - написать препроцессор, который будет компилироваться на основе тех же файлов и заменять код основной программы перед её компиляцией.
Это теоретические вопросы. На практике как нечто полезное используется не состояние программы, а её результат. Нужно лишь попросить программиста предоставить какой-то результат.
Можно определить некоторые переменные, которые будут экспортированы.
Например, обозвать их compiletime:
compiletime int x;
compiletime int*y;
int z;
int ct_main(){
y = malloc(10);
x = 5;
z = 2;
if(y) y[0] = 6;
}
int main() {
// z - не определена
// y [0] - 6
printf("%d", x);
if(y) free(y);
}
Во время компиляции исполняется ct_main, после этого при запуске в compiletime-переменных хранится то, что было в них в момент завершения ct_main.
Возможно, malloc/free/конструкторы/деструкторы патчатся, чтобы сохранить структуру экспортируемого объекта. Возможно, malloc и конструкторы должны выполняться при исполнении. Т.е. если есть динамический compiletime массив с посчитанными на этапе компиляции знаками пи, во время исполнения создаётся int*/std::vector с вызовами malloc/new/конструкторов и заполняется посчитанными знаками.
Т.е. осуществляется сериализация/десериализация объектов.
Если, скажем, статические поля класса как-то изменялись, но не были помечены compiletime, там им и место, они останутся неинициализированными, программист явно указал экспортируемое состояние, посчитать которое стоило больших, но однократных усилий.
malloc это функция размещения памяти в куче. Так как вы не можете гарантировать, что по адресу 0xXXXXXXXX вы сможете разместить N байт памяти, значение *y не может быть определено на этапе компиляции через malloc. Это не недоработка плюсов, просто так компьютеры сделаны.
*y может быть посчитан только если есть статические константные N байт памяти программы, на которые он указывает. То есть для компайл-тайм вычисления сначала должны быть определены данные (которые зашьются в тело программы), а только потом вычисляется указатель на них. Поэтому
constexpr struct { int data[3]; } getData() { return {{1,2,3}};}
возможно, а через указатели - нет.
> Так как вы не можете гарантировать, что по адресу 0xXXXXXXXX вы сможете разместить N байт памяти, значение *y не может быть определено на этапе компиляции через malloc.
Я же говорю, что
1. malloc может быть пропатчен.
2. важны данные, а не их положение, получение и т.д.
Понятно, сложно или даже невозможно восстановить всё состояние программы. Но ведь тут надо работать как инженер, а не как математик. Возможно с таким приближением - делаем с приближением.
(Кстати, на этапе исполнения абсолютно не важно, что вернёт malloc. Зачем копировать указатели, если они не несут смысла?)
Что Вы делаете?
1. Вычисляете число Пи
2. Записываете знаки в блокнот
3. Пишете программу
Пусть кто-то вычислял Пи на этапе компиляции.
Известны знаки числа Пи после компиляции? Можно их сохранить? Да и да.
А значит компилятор может создать программу, которая вызывает malloc и заполняет массив.
Или сериализация/десериализация в принципе не возможны, их вообще в мире не существует?
ВНЕЗАПНО, во время компиляции никто не мешает выделять память В КУЧЕ, и потом байтиками из выделенной памяти инициализировать некие массивы. А стека может элементарно НЕ ХВАТИТЬ при работе с большими объемами данных
Так и с кучей всё аналогично, только нужно чуть серьёзнее переписать компилятор.
Вариант первый (выше я его упоминал)
1. Для каждого типа, который участвует в экспортируемых в рантайм выражениях (тип экспротируемой переменной, типы её полей, типы полей их полей и т.д.), генерируется дополнительный конструктор инициализации; для malloc генерируется конструктор массива.
2. Во время компиляции за каждой создаваемой переменной следят, чтобы определить способ её построения. Т.е., какие конструкторы надо вызвать, чтобы получить такую переменную. Но с ограничениями: не помнить иерархию изменений, не следить за кастами указателей в инты и т.п.
Например, был указатель и блок памяти длиной 8 байт, сделали placement new и сконструировали объект типа A размером 8 байт, затем сделали placement new и сконструировали объект B размером 4 байта и экспортировали указатель. Во время исполнения под указатель выделится 8 байт, первые 4 заполнятся объектом B, остальные не будут определены. Если в экспортируемом объекте был указатель на объект, который сейчас в памяти, о котором есть метаданные (кто-то вызвал malloc или конструктор), то записывается сожержимое, иначе - error/warning для компиляции или же нулевой указатель.
3. Во время исполнения вызываются специальные конструкторы и пропатченный malloc, заполняющие экспортированные объекты вычисленными на этапе компиляции значениями. Объекты становятся такими же, как будто бы программист вручную написал код как в предыдущем моём комментарии. Т.е. их можно изменять, удалять и т.д.
Вариант 2
1. Определяем правила для compiletime-классов: есть конструктор, инициирующий все поля; все поля - compiletime-типы.
2. Просим пользователя на этапе компиляции засунуть результат в compiletime-типы. Иначе - ошибка. Т.е. на этапе компиляции программа творит любые непотребства, но экспортирует их в строгие compiletime-типы.
3. На этапе компиляции следим только за созданием compiletime-типов.
4. На этапе исполнения восстанавливаем экспортированные объекты как в варианте 1.
Возможно, потребуется создать шаблонный псевдокласс std::compiletime<T>, который будет автоматически преобразовывать T в compiletime-тип. На этапе исполнения std::compiletime<T> будет иметь дополнительный конструктор, но более никак не будет отличаться от T.
compiletime int x;
compiletime int*y;
compiletime std::vector<std::vector<int>*> z;
int ct_main(){
int* m = malloc(10); // m - просто какой-то адрес; его нельзя будет экспортировать
std::compiletime<int> k = std::compiletime_malloc<int>(10);
// k - адрес фигни, которую можно вычислить и сделать частью экспортируемого объекта
y = k;
x = 5;
if(y) y[0] = 6;
std::compiletime<std::vector<int>*> z0 = new std::compiletime<std::vector<int>>;
// z0 - адрес фигни, которую можно вычислить и сделать частью экспортируемого объекта
z0.push_back(1);
z.push_back(z0);
}
int main() {
printf("%d", x);
if(y) free(y);
}
Там очень няшный компайлтайм, не то что крестоблядское уёбище по имени constexpr. Даже файлы читать можно. Файлы в компайлтайме, Карл!
Насчёт практичности всего этого - протобуф без внешних генераторов, просто либой. Регулярки с компайлтайм парсингом и статической проверкой. Сериализация без бойлерплейта и генераторов. Разве всё это нинужно?
> Хуевый т.к. код надо для компилтайма и рантайма дублировать.
статика в тьюринг-полных языках по-хорошему всегда должна и будет иметь другую семантику, нежели рантайм, поскольку мы хотим, чтобы компиляция завершалась
(не берём в расчёт кресты -- там своя атмосфера)
type-foo в haskell тоже пишется отдельно, и это правильно
Вот занятный пример: человек запилил проверку SQL-запросов в compile-time. https://github.com/darioteixeira/pgocaml
Правда, компилятору коннект к базе нужен, лол 🙂
Если код для работы в компилтайме тьюринг-полный, то вне зависимости от того, совпадает или не совпадает семантика кода-для-компилтайма с обычным кодом, компиляция у нас может не завершиться
А чем она мешает? Не нравится(или как вариант - не нужна для текущей задачи) - не используй. А когда будет нужна - используй.
Или хочешь сказать, что она вообще никогда не нужна? Если да, с этим утверждением я готов поспорить
> статика в тьюринг-полных языках по-хорошему всегда должна и будет иметь другую семантику, нежели рантайм, поскольку мы хотим, чтобы компиляция завершалась
рантайм в тьюринг-полных языках по-хорошему всегда должен и будет иметь другую семантику, нежели рантайм, поскольку мы хотим, чтобы исполнение программы завершалось
Лол. Суть начала шахмат - развитие.
>вдруг любым первым ходом они ставят себя в невыгодное положение?
А для этого есть возможность скипнуть ход (конём туда-обратно или пешкой не через два поля, а по одному). И тогда чёрные становятся белыми.
Чем же мешают эти undecidable проблемы? Зачем что-то решать (кстати, факторизация - decidable?), когда можно подождать разумное время и убить. Кажется, wvxvw нанял вместо себя тролля-теоретика.
> Чем же мешают эти undecidable проблемы?
они стрёмные
> кстати, факторизация - decidable?
да
>когда можно подождать разумное время и убить
разумное время подождать и убить, смышлёное пространство убить сразу, а истеричную энергию избить в переулке
> тролля-теоретика
wow rude
> они стрёмные
Это какой-то уход в мир грёз и поиск виновных ради раскрываемости... Я не дождусь, пока компьютер посчитает от 1 до N >> 1 с вероятностью не менее 1 - 1/N, а фанаты Тьюринга что-то про вычислимость говорят. А в то же время где-то крутится сервер с while(1) { processRequest(); } и всем дарит счастье.
>Я не дождусь, пока компьютер посчитает от 1 до N >
> а фанаты Тьюринга что-то про вычислимость говорят
Ээээ.
А подскажите пожалуйста, а для каких чисел N зависнет цикл дописывающий сзади 1 к бинарному представлению N после чего добавляющий к результату исходное N, в случае если N - нечётное,
и убирающий из конца бинарного представления все 0, если N - чётное.
Для положительных значений Int32 требуется в среднем 175 итереций для того, чтобы достичь цели. Максимум - 971 итереция для числа 1674652263, среднее количество итереций растёт логарифмически.
Думаю, даже для UInt64 выдержит, вычисление упрётся в длинную арифметику.
В среднем наверно хватит на 1К десятичных знаков, но где-то там может встретиться роковое число, из-за которого зависнет.
А еще крестоблядские шаблонопидарасы настолько выживают из крестоума, что крестобляские крестопрограммы крестокомпилируются крестогодами, из-за того что Александреску когда-то просто прикололся.
... Внезапно АНДРЕЙ увидел ВЫЧИСЛЕНИЕ ЧИСЕЛ ФИБОНАЧЧИ ВО ВРЕМЯ КОМПИЛЯЦИИ НА C++ и замер. В его уставшей, больной голове что-то щелкнуло - он нашел, что искал. Он начал читать книги по C++. Чем дальше он проникал в тайны C++, тем больше он понимал, что этот язык создан для него. Мерзкие извращения, которые он наблюдал на страницах, глубоко резонировали с его истерзанной и едко ненавидящей все светлое душой. Его глаза наливались кровью от удовольствия и слезы текли по щекам, от осознания, что на свете есть люди, не намного менее больные, чем он. АНДРЕЙ понимал, что скоро ему сделают третью лоботомию и тогда он вряд ли сможет написать книгу. Времени до третьей лоботомии оставалось немного и АНДРЕЙ решил начать писать книгу прямо сейчас. "THE TIME IS NOW, ANDREI", сказал он вслух самому себе на ломаном английском с выблядски кривым акцентом и начал писать. Вначале он не знал, в чем суть того, что он пишет. Но со временем картина стала ясной как день. АНДРЕЙ взял самый гнилой, уродский и омерзительный язык программирования и решил довести его до уровня сумасшествия, до сих пор невиданного в мире людей. Первый (и последний) технический рецензент его книги, сошел с ума и убил всю свою семью, после прочтения нескольких глав. Узнав об этом АНДРЕЙ смеялся, пока не потерял сознание. АНДРЕЙ понимал, что все идет как надо. Сразу после того, как он дописал последнюю главу, ему сделали последнюю лоботомию и писать книги ему больше не хотелось. Представители издателя взяли книгу АНДРЕЯ и, согласившись ее издать, спросили у него, как бы он хотел ее назвать. На ломаном, кривом английском он ответил: "MODERN C++ DESIGN: GENERIC PROGRAMMING AND DESIGN PATTERNS APPLIED BY ANDREI ALEXANDRESCU". Его акцент был настолько уебищен, что представители издателя начали ржать, с такой силой, что моча начала струиться по их ногам. Но, слишком поздно они поняли, что это была моча АНДРЕЯ...
P.S. Кстати, в этом треде собрались удивительно нелогичные люди, которые, с одной стороны, погрязли в формализме, математике и логике с ног до головы и отрицают полезные нововведения, а с другой - пользуются существующими хаками и нелогичностями. То есть, если бы я говорил, что число Пи можно записать и использовать, они бы сказали "да как же, нет, нельзя, теория, формализм", а как только какой-нибудь авторитет показал бы им Пи в double и плавающего питуха, повсеместно используемого, так сразу можно.
Иными словами, некоторые пользователи ГК верят в логику ровно до тех пор, пока какой-нибудь авторитет вроде Страуструпа не покажет им авторитетный нелогичный метод решения их проблем вроде kill и double.
>Вы так говорите, как будто это что-то плохое.
Не знаю. Всегда думал что для любого по-настоящему ленивого чело программиста dry и code reuse не пустые звуки.
К тому же джве разных версии кода имеют свойства разъезжаться при долгом маинтенсе.
>хотят использовать один код и на сервере и на клиенте...
На мой взгляд это единственное что сколь-нибудь оправдывает серверный js. Хотя тут конечно поджидает неприятный нюанс в виде невозможности использования новых фич (надо чтоб в старых браузерах).
> неприятный нюанс в виде невозможности использования новых фич (надо чтоб в старых браузерах).
А вот arrow functions у меня в node не работают. Хотя я, кажется, обновлял недавно.
Хотя, есть же трансляторы. Особо умные уже пишут на ES7.
Итого: пишем на ES6 или ES7, код автоматически приводим к ранней версии JS (делая его пригодным как минимум для серверной стороны) и переиспользуем.
> А вот arrow functions у меня в node не работают. Хотя я, кажется, обновлял недавно.
Нет, уже работают. Таки давно обновлялся.
Это на далёкой машине с линуксом недавно обновлялся, только там в репозитории была версия времён Ленина.
Вот такая штука https://en.wikipedia.org/wiki/Haxe есть, которая во что попало компилируется. И на ней можно писать клиент-серверный код
Bytecode Targets produce executable byte code (Neko, SWF, SWF8), that can be executed directly by the runtime (Neko VM, Adobe Flash Player, Adobe AIR). Haxe API and platform-specific API is available.
Language Targets produce source code (AS3, C++, C#, Java). Most source code must be compiled by a third-party compiler to produce an executable file (Flex SDK, GCC, Microsoft Visual C++, Microsoft .NET, Java compiler). JavaScript and PHP code can be run directly, since the runtime uses just-in-time compilation. Inline code written in the target language can be inserted at any point in the application, thereby supporting the entire platform API; even features missing from the Haxe wrapper API.
>Кстати, штуку "что угодно в что угодно" уже создали?
Не слышал о таком. Но наверяка это возможно. Только вот если захочется x86 ассемблер откомпилировать в java например, это будет ооооочень медленно
Turning a word into a question by appending the syllable ‘P’; from the LISP convention of appending the letter ‘P’ to denote a predicate (a boolean-valued function). The question should expect a yes/no answer, though it needn't. (See T and NIL.)
At dinnertime:
Q: “Foodp?”
A: “Yeah, I'm pretty hungry.” or “T!”
At any time:
Q: “State-of-the-world-P?”
A: (Straight) “I'm about to go home.”
A: (Humorous) “Yes, the world has a state.”
On the phone to Florida:
Q: “State-p Florida?”
A: “Been reading JARGON.TXT again, eh?”
[Once, when we were at a Chinese restaurant, Bill Gosper wanted to know whether someone would like to share with him a two-person-sized bowl of soup. His inquiry was: “Split-p soup?” — GLS]
Сейчас же почти что угодно компиляется в JS. Поэтому вполне можно писать и клиент, и сервер, на каком-нибудь Haskell / Clojure, и генерировать клиентскую часть соответствующим компилятором хоть в asm.js. По сути ведь в GWT эту задумку и реализовали, причём уже довольно давно.
Конечно, на клиенский код будут ограничения, но и ведь не любой нодовый JS можно на клиенте использовать.
А уж компилять новый диалект JS в старый при сборке вообще (в теории) не проблема.
Конкретно в этом приложении -- да.
Но оно было ОЧЕНЬ простое. Полезность питона в огромной стандартной библиотеке, и я не думаю что вот прямо вся она отлично работает в браузере.
Хотелось переюзать часть бизнес-логики чтобы валидировать данные на клиенте очень быстро, не засылая запрос на сервер.
Логика уже была написана на питоне.
Но я не считаю это серьезным решением: как минимум они не умеют тройку.
А вот GWT я видел в продакшене еще в 2009-м году. Это было single page application которое сосало данные через COMET (вебсокетов тогда еще не умели), и дело было не в переюзании логики, а в нежелании писать на javascript)
только заради code reuse валидаторов ввода на форме?
там в визуальщине и метапитушне (где какой инпут зависит от другого, чтобы каскадно их апдейтить, с шашечками как в экселе) больше работы, чем в непосредственно проверке собранной модели на валидность
Там не просто валидация, там были довольно мутные фукнции, некоторые из которых были документированы только их реализацией на питоне:)
Писать ДВА раза бизнес-логику это вообще самый ужасный ужас, который может случиться.
Другой вопрос что можно было описать ее языконезависимо и генерить(или программировать) по ней клиент и сервер. Жаль что нет каких-то стандартных языков для этого (ну или мне они неизвестны)
> ДВА раза бизнес-логику
Дак в том и дело, что бизнес-логика на клиенте и на сервере всё равно различна. Один клиент, другой - сервер. Клиент вообще может что угодно заслать - апи то открытое, всё видно в F12, исходники (даже обфусцированные, но исходники) клиента у злоумышленника в наличии.
Пусть даже у них модель будет одинакова, включая связи между сущностями (и это чистая правда, и даже мы предпринимали потуги по автогенереции .ts моделей из .java, в итоге фронтовики сказали - лан, это всё заебись, но руками мы более качественный кот напишем, это 10 минут работы) - сама логика (сервисы) будут иметь различную реализацию - потому что ты в одном месте (фронт) контролируешь пользовательский ввод на уровне связи инпутов с моделью и взаимозависимостей вьюх (так, ага, тыкнули сюда, показываем вот это), а в другом (бек) тупо проверяешь уже пачку этих объектов, собранных воедино в объект AllEnteredUserStuffToProcessFoo - беку насрать на атомарные действия юзера, он контролирует шаги более крупно (в пределе - в один пост сразу весь конечный самодостаточный итог, который проверяет бек и ТОЖЕ может отказать фронту, но уже по причине того, что весь кумулятивный объект ему не понравился)
Ты же не будешь на фронте считать конечную стоимость КАСКО, и потом уже захуяривать на бек запрос "выстави этому придурку счет на 100500 рублей, вот конечное число, я тут на фронте пощитал", как и не будешь на бек слать "так, он тут фамилию свою ввёл, и перешёл на другой инпут, че делать"
В любом случае, я не настаиваю, хозяин барин. Прост хотелось узнать как так вышло. Ответ устроил.
Мы, например, кое-где в жабьем беке сами движок v8 заембеддили, у нас тоже причины были на это (не код реюз).
Ну вот примеры логики, которая может быть общей и на клиенте и на сервере
* валидация полей (регулярки, граничные значения для числовых полей)
* доступность полей в зависимости от других: если в поле A я ввел foo, а в поле B ввел bar, то в поле C можно выбирать только 1 или 2, а иначе там будет еще 3.
* если заполнены поля A и B, то так же нужно заполнить поле C.
Разумеется, сервер не доверяет клиенту и заново все это валидирует. Просто пользователю приятнее когда ГУИ ему помогает, а не сообщает в конце что он не заполнил нужное поле.
И конечно сервер может валидировать более глобальные вещи чем один объект, представляющий форму. Ну это гую никак не мешает.
Есть разные попытки решить это генерацией HTML. Скажем, в джанге можно указать max_length поля и некоторые формочки отрендерят ее с HTML5 input type=number, но это не везде работает, и не всё можно этим покрыть.
Ты говоришь вполне разумные доводы, которые посещали каждого, кто подступался к таким задачам, но в реальной жизни выходит все не так сферично.
Порассуждаю вслух.
Каждый инпут должен быть связан с моделью, поменяли инпут - изменился объект, изменился объект - изменился инпут. Модель в т.ч. подразумевает, что ты должен насосать кучу справочников, в т.ч. иногда подсосать по мере ввода. Что изменяя инпут А и Б, у тебя подмножество допустимых значений инпута Ц должно пройти фильтрацию (влияя ли на UI, сокращая количество опций в дропдауне, или просто на результат проверки инпута Ц - дело второе). При этом фронту еще огого сколько делать кроме общения непосредственно с модулем валидации.
Т.к. на фронте и так сосать все эти справочники, в варианте с подключением питона надо учесть момент, что в режиме работы на фронте эти справочники ему скормит жс, а на сервере он те же репозитории найдет с более другой имплементацией. Требуется 1:1 мапинг питоновских объектов на жсные, чтобы питоновская модель соответствовала модели жса (чтобы питон мог в своих объектах сохранить атрибуты isValid, isEnabled, userNoticeText и т.д., на которые соответствующе отреагирует фронт). Ну т.е. взлетит, правда накладных расходов все больше на эту конструкцию.
Чтобы окупилось, требуется чтобы на беке реально был напитон, и чтобы таких форм реально было много и все нетривиальные. Я даже предполагаю в каких отраслях народного хозяйства нужен такой беспредел.
[ч.2]
> Жаль что нет каких-то стандартных языков для этого
Для агностик решения потребуется описывать метапитушню на чем-то декларативном, и учить и фронт, и бек эту метапитушню переваривать одинаково (ну или хотя бы генерить из бека метапитушню, разбираемую фронтом), что выйдет ещё дороже, чем когда фронт и бек уже сразу на "питоне" (ну или на реакте+ноде, как советовали выше).
Я тоже не знаю таких библиотек, которые бы что-то автоматизировали (т.к. у нас нет портянок по 50 инпутов, с зависимостями от фаз луны).
Самое позорное случится тогда, когда библиотека перестанет удовлетворять 1% потребностей, и для поиска обходных путей придется вложить столько костов и граблей, что разделение между клиентом и сервером логики валидации с дублированием покажется не такой и плохой темой в реализации и сопровождении.
Ротация вчерашней технического оснащения собственно на свежую - это надобность, какая возникает следующие два-три года, скорое развитие приложений плюс свежего формата электронной аппаратуры выставляет к продаже планшеты, какие различный абонент желает заказать в прямое период, то что напрямую к тому же доступно сделать через сервесную- фирму, какова работает над продажей гаджетов. Фирма дает возможность пользователям выполнить быстрый этапность касательно обмена вышедшей с моды оборудования еще и получения новой за минимальное действие: продавец указывает информацию работающему оператору, регулирует отпуск ожившей аппаратуры, также производит чистыми деньгами в малость минут. Работать из Digitbuy <a href=http://www.digitbuy.ru/11-skupka-telefonov.html>продать iphone</a> практично на возможном виде, наша команда производит закупку электроники также качественной установок, сотовых, фотоаппаратов, Apple оборудования, ноутбуков также других агрегатов в различный момент суток, и непрерывно предлагаем хорошие договор скупки плюс солидные оплату http://www.digitbuy.ru/ на ваш товар. Звоните, также производите надобную сумму, тогда когда потребителю удобно со DigitBuy.
Во время беседе, конгресса, деловых вечере непременно предлагают выпить кружку кофе или же чая, именно как принцип - чем больше элитней станет вид презентуемого какон, тем предпочтительней и сходное налаженность личного партнера . Сервисный- маркет дорогого чайных листьев также какаобобов <a href=http://www.kofeichay.ru/133-podarochnye-nabory-dammann.html>чай dammann подарочный набор</a> выставляет пользователям и ценителям нагретых какао большой ассортимент чая, кофе, кофейных машин к тому же аппаратов именно для приготовление доброкачественного кофе, лишь у нас пользователь можете выбрать презентные комбинации с Dammann, Кофеичайру, приготовить кофейные зерна и зеленые листья оптом либо в розницу, а также принять участие у оперативных деяниях. Заказать эксклюзивный кофе Milani, Molinari, Musetti, illy, Danesi, чай Coccole, Nik Tea, Dammann, Althaus плюс указанные под офиса также http://www.kofeichay.ru/ себе клиенты сумеют у нашем веб сайте элитного кофе совместно с чайных листьев из доставкой на доступное локацию.
Качественная оборудование Эппл определенная каждому абоненту, тот что признает комфорт также доброкачество, поэтому непременно в вашем лице или давние друзья владеют при себе айфон, плеер или же iPad и восторгаются техническими характеристиками при применении. Команда открываем для клиентов сервисный-сайт телефонов к тому же плееров области [url=http://lapplebi.com/prilozheniya/1372-ustanovlenie-vyklyuchatelya-3g-vmesto-lte-na-iphone-5s-5c-i-5-s-ios-7.html]lte мегафон iphone 5[/url], на какой демонстрирует целиком свежие сведения о Эппл девайсы, реновации, приложения, гейм, анализ, отзывы, детали и указания касательно эксплуатации. На сайте Вы можете пребывать себя в личной сфере, просматривая данные публикации и выпуски технического оснащения ото фирмы Apple, регистрироваться в индивидуальном аккаунте также повседневно следить за итоговыми новинками в цифровом мире. Осмотрите точную пресс-релиз об Эппл http://lapplebi.com/ совместно со нами именно на ресурсе всех преимуществ также достижимости iPhone, плееров, iPad.
Атрибуты плюс практичность, комфортабельность еще и модерн - главные параметры высококачественной обстановки именно для квартиру, конторы, ресторанов либо сада, что внешним сортом уверяет про длительной функционированием . Заказать мебель всяческого функции у данном веб- ресурсе <a href=http://kimnatka.com/mattresses_beds/krovati/beds_with_lifting_mechanism>http://kimnatka.com/mattresses_beds/krovati/beds_with_lifting_mechanism</a> пригодно и общедоступно, вследствие скорой доставке по указанный пунктам покупатель сможет решительно реализовывать покупку со всякому расположения. Экуменизм из текущим фабрикой предоставляет возможность вовсе не лишь применять снаряженным гарнитурой, но плюс делать личные покупки , прилагаясь по скидках к тому же подготовленных пропозициях . Новая обставку на офиса, жилище, сада, детской комнаты , мягкой формирования по нашем всеобъемном - прейскуранте на источника http://kimnatka.com/ по низкие оплату завершит желаемый интерьер стильным также роскошным, а мы послужит в отменном работе с наших заказчиками.
wh0cd341090 <a href=http://toradolnorx.us.com/>Cheap Toradol</a> <a href=http://online-viagra.us.com/>check out your url</a> <a href=http://tamoxifennorx.us.com/>tamoxifen no rx</a>
Этот чай уникального пурпурного цвета, вот уже более шести тысяч лет выращивается в самых высоких горах на земле - Непале и Тибете и используется в разнообразных медицинских целях, а сейчас засчёт огромного набора полезных свойств заслуженно набирает популярность во всём мире.
Пурпурный чай - это мощнейший жиросжигатель, который к тому же препятствует возникновению новых отложений, превращая жир в энергию.
Официальный сайт: http://pur.bxox.info
wh0cd961686 <a href=http://amoxicillin247.us.org/>amoxicillin</a> <a href=http://buyviagraonline24.us.org/>viagra online no prescription</a> <a href=http://cheapviagra247.us.org/>can i buy viagra without a prescription</a>
wh0cd295802 <a href=http://tamoxifen24.us.org/>tamoxifen online</a> <a href=http://retina24h.us.org/>where can i get tretinoin cream</a> <a href=http://erythromycin24.us.org/>erythromycin</a>
wh0cd906214 <a href=http://paxil24.us.org/>generic paxil</a> <a href=http://viagrasoft24.us.org/>read full report</a> <a href=http://viagra24.us.org/>best place to order viagra</a>
wh0cd210842 <a href=http://citalopram5.top/>citalopram</a> <a href=http://clomid2015.gdn/>clomid</a> <a href=http://cytotec20.top/>misoprostol over the counter</a>
Тернарники тоже нельзя? И short-circuit && и || ?
cond && thenAction;
cond || elseAction;
Но вот функции лучше.
Во-1 это законченные блоки кода, с неймспейсом и стеком.
Во-2 рекурсивной вложенности можно избежать передав функции адрес тела (указатель на функцию) и условия выхода.
Это плохо. Если вызывать функцию по указателю, она отжирает стек. Даже если нет рекурсивной вложенности, все равно на эти создания стека для функции тратится время. Да еще при call пишется адрес возврата, который потом по ret из стека вытаскивается. Неймспейс я могу сам запросто сделать, завернув куски кода в {} чтобы ограничить область видимости, и через goto передавать туда управление. А потом в конце этого {} обратно сделать goto на кусок кода, занимающийся обработкой этого самого шитого кода.
http://habrahabr.ru/company/intel/blog/261665/ вот кстати статья на хабре про интерпретаторы, там эта фича с присваиваемыми goto используется. Кроме того, там используется еще вот эта вот https://gcc.gnu.org/onlinedocs/gcc/Global-Register-Variables.html замечательная GCC фича. Настоящие программисты должны оценить
А на графики ты посмотрел? Это говно с вычисляемыми goto, внезапно, отработало медленнее процедур 🙂
http://www.agner.org/optimize/microarchitecture.pdf page 34
The P1 has no return stack buffer, but uses the same method for returns as for indirect jumps. Later processors have a return stack buffer. The size of this buffer is 4 in the PMMX, 8 in Atom, 12 in AMD k8, 16 in PPro, P2, P3, P4, P4E, PM, Core2 and Nehalem, and 24 in AMD k10. This size may seem rather small, but it is sufficient in most cases because only the innermost subroutines matter in terms of execution time. The return stack buffer may be insufficient, though, in the case of a deeply nesting recursive function. In order to make this mechanism work, you must make sure that all calls are matched with returns. Never jump out of a subroutine without a return and never use a return as an indirect jump.
И я подозреваю, что именно благодаря этому, там эти вызовы процедур оказались быстрее. На каком-нибудь древнем интель-проце или на дохлом арме ситуация может оказаться прямо противоположной
По какой-нибудь формуле вычисляем индекс массива — это и будет ветвлением.
итп
Операторы нинужны.
А слабо сделать бесшовные операторы через макросы и лямбду? Чтоб не писать постоянно [&]()
IF x < 10 THEN
...
ELSE
...
ENDIF
Интересно, а паттерн-матчинг или нормальный свищ с диапазонами запилить сложно?
У меня вложенность была какой-то дико маленькой. емнип 16.
Тогда max(x, y) = y + (x - y) * θ(x - y); min(x, y) = y + (x - y) * θ(y - x).
http://graphics.stanford.edu/~seander/bithacks.html
{
return (a > b) * a + (a <= b) * b;
}
Совсем не то.
Доклад интересный, но не сильно глубокий. Облом в том что у него `One + One` не возвращает `Two`. И все остальное обламывается в этом же подобном духе. И не сильно ново/оригинально - это более или менее переложение мат. языка на язык программирования. Lambda calculus уже слишком высокоурвнево.
Я думаю что тот чудак про формальные языки не слышал, потому что это именно то что он переизобретает, углубляясь в доисторические уровнии ОО, когда оно было теоретической моделью для экспертных систем ИИ.
Ну и пофиг. Оно же возвращает объект, эквивалентный Two. Этого достаточно.
В жабе вон Integer.valueOf(100500) и Integer.valueOf(100500) могут быть разными объектами. Но всем похуй, т.к. они эквивалентны.
> Ну и пофиг. Оно же возвращает объект, эквивалентный Two. Этого достаточно.
Достаточно, но криво. Потому что Two это не класс, а объект. Потому что Two не знает что оно Two.
С буквами он догадался что не возможно - а вот с числами не догадался. Концепция чисел настолько примитивна, что семантика и синтакс у большинства в голове перемешаны. А вот кодировки и чар-сеты тема более высокоуровневая и там он как бы и споткнулся. Но на самом деле концептуальная проблема та же самая что и с числами: двойка не знает что она двойка, двойка есть двойка потому что это то семантика которую люди приписывают слову "два".
ЗЫ Extreme OO как оно там излагается на самом деле большее Extreme O, потому что там "объектно-ориентированость" отсутствует, и используется почти исключительно "объектность".
Твоя фантазия недостаточно извращена, чтобы реализовать всё это вообще без циклов, условий, свичей, тернарников и short-circuit операторов, guest?
P.S. Да и вместо флагоёбства можно было просто break сделать.
Генерируем в зависимости от условий имя функции, запрашиваем его у либы, вызываем -> profit
> Как все настоящие программисты знают, единственной полезной структурой данных является массив.
> Если вы не можете выполнить эти работы на Фортране, выполните их на ассемблере. Если же их нельзя выполнить на ассемблере, их не стоит делать вообще.
/s/Фортран/Сишк/
MAXIMUM TSAR!!!
Вообще конечно он прав.
If you ignore the fact that it's structured, even C programming can be appreciated by the Real Programmer: after all, there's no type checking, variable names are seven (ten? eight?) characters long, and the added bonus of the Pointer data type is thrown in. It's like having the best parts of FORTRAN and assembly language in one place. (Not to mention some of the more creative uses for #define.)
Давно читал крео, это помню там за сишку было.
Вообще он прав. Фортран — охуенный язык, из которого произошёл бейсик. алгол и кобол — сакс.
Еще доставляет пассаж про правки двоичного кода
Из-за этого настоящие программисты неохотно редактируют уже работающие программы. Они считают более простым непосредственно латать двоичный объектный код, используя прекрасную программу под названием SuperZap (или ее эквивалент на не-IBM машинах).
Этот метод настолько хорош, что многие программы, работающие на ЭВМ фирмы ИБМ, не имеют ничего общего со своим собственным текстом на Фортране. В большом количестве случаев первоначальный символьный текст программы вообще не существует. Когда наступает время подправить такого рода программу, никакой администратор даже не думает послать на эту работу кого-либо, кроме настоящего программиста - никакой сосунок (структурный программист) не будут знать даже с чего начать. Это называется защита от несанкционированного доступа.
Разница с java том что однажды пропатчив слинкованный бинарник получается невосстановимый пиздец, который уже никак не декомпилировать в ЯВУ. После этого просто необходимо иметь в штате такого гения, который будет его сопровождать.
Означает ли это, что неструктурированные ассемблероподобные языки лучше?
Эммм. Самое ключевое их отличие было статическая тупизация vs динамическая. Это раз. Ну и компилятор vs интерпретатор — два.
Сишка всё-таки где-то посередине, ближе к алголу (точки с запятой, какие-никакие типы, компилируемость).
Ключевые отличия алгола от фортрана: точки с запятой, типы, структуры данных.
Идейными последователями фортрана я считаю: Бейсик, Питон, Js.
Хоть это и на первый взгляд абсурдно звучит. Но речь именно об идее. И тут они родственны фортрану: в отношении к типам, краткости, простоте, порогу вхождения и отсутствию точек с запятой (js может без них). И все изначально были интерпретируемыми.
Вот все кто пёрлись в своё время от фортрана и бейсика сейчас котируют js и питон.
Хотя лет 15-20 назад на JS и пытались писать, как на Фортране (вспомним сайты с document.writeln).
В Питоне тоже на первом месте структурированность кода, хотя там и отступы вместо фигурных скобок. Плюс функциональщина, без которой программиста питонистом считать не будут.
Остаётся только Бейсик.
Так в Фортране тоже отступы! 6 пробелов.
Идентация и структурированность!
Но вертикальной структурированности в классическом Фортране нет! В Фортране 66, как и в классическом Бейсике, не было этих всяких end if, а ветки разруливали с помощью меток и GOTO.
Когда в Фортране 90 вместо DO <метка конца цикла> ... ввели DO ... END DO, вместо PRINT <метка строки с форматом>, ... ввели PRINT "строковая константа", ..., добавили модульность (в том смысле, как в Модуле), а в реализациях Фортрана при вызове подпрограмм стал использоваться стек (как в Си и в Паскале), что наконец-то позволило совершать рекурсивный вызов, старые фортранщики это посчитали предательством.
*****
Но вообще в Фортране было что-то, что в Паскале и в Си долгие годы было недоступным. Это, например, неявные циклы (когда в некоторые выражения на вход вместо скаляра подаётся массив), «арифметический» IF с тремя ветками (это вам не тернарник с двумя!), а с Фортрана 90 ещё и стандартные функции над массивами. А ещё работа с комплексными числами из коробки!!!11адын-адын-адын
https://en.wikipedia.org/wiki/List_of_quantum_chemistry_and_solid-state_physics_software
http://stackoverflow.com/questions/7588079/how-to-computed-goto-and-longjmp-in-c
(рекомендую читать доставляющий срач в комментах)
Кстати, у нас есть две крайности:
1. С одной стороны, Царь, который считает, что никакие структуры, кроме одномерного массива не нужны, потому что оперативка представляет собой одномерный массив.
2. С другой стороны, математик-теоретик wxv (не помню, как дальше), который считает, что с помощью массивов можно решить далеко не любую задачу (а факт, что все эти списки, деревья, очереди, стеки, множества и прочие объекты в конце концов в библиотеке языка реализуются с помощью массивов, игнорирует).
Интересно было бы найти то же самое, но наоборот, в wxv-стиле, чтобы сравнить ощущения.
Чушь же. Все структуры — это просто сахар над одномерным массивом.
>Интересно было бы найти то же самое, но наоборот, в wxv-стиле, чтобы сравнить ощущения.
При всём уважении к wvxvw наоборот вряд ли получится.
Там речь о том, что Real Programmer настолько гениален что ему не нужны проверки типов для написания безошибочных программ.
Ну разве что обратная херня: не поближе к машинной реализации. А наоборот абсурдно далёко от неё и запредельно абстрактно.
Где так: настоящий программист пишет программы только на Лиспе и только в EMacs, транслируя их в нумералы Чёрча, которые он потом правит руками.
Проблему пока вижу в общении с внешним миром - линуксовое ядро эти нумералы в syscall'аъ не принимает 🙁
линукс пропатчить, чтобы он указатель на функцию вместо чисел принимал, и применял бы эту функцию к обычному инкременту
вот кусок кода ввода-вывода одной VM, которая поддерживает только комбинаторы и один примитивный тип RealWorld
как видно, мы лифтим хаскельные инкремент, (.), ($) и 0 в VM, а потом позволяем ей доделать всё остальное согласно обычной семантике
как-то так можно
пердолиться с остальной арифметикой на крестах мне сегодня лень
http://ideone.com/J9hZ4k
> надо
эти два слова могут встречаться в одном предложении только при условии, что третье и последнее слово в нём -- "закопать"
http://maxim.livejournal.com/352305.html
Иногда получается хаскелъ.
А как одну пропустил -- так сразу Хуанос застрелил Альберто, Мария беременна, Чиполлино объелся чеснока, а Чёрч - так вообще s (Â :. n :. ψ @ (Ψ {})) = Â' :. (n :. F (+1) :. N 0) :. ψ.
И оперативка это не массив, просто оь вас ласеров это скрыто контроллером памяти
И тролль лжец wwxw не знает что все выражаетс через моссив
А вот это, внезапно, уже вполне соответствует новым веяниям...
http://imgur.com/TutXNrT
t. clueless
P.S. А С++ стал такой няшей после С++11 (включительно)
Из собственного опыта, я думаю, можно смело сказать, что будущее прекрасно для настоящих программистов. Ни Linux, ни Сишка не высказывают ни каких признаков отмирания, несмотря на усилия программистов на С++. Даже такие изощренные уловки, как добавление RAII, конструкций объекто-ориентированного и функционального программирования в Сишку, провалились. Да, конечно, некоторые выпустили компиляторы С++, но каждый из них
оставил возможность перейти в режим компилятора С89 с помощью добавления одного флага — чтобы управлять памятью как предписано Богом.
а. остается написанный на ней актуальный код
б. остаются упрямые кодеры, которым легче всю жизнь вручную гонять байты, чем освоить ооп и шаблоны
Итого, сишка будет жить вечно, потому что кодеры из (б) будут писать код из (а), называя всех с++-кодеров крестоблядьми
Вот https://ideone.com/RHz5vW С++ (если б не было говношаблонов(например в чистом Си), я мог бы нагерить код функций через genfunctadd). А вот пример на языке D http://melpon.org/wandbox/permlink/22k35NmuFvFuVGKu где это делается одним единственным кодом.
Приведенные вами примеры генерации кода намного хуже, чем шаблоны.
Проблема известна, и недавнее введение в язык constexpr направлено как раз на её частичное устранение.
В лиспе, к примеру, компилятор может использовать в компайл-тайме встроенную или пользовательскую функцию, работающую со списками, для трансформации AST.
Этот пример даже не я сочинил, а спорящий со мной @j123123. Просто сравните читабельность двух процедур. А если надо написать что-то сложное?
Как зачем? Нужны конкретные примеры? Ну хорошо, допустим я хочу сделать некую хеш-таблицу
{"Петр Петрович", "$200", hash1}
{"Петр Иллич", "$1488", hash1}
{"Иван Петрович", "$400", hash2}
{"Петр Иванович", "$700", hash3}
{"Владимир Владимирович", "$10000000", hash2}
"hash?" это некое числовое значение(корзина), и у меня есть еще некая функция, которой если передать "Петр Петрович" то оно вернет число hash1, и потом я могу быстро найти. В начале у меня есть просто набор:
"Петр Петрович", "$200"
"Петр Иллич", "$1488"
"Иван Петрович", "$400"
"Петр Иванович", "$700"
"Владимир Владимирович", "$10000000"
без всяких хешей. Я хочу в компилтайме получить уже инициализированную хеш-таблицу. Я могу сделать это на этих гребаных шаблонах в компилтайме, но если я хочу еще в процессе работы править эту хеш-таблицу, например добавлять новых людей, то разве мне этот компилтайм-код поможет? Мне придется писать код для работы в рантайме, который дублирует функционал шаблонного кода. А нафиг мне вообще эти самые шаблоны, если я своим рантайм кодом могу накодогенерировать что мне надо, и просто заинклудить это в нужное место?
Хотя шаблоны в этом плане еще хуже
Вот что получилось: http://goo.gl/bhmKTO
Это
.L16:
test r8d, r8d
jne .L15
test r13b, r13b
jne .L80
.L18:
cmp BYTE PTR [rsp+15], 0
je .L19
mov DWORD PTR [rsp+64], ebx
.L19:
cmp BYTE PTR [rsp+7], 0
je .L20
mov DWORD PTR [rsp+72], edi
.L20:
cmp BYTE PTR [rsp+6], 0
je .L21
mov DWORD PTR [rsp+76], r10d
.L21:
cmp BYTE PTR [rsp+5], 0
je .L22
mov DWORD PTR [rsp+80], esi
.L22:
cmp BYTE PTR [rsp+12], 0
je .L23
mov DWORD PTR [rsp+84], ecx
нихрена не компилтайм, это говно какое-то
Плюс к тому, в вашем примере bubble_sort - процедура, а не функция, со всеми вытекающими.
Пример о том, как вам достичь желаемого и не только. Правда, использовать придется с++14. А написать один раз, чтобы сразу двух зайцев - ну, мб, дефайны помогут, хотя проще будет написать два куска для разных случаев
Послушайте, просто послушайте, что Вам говорит j123123, подумайте над этим.
1. В языке C++ есть возможность писать код, который работает во время компиляции и код, который работает во время исполнения
2. В принципе, на шаблонах можно написать всё то же самое (тьюрингова питушня и всё такое) за исключением ввода-вывода и с ограничением на вложенность шаблонов (Простого -ftemplate-depth хватает GCC для решения проблемы останова, над которой Тьюриг, CHayT и их апостолы бьются в математических спорах вот уже тысячелетия), но заново, что в нашем мире библиотек и переиспользования неприемлемо.
3. C++ и шаблоны C++ - это не python и lisp, это не два языка, это один язык, поэтому логично было бы желать иметь единообразное описание программ.
При правильном использовании, функция, результат которой будет вычисляем на этапе компиляции, будет отличаться от обычной функции только словом constexpr. Логично? Да. Человек приводит пример НАМЕРЕННОЕ нарушение требований constexpr и вообще банальной человеческой логики (constexpr void не несёт смысловой нагрузки) как аргумент против его использования. Логично? Нет. Это как жаловаться на язык потому что компилятор не умеет разбирать заведомо некорректную конструкцию.
Последнее: шаблоны - прослойка поверх синтаксиса плюсов, отвечающая за метапрограммирование. Логично? Да. И это лучше, чем запихивать код в строки и редактировать его вручную.
Не объясняет. Речь об ограниченности шаблонов в C++ и проблемах C++.
> Человек приводит пример НАМЕРЕННОЕ нарушение <...> банальной человеческой логики
Знаете, вот есть "по закону", а есть "по совести". Есть банальная человеческая логика, а есть здравый смысл.
Логика часто надеется на то, что программа выполнится за конечное и крайне малое время. В реальности же возникает бюрократия. Справка о том, что нужна справка, с подтверждением личности по паспорту, удостоверениям и кипе документов - понятие логичное, но крайне бессмысленное.
Кто-то логичный сказал: "Давайте я решу на песке уравнение и пойму, кто из людей более ценный, чтобы его спасти", а кто-то здравомыслящий успел спасти, из-за него кто-то не утонул.
Также логика точна, а здравый смысл работает с вероятностными величинами. Можно долго спорить о иррациональных числах и бесконечное количество времени вычислять число Пи, это логично, но можно работать с погрешностью. 3.14+-0.01 - всё то же старое Пи, заданное корректно с погрешностью, уместной в нашей задаче.
Вы смотрите на проблему с точки зрения логики, посмотрите на неё с точки зрения здравого смысла.
Они бы тогда убили и программистов, и пользователей.
В общем-то затем и нужен программист, чтобы аппроксимировать здравый смысл в бинарную логику.
Если здравый смысл противоречит логике, значит эта модель (логика) здесь неприменима или недостаточна, нужно придумать логику получше.
Вообще, очень показательный пример. Предположим что уже есть некая готовая кем-то написанная обычным способом функция на С/C++(уверен что есть), делающая разбор выражений вида "(4^2-9)/8+2/3". НО использовать ее в этих constexpr нельзя, и надо заново это все переписывать с учетом ограничений. Так почему бы вместо того чтобы переписывать, не сделать генерацию через эту функцию? Притом этот код на constexpr будет ТРУДНЕЕ для понимания, чем код, написанный в обычном стиле на тех же плюсах.
Там даже нет никакого malloc
О масштабах изменений судите сами. Challenged? Check. Delivered? Check.
Я уж не говорю о таких "мелочах", как то, что этот constexpr-код сортировки в компилтайме отрабатывает очень долго по сравнению с тем, как если эту сортировку скомпилировать обычным образом и передать ей на вход массив, который надо отсортировать. При сортировке больших массивов он отрабатывает так долго, что clang начитает предполагать что там бесконечный цикл, и прекращает сортировку компиляцию, а gcc падает с internal compiler error(судя по всему от нехватки памяти).
С++ код в общем случае не является С кодом. Но С код почти всегда можно собрать С++ компилятором(есть конечно пара-тройка несовместимостей, но это мелочи, можно поправить) и он будет корректно работать. Таким образом, очень большое количество С кода является С++ кодом.
>Я не менял сигнатуру ФУНКЦИИ, я менял сигнатуру ПРОЦЕДУРЫ
В языке СИ нет деления на процедуры, подпрограммы и функции, здесь вся программа строится только из функций. Есть понятие функции, возвращающей void.
>а процедура не может иметь смысл как constexpr, и это потому что это логично, а не потому что язык говно.
А меня не волнует. Допустим у меня есть работающий код из функций(или процедур если угодно), написанных в обычном Си стиле. Когда писался этот код, никаких constexpr-ов в плюсах не было, и естественно код писался в обычном стиле, а не так, чтобы потом если вдруг в плюсах появятся constexpr-ы то чтобы оно на них легко портанулось. Очевидно, что этот код просто так не засовывается в этот constexpr, и чтобы его туда засунуть, надо очень много переписывать, а заниматься такой ерундой никто не собирается. Так что если я вдруг захочу использовать этот код в компилтайме для генерации неких данных, я просто скомпилирую этот код обычным образом, прикрутив к нему вывод через printf или std::cout <<, запущу его и потом просто использую этот полученный вывод.
2. Функция, которая ничего не возвращает назвыается процедурой. void - это как раз таки "ничто".
3.
> >а процедура не может иметь смысл как constexpr
> А меня не волнует.
"Я хочу чтобы void в компайлтайме пересчитался в отсортированный массив" - это финиш
Почему же другой? Если он корректно компилируется С++ компилятором и корректно(так же, как если бы его компилировали С компилятором) выполняется, то этого разве недостаточно?
>Функция, которая ничего не возвращает назвыается процедурой. void - это как раз таки "ничто".
Хорошо, если вам такая терминология больше нравится, будем называть функцию, возвращающую void процедурой. В СИ это всего лишь частный случай функции, не более.
>"Я хочу чтобы void в компайлтайме пересчитался в отсортированный массив" - это финиш
А что не так? По-вашему, сделать это не представляется возможным? Или это как-то "методологически неправильно" ?
Сколько информации может нести переменная типа void? 0, черт подери, 0!, КАРЛ! Мы говорим о языке со СТАТИЧЕСКОЙ типизацией, где, в отличие всяких лиспов/питонов/жабаскриптов нельзя вместо true/false засунуть хеш или обратиться к 10-му элементу null-а и не получить ошибки.
Речь вообще была о с++. В с++ есть интересующий вас функционал? Да. Неумение им пользоваться - ваш личный геморрой. Всё. Вопрос закрыт.
Функция, меняющая состояние окружения в принципе не может являться выражением времени компиляции, т.к. логика работы программы зависит от времени вызова такой функции. И это правильно. То, с чем вы сравниваете, можно сделать хоть на си - написать препроцессор, который будет компилироваться на основе тех же файлов и заменять код основной программы перед её компиляцией.
Это теоретические вопросы. На практике как нечто полезное используется не состояние программы, а её результат. Нужно лишь попросить программиста предоставить какой-то результат.
Можно определить некоторые переменные, которые будут экспортированы.
Например, обозвать их compiletime:
Во время компиляции исполняется ct_main, после этого при запуске в compiletime-переменных хранится то, что было в них в момент завершения ct_main.
Возможно, malloc/free/конструкторы/деструкторы патчатся, чтобы сохранить структуру экспортируемого объекта. Возможно, malloc и конструкторы должны выполняться при исполнении. Т.е. если есть динамический compiletime массив с посчитанными на этапе компиляции знаками пи, во время исполнения создаётся int*/std::vector с вызовами malloc/new/конструкторов и заполняется посчитанными знаками.
Т.е. осуществляется сериализация/десериализация объектов.
Если, скажем, статические поля класса как-то изменялись, но не были помечены compiletime, там им и место, они останутся неинициализированными, программист явно указал экспортируемое состояние, посчитать которое стоило больших, но однократных усилий.
*y может быть посчитан только если есть статические константные N байт памяти программы, на которые он указывает. То есть для компайл-тайм вычисления сначала должны быть определены данные (которые зашьются в тело программы), а только потом вычисляется указатель на них. Поэтому
constexpr struct { int data[3]; } getData() { return {{1,2,3}};}
возможно, а через указатели - нет.
Я же говорю, что
1. malloc может быть пропатчен.
2. важны данные, а не их положение, получение и т.д.
Понятно, сложно или даже невозможно восстановить всё состояние программы. Но ведь тут надо работать как инженер, а не как математик. Возможно с таким приближением - делаем с приближением.
(Кстати, на этапе исполнения абсолютно не важно, что вернёт malloc. Зачем копировать указатели, если они не несут смысла?)
Вы же можете написать вот такую программу:
Что Вы делаете?
1. Вычисляете число Пи
2. Записываете знаки в блокнот
3. Пишете программу
Пусть кто-то вычислял Пи на этапе компиляции.
Известны знаки числа Пи после компиляции? Можно их сохранить? Да и да.
А значит компилятор может создать программу, которая вызывает malloc и заполняет массив.
Или сериализация/десериализация в принципе не возможны, их вообще в мире не существует?
Сериализацию/десериализацию пакетов фиксированной структуры лично я обычно так и делаю.
Вариант первый (выше я его упоминал)
1. Для каждого типа, который участвует в экспортируемых в рантайм выражениях (тип экспротируемой переменной, типы её полей, типы полей их полей и т.д.), генерируется дополнительный конструктор инициализации; для malloc генерируется конструктор массива.
2. Во время компиляции за каждой создаваемой переменной следят, чтобы определить способ её построения. Т.е., какие конструкторы надо вызвать, чтобы получить такую переменную. Но с ограничениями: не помнить иерархию изменений, не следить за кастами указателей в инты и т.п.
Например, был указатель и блок памяти длиной 8 байт, сделали placement new и сконструировали объект типа A размером 8 байт, затем сделали placement new и сконструировали объект B размером 4 байта и экспортировали указатель. Во время исполнения под указатель выделится 8 байт, первые 4 заполнятся объектом B, остальные не будут определены. Если в экспортируемом объекте был указатель на объект, который сейчас в памяти, о котором есть метаданные (кто-то вызвал malloc или конструктор), то записывается сожержимое, иначе - error/warning для компиляции или же нулевой указатель.
3. Во время исполнения вызываются специальные конструкторы и пропатченный malloc, заполняющие экспортированные объекты вычисленными на этапе компиляции значениями. Объекты становятся такими же, как будто бы программист вручную написал код как в предыдущем моём комментарии. Т.е. их можно изменять, удалять и т.д.
1. Определяем правила для compiletime-классов: есть конструктор, инициирующий все поля; все поля - compiletime-типы.
2. Просим пользователя на этапе компиляции засунуть результат в compiletime-типы. Иначе - ошибка. Т.е. на этапе компиляции программа творит любые непотребства, но экспортирует их в строгие compiletime-типы.
3. На этапе компиляции следим только за созданием compiletime-типов.
4. На этапе исполнения восстанавливаем экспортированные объекты как в варианте 1.
Возможно, потребуется создать шаблонный псевдокласс std::compiletime<T>, который будет автоматически преобразовывать T в compiletime-тип. На этапе исполнения std::compiletime<T> будет иметь дополнительный конструктор, но более никак не будет отличаться от T.
Там очень няшный компайлтайм, не то что крестоблядское уёбище по имени constexpr. Даже файлы читать можно. Файлы в компайлтайме, Карл!
Насчёт практичности всего этого - протобуф без внешних генераторов, просто либой. Регулярки с компайлтайм парсингом и статической проверкой. Сериализация без бойлерплейта и генераторов. Разве всё это нинужно?
И printf, который не требует особой компиляторной магии для проверки, при ошибке переходишь в код валидации в стандартной либе.
Я развлекался с D (в основном, правда, решая олимпиадные задачки), прочитал книжку Александреску. В целом весьма приятный язык, рекомендую.
http://dlang.org/template-comparison.html
Вот этот пример, однако, позабавил:
http://media.giphy.com/media/3o85xoH0tmyTKstS9y/giphy.gif
статика в тьюринг-полных языках по-хорошему всегда должна и будет иметь другую семантику, нежели рантайм, поскольку мы хотим, чтобы компиляция завершалась
(не берём в расчёт кресты -- там своя атмосфера)
type-foo в haskell тоже пишется отдельно, и это правильно
Вот занятный пример: человек запилил проверку SQL-запросов в compile-time.
https://github.com/darioteixeira/pgocaml
Правда, компилятору коннект к базе нужен, лол 🙂
Для произвольной программы определить, закончится ли её компиляция
она и рантайме-то не всегда желательна
Или хочешь сказать, что она вообще никогда не нужна? Если да, с этим утверждением я готов поспорить
рантайм в тьюринг-полных языках по-хорошему всегда должен и будет иметь другую семантику, нежели рантайм, поскольку мы хотим, чтобы исполнение программы завершалось
Но почему для Вас так важно знание о завершении компиляции, что из-за этого даже можно сломать язык потерять универсальность языка?
У нас есть резервный компилятор %windir%\Microsoft.NET\Framework\v4.0.30 319\jsc.exe
P.S. А зомби зачем останавливать? От него и так одна шкурка осталась.
REISUB.
не пытайтесь спорить с Тьюрингом
>вдруг любым первым ходом они ставят себя в невыгодное положение?
А для этого есть возможность скипнуть ход (конём туда-обратно или пешкой не через два поля, а по одному). И тогда чёрные становятся белыми.
> в шахматах
О_о
Кажется, wvxvw нанял вместо себя тролля-теоретика.
они стрёмные
> кстати, факторизация - decidable?
да
>когда можно подождать разумное время и убить
разумное время подождать и убить, смышлёное пространство убить сразу, а истеричную энергию избить в переулке
> тролля-теоретика
wow rude
Это какой-то уход в мир грёз и поиск виновных ради раскрываемости... Я не дождусь, пока компьютер посчитает от 1 до N >> 1 с вероятностью не менее 1 - 1/N, а фанаты Тьюринга что-то про вычислимость говорят. А в то же время где-то крутится сервер с while(1) { processRequest(); } и всем дарит счастье.
> а фанаты Тьюринга что-то про вычислимость говорят
Ээээ.
А подскажите пожалуйста, а для каких чисел N зависнет цикл дописывающий сзади 1 к бинарному представлению N после чего добавляющий к результату исходное N, в случае если N - нечётное,
и убирающий из конца бинарного представления все 0, если N - чётное.
Думаю, даже для UInt64 выдержит, вычисление упрётся в длинную арифметику.
В среднем наверно хватит на 1К десятичных знаков, но где-то там может встретиться роковое число, из-за которого зависнет.
Поздравляю, вы решили одну из нерешённых проблем математики! Не забудте получить свой приз ТУТ
Если без чего-то можно обойтись, это не значит что это не нужно
простите, но мы программиста ищем, а не метапрограммиста
P.S. Кстати, в этом треде собрались удивительно нелогичные люди, которые, с одной стороны, погрязли в формализме, математике и логике с ног до головы и отрицают полезные нововведения, а с другой - пользуются существующими хаками и нелогичностями. То есть, если бы я говорил, что число Пи можно записать и использовать, они бы сказали "да как же, нет, нельзя, теория, формализм", а как только какой-нибудь авторитет показал бы им Пи в double и плавающего питуха, повсеместно используемого, так сразу можно.
Иными словами, некоторые пользователи ГК верят в логику ровно до тех пор, пока какой-нибудь авторитет вроде Страуструпа не покажет им авторитетный нелогичный метод решения их проблем вроде kill и double.
Не знаю. Всегда думал что для любого по-настоящему ленивого чело программиста dry и code reuse не пустые звуки.
К тому же джве разных версии кода имеют свойства разъезжаться при долгом маинтенсе.
>хотят использовать один код и на сервере и на клиенте...
На мой взгляд это единственное что сколь-нибудь оправдывает серверный js. Хотя тут конечно поджидает неприятный нюанс в виде невозможности использования новых фич (надо чтоб в старых браузерах).
А вот arrow functions у меня в node не работают. Хотя я, кажется, обновлял недавно.
Хотя, есть же трансляторы. Особо умные уже пишут на ES7.
Итого: пишем на ES6 или ES7, код автоматически приводим к ранней версии JS (делая его пригодным как минимум для серверной стороны) и переиспользуем.
Нет, уже работают. Таки давно обновлялся.
Это на далёкой машине с линуксом недавно обновлялся, только там в репозитории была версия времён Ленина.
Bytecode Targets produce executable byte code (Neko, SWF, SWF8), that can be executed directly by the runtime (Neko VM, Adobe Flash Player, Adobe AIR). Haxe API and platform-specific API is available.
Language Targets produce source code (AS3, C++, C#, Java). Most source code must be compiled by a third-party compiler to produce an executable file (Flex SDK, GCC, Microsoft Visual C++, Microsoft .NET, Java compiler). JavaScript and PHP code can be run directly, since the runtime uses just-in-time compilation. Inline code written in the target language can be inserted at any point in the application, thereby supporting the entire platform API; even features missing from the Haxe wrapper API.
Т.е. существует .NET, который "что угодно в одно" и Haxe, который "одно во что угодно".
Кстати, штуку "что угодно в что угодно" уже создали?
Нет вроде бы. Зато можно компилить haxe во что угодно и запускать всё это на .net.
Не слышал о таком. Но наверяка это возможно. Только вот если захочется x86 ассемблер откомпилировать в java например, это будет ооооочень медленно
https://ru.wikipedia.org/wiki/Vala
пости С# компилится в сишечку.
http://eu.battle.net/heroes/ru/heroes/valla/
Наконец-то хоть современные технологии и языки, а то всё напитон да напитон.
http://catb.org/jargon/html/p-convention.html
Англоязычные лисполюбы добавляли суффикс “-p” к словам для придания вопросительной интонации:
— Haxe-p?
— T!
The -P Convention
Turning a word into a question by appending the syllable ‘P’; from the LISP convention of appending the letter ‘P’ to denote a predicate (a boolean-valued function). The question should expect a yes/no answer, though it needn't. (See T and NIL.)
[Once, when we were at a Chinese restaurant, Bill Gosper wanted to know whether someone would like to share with him a two-person-sized bowl of soup. His inquiry was: “Split-p soup?” — GLS]
Сейчас же почти что угодно компиляется в JS. Поэтому вполне можно писать и клиент, и сервер, на каком-нибудь Haskell / Clojure, и генерировать клиентскую часть соответствующим компилятором хоть в asm.js. По сути ведь в GWT эту задумку и реализовали, причём уже довольно давно.
Конечно, на клиенский код будут ограничения, но и ведь не любой нодовый JS можно на клиенте использовать.
А уж компилять новый диалект JS в старый при сборке вообще (в теории) не проблема.
JavaScript is new ISA:) (instruction set architecture), лол
Ну и как, окупило себя хождение напитон в клиенте?
Но оно было ОЧЕНЬ простое. Полезность питона в огромной стандартной библиотеке, и я не думаю что вот прямо вся она отлично работает в браузере.
Профессионалы доверяют стандартной библиотеке. Стандартная библиотека — всё проверено до нас.
Логика уже была написана на питоне.
Но я не считаю это серьезным решением: как минимум они не умеют тройку.
А вот GWT я видел в продакшене еще в 2009-м году. Это было single page application которое сосало данные через COMET (вебсокетов тогда еще не умели), и дело было не в переюзании логики, а в нежелании писать на javascript)
во всяком случае на той версии, куда не завезли ключевое слово class
там в визуальщине и метапитушне (где какой инпут зависит от другого, чтобы каскадно их апдейтить, с шашечками как в экселе) больше работы, чем в непосредственно проверке собранной модели на валидность
Писать ДВА раза бизнес-логику это вообще самый ужасный ужас, который может случиться.
Другой вопрос что можно было описать ее языконезависимо и генерить(или программировать) по ней клиент и сервер. Жаль что нет каких-то стандартных языков для этого (ну или мне они неизвестны)
Дак в том и дело, что бизнес-логика на клиенте и на сервере всё равно различна. Один клиент, другой - сервер. Клиент вообще может что угодно заслать - апи то открытое, всё видно в F12, исходники (даже обфусцированные, но исходники) клиента у злоумышленника в наличии.
Пусть даже у них модель будет одинакова, включая связи между сущностями (и это чистая правда, и даже мы предпринимали потуги по автогенереции .ts моделей из .java, в итоге фронтовики сказали - лан, это всё заебись, но руками мы более качественный кот напишем, это 10 минут работы) - сама логика (сервисы) будут иметь различную реализацию - потому что ты в одном месте (фронт) контролируешь пользовательский ввод на уровне связи инпутов с моделью и взаимозависимостей вьюх (так, ага, тыкнули сюда, показываем вот это), а в другом (бек) тупо проверяешь уже пачку этих объектов, собранных воедино в объект AllEnteredUserStuffToProcessFoo - беку насрать на атомарные действия юзера, он контролирует шаги более крупно (в пределе - в один пост сразу весь конечный самодостаточный итог, который проверяет бек и ТОЖЕ может отказать фронту, но уже по причине того, что весь кумулятивный объект ему не понравился)
Ты же не будешь на фронте считать конечную стоимость КАСКО, и потом уже захуяривать на бек запрос "выстави этому придурку счет на 100500 рублей, вот конечное число, я тут на фронте пощитал", как и не будешь на бек слать "так, он тут фамилию свою ввёл, и перешёл на другой инпут, че делать"
В любом случае, я не настаиваю, хозяин барин. Прост хотелось узнать как так вышло. Ответ устроил.
Мы, например, кое-где в жабьем беке сами движок v8 заембеддили, у нас тоже причины были на это (не код реюз).
* валидация полей (регулярки, граничные значения для числовых полей)
* доступность полей в зависимости от других: если в поле A я ввел foo, а в поле B ввел bar, то в поле C можно выбирать только 1 или 2, а иначе там будет еще 3.
* если заполнены поля A и B, то так же нужно заполнить поле C.
Разумеется, сервер не доверяет клиенту и заново все это валидирует. Просто пользователю приятнее когда ГУИ ему помогает, а не сообщает в конце что он не заполнил нужное поле.
И конечно сервер может валидировать более глобальные вещи чем один объект, представляющий форму. Ну это гую никак не мешает.
Есть разные попытки решить это генерацией HTML. Скажем, в джанге можно указать max_length поля и некоторые формочки отрендерят ее с HTML5 input type=number, но это не везде работает, и не всё можно этим покрыть.
Ты говоришь вполне разумные доводы, которые посещали каждого, кто подступался к таким задачам, но в реальной жизни выходит все не так сферично.
Порассуждаю вслух.
Каждый инпут должен быть связан с моделью, поменяли инпут - изменился объект, изменился объект - изменился инпут. Модель в т.ч. подразумевает, что ты должен насосать кучу справочников, в т.ч. иногда подсосать по мере ввода. Что изменяя инпут А и Б, у тебя подмножество допустимых значений инпута Ц должно пройти фильтрацию (влияя ли на UI, сокращая количество опций в дропдауне, или просто на результат проверки инпута Ц - дело второе). При этом фронту еще огого сколько делать кроме общения непосредственно с модулем валидации.
Т.к. на фронте и так сосать все эти справочники, в варианте с подключением питона надо учесть момент, что в режиме работы на фронте эти справочники ему скормит жс, а на сервере он те же репозитории найдет с более другой имплементацией. Требуется 1:1 мапинг питоновских объектов на жсные, чтобы питоновская модель соответствовала модели жса (чтобы питон мог в своих объектах сохранить атрибуты isValid, isEnabled, userNoticeText и т.д., на которые соответствующе отреагирует фронт). Ну т.е. взлетит, правда накладных расходов все больше на эту конструкцию.
Чтобы окупилось, требуется чтобы на беке реально был напитон, и чтобы таких форм реально было много и все нетривиальные. Я даже предполагаю в каких отраслях народного хозяйства нужен такой беспредел.
> Жаль что нет каких-то стандартных языков для этого
Для агностик решения потребуется описывать метапитушню на чем-то декларативном, и учить и фронт, и бек эту метапитушню переваривать одинаково (ну или хотя бы генерить из бека метапитушню, разбираемую фронтом), что выйдет ещё дороже, чем когда фронт и бек уже сразу на "питоне" (ну или на реакте+ноде, как советовали выше).
Я тоже не знаю таких библиотек, которые бы что-то автоматизировали (т.к. у нас нет портянок по 50 инпутов, с зависимостями от фаз луны).
Самое позорное случится тогда, когда библиотека перестанет удовлетворять 1% потребностей, и для поиска обходных путей придется вложить столько костов и граблей, что разделение между клиентом и сервером логики валидации с дублированием покажется не такой и плохой темой в реализации и сопровождении.
--------
<a href=https://bezopasnik24.ru>техническое обслуживание пожарной сигнализации</a> | https://bezopasnik24.ru
Пурпурный чай - это мощнейший жиросжигатель, который к тому же препятствует возникновению новых отложений, превращая жир в энергию.
Официальный сайт: http://pur.bxox.info
<a href=http://bit.ly/2oQUzUu>легкий заработок онлайн</a>
<a href=http://bit.ly/2oQUzUu>источники пассивного дохода</a>
<a href=http://bit.ly/2oQUzUu>быстрый заработок</a>
<a href=http://bit.ly/2oQUzUu>стратегии заработка</a>
<a href=http://mikrosaym.blogspot.ru>Онлайн займы</a>
hhhh=
Доставка по России. Анонимно.
wiistriker@gmail.com