Комментарии 103
У автора, я смотрю, от каждой мелочи мозг взрывается...
Автор просто эмоциональный. Вот его блог по ссылке

В те^H^H мозге такая приятная гибкость образовалась!
Видимо, это инопланетянин из Людей в чёрном, который быстренько отращивает себе новую голову.
Я программирую с восьми лет, и основную часть жизни был разработчиком

Эх, нет упоминания ATS. Надо действительно запланировать статью про него, а то про такую жемчужину на Хабре ничего и не видно.
Догматизм и начетничество.
Меня всегда это удивляло. Ну давайте тогда и в асме отменим jmp, rjmp и прочие безусловные переходы. Как легко станет писать то сразу! Никаких переходов, никакой путаницы, исключительно условные операторы, условные переходы - красота.
В асме пусть живет)
Все же если человек пишет в 2024 на асме он по идее точно знает что делает.
А вот на каком-нибудь JS не всегда.
Неа. Достаточно while с continue и со сменой стейта. А peg или parser-combinator вообще даже без continue можно. Последний вообще через рекурсию, тупо с новыми аргументами (которые опять же можно развернуть в обычный while с массивом/вектором из стека).
Короче, goto не нужен. Только если хочется сделать одну километровую говнокодистую портянку сразу со всеми видами состояний, да и то спорно.
Рекурсия обычно гораздо сложнее для понимания чем GOTO
Ну вот тут я вообще крайне не согласен. Это же получается вызов метода с явными аргументами, пусть и рекурсивно, против запуска всего заново с непонятно как изменившимся стейтом.
В любом случае в первом варианте аргументы очевидны, а во втором - нужно искать по коду что поменялось вообще.
Это если у вас язык поддерживает рекурсию "искаропки". Скажем, Паскаль и Си такое умеют с рождения, а вот Фортран и Бейсик тех лет (в частности, Спектрумовский) - нет.
Рекурсия там была всё ещё возможной, вот только её реализация вручную была ну эээ никак не проще GOTO.
Эх давно хочу но руки не доходят написать серию статей с названием Антирекурсия. Разумеется с большим набором конкретных примеров. Суть в том что если вы видите "лаконичный" код с использованием рекурсии, то на самом деле внутри него всегда скрывается мозгодоробительнейшая логика, которую никто в здравом уме не будет использовать (если будет её видеть). Меня всегда удивляют люди, которые восхищаются рекурсией. Мне кажется что они просто не понимают как она работает на самом деле, хотя они думают с точностью до наоборот, что не понимают те кто не используют рекурсию :)
Prolog с вами не согласен 😂
Суть в том что рекурсивный алгоритм, даже кажущийся однопроходным, на самом деле содержит как минимум два цикла "цикл закрутки" и "цикл раскрутки" (названия мои, возможно у них уже есть другие названия, просто я не встречал раскрытия этой темы).
При этом даже в тривиальных случаях (типа факториала) один из этих циклов явно лишний. И это не "особенности двоичного кода", а алгоритмически лишний цикл.
Но в общем случае этих циклов ни разу не два, а больше и одна из проблем рекурсивных алгоритмов в том что сложно на глазок определить сколько именно, да и не на глазок надо изрядно мозг поломать. Но насколько я понимаю поклонники рекурсии просто предпочитают "не видеть этих сусликов". Ну или они все гении и это для меня рекурсия выглядит айсбергом скрывающим настоящую алгоритмическую сложность, а они её "видят насквозь" и "просекают в момент". Подробнее без примеров сложно, а с примерами это уже статья выходит.
Видимо речь про вызов и возврат из вызова.
Рекурентный факториал:
int factorial(int N)
{
if (1 == N)
return 1;
return N*factorial(N-1);
};
выглядит однопроходным но на самом деле алгоритмически работает вот так:
int factorial(int n)
{
std::stack<int> st;
// цикл «закрутки»
while (--n >= 1)
st.push(n);
// цикл «раскрутки»
while (!st.empty())
{
n *= st.top();
st.pop();
}
return N;
}
Т.е. сначала захламляет стек значениями n-1, n-2, ... 3, 2, 1 (про захламление адресами возврата скромно умалчиваю) и только после этого переходит к этапу вычислений.
Но сообразить это глядя на рекурентную запись совсем не очевидно.
И это самая банальщина.
А вот если взять например так же простенько выглядящую функцию Аккермана, то циклов закрутки и раскрутки будет далеко не два и она будет очень долго по ним бегать.
https://wasm.in/threads/rekursivnaja-funkcija-i-razmer-steka.17617/
Между прочим, для малых фиксированных значений n для функции Аккермана есть простые формулы:A(0,m) = m+1A(1,m) = m+2A(2,m) = 2m+3A(3,m) = 2^(m+3)-3а при больших она просто слишком быстро растёт: уже N(4,2) = 2^65536-3, N(4,3) = 2^(2^65536)-3, что ни в какую память не влезет, так что вычислять значения невозможно.
И проблема в том что смотришь на функцию - она выглядит простенько, подставляешь небольшие (с виду) значения и вдруг тебе бац - дикое торможение и переполнение стека.
Это я рассмотрел достаточно полярные случаи - предельно простой и с высокой степенью алгоритмического коварства и непредсказуемости.
С чего это ему быть одним?
Нет, компилятор (за исключением специальных случаев типа хвостовой рекурсии) не станет выкидывать из рекурсии "лишние" циклы "закрутки" "раскрутки" по той простой причине что они неотъемлемая часть рекурсии как Алгоритма. И не в компетенции компилятора решать когда они выполняют полезную функцию, а когда только "мусорят". Это должен понимать программист.
И да, в ассемблерном коде не будет вообще никаких циклов - там будет та же самая рекурсия (вызов из функции самой себя). Компилятор вообще никогда (кроме вышеупомянутых хвостовых рекурсий) не заменяет рекурсии циклами.
Эквивалентные циклы, про которые я пишу, вы сможете увидеть только выполняя программу пошагово и наблюдая за тем что она вытворяет на самом деле.
Я понял - мы говорим и мыслим на разных языках программирования.
И я зря всё свалил в одну кучу - нужно чётко разделить.
Практически всё что я написал выше про компиляторы относится к С\С++ и ему подобным.
Они никогда не превращают рекурсию в циклы. Потому что это задача программиста.
Да многоцикловую сущность рекурсии иногда можно применить для того чтобы заменить много циклов на одну рекурсию. Например, в классическом подходе к вычислению определителя матрицы. Я когда то даже игрался с его оптимизацией . https://github.com/wmurw/determinant/tree/master. В этом случае много циклов "закрутки/раскрутки" рекурсии идеально ложатся в математическую суть задачи. А попытка прописать все эти циклы вручную без рекурсии - будет жёстким насилием над мозгом программиста.
Но попробуйте найти этим способом определитель матрицы хотя бы 100х100 и компьютер наглухо зависнет. А основанный на циклах метод Гаусса легко одолеет и матрицу 1000х1000 (и он тоже ещё не самый эффективный).
Вывод: многоцикловая сущность рекурсии - этакая "осьминожка - раскоряка", которая:
алгоритмически сложна для понимания и имеет крайне низкую наглядность;
предоставляется "как есть" - у программиста нет возможности как то подкорректировать один из этой навороченной комбинации её внутренних условных циклов.
если не зацикливаться на логике рекурсии то можно найти гораздо более эффективный алгоритм, основанный на циклах, которые не тупо пытаются скопировать логику рекурсии, а делают шаг в сторону и идут принципиально другим путём. Зацикленность на рекурсивном мышлении мешает программисту находить эти эффективные алгоритмы.
В связи с этим идея ФЯП - заменить вообще все наглядные, логичные, понятные с беглого взгляда циклы на "более универсальные" рекурсии для меня звучит полным бредом. Рекурсия вот ни разу не универсальнее - наоборот она очень узко специализирована. Да иногда одна рекурсия может заменить много сложно связанных циклов, но это не управляемый процесс - либо этот паззл-осьминог встанет на своё место либо нет и никак его не впихнёшь. А вот алгоритмическое мышление программиста при этом корёжится и внимание направляется вот совсем не в ту сторону. Этакая идеологическая диверсия.
А так называемая "хвостовая рекурсия" в моей картине мира выглядит так:
разработчики ФЯП повелись на странную идею отказаться от циклов;
попробовали с этим жить и осознали что не очень получается;
вернули циклы обратно в виде кривого костыля, зато исходную идею вроде как сохранили.
Опять же, доказывать утверждения о рекурсии проще, чем о циклах. Вплоть до того, что у нас есть много пруверов вроде coq/agda/etc, основанных на индуктивно-рекурсивных рассуждениях, и около ноля аналогичных пруверов на мутабельных циклах.
Согласен что в Математике (из которой она и пришла) рекурсия восхитительно прекрасна! Проблемы с ней возникают когда её переносят в программирование, особенно если делают это математически формально, без трассировки того что при этом происходит в программе.
любой алгоритм на циклах выражается через рекурсию. И обратно, конечно — это эквивалентные по выразительной силе (но не по простоте рассуждений, хоть мы и не согласны с вами, в какую сторону) формализмы.
Чисто формально - да, а вот на практике проблема в том что:
рекурсия это не один, а всегда несколько условных взаимосвязанных циклов;
у программиста нет прямого доступа к этим внутренним циклам (в отличии от программы на комбинации циклов, где всё максимально прозрачно);
иногда можно заменить мозгодробительно сложную комбинацию циклов на простую и элегантную рекурсию, но для этого "звёзды должны сойтись в правильный знак на небе". Найти подходящую рекурсию для такой замены можно исключительно редко, а зачастую ещё эффективнее просто взять другую комбинацию циклов (см. мой пример: формальный определитель vs Гаусс).
А вот если мы поступаем наоборот - заменяем один или несколько циклов на комбинацию из нескольких рекурсий, то с чисто математической точки зрения проблема решается. А вот если посмотреть на трассировку этого в программе, то выяснится что каждая рекурсия притащила с собой ещё по десятку неочевидных и неконтролируемых внутренних циклов и этим убила напрочь алгоритмическую эффективность.
Вот я управляемо взял и переписал ваш факториал так, чтобы он гарантированно был «одноцикловым» в ФП.
Да в отдельных случаях можно научить компилятор нормально превращать рекурсию в цикл. Только помимо вопроса "а зачем тогда с рекурсией то заморачивались?" это очень частный случай для очень простых задач начального уровня (факториал как раз такая). А вы попробуйте научить компилятор превращать рекурентный определитель в метод Гаусса с повышением быстродействия не в разы, а на порядки! Причём достигается это как раз алгоритмически!
Проблема рекурсии в том что в программировании это всегда "айсберг" с маленькой вершинкой и огромной подводной частью и склонностью неожиданно вращаться, потому что где-то подтаяло. Но увидеть это можно только при трассировке программ. Если подходить к вопросу чисто математически то вы будете видеть только те самые красивые ледяные вершинки и восхищаться ими. Вот именно в этом и заключается алгоритмическое коварство рекурсии.
«разработчики процедурного программирования повелись на странную идею выделять всё в функции, попробовали с этим жить, вернули всё-в-одном через кривой костыль инлайнинга и фьюжн базовых блоков»
Если посмотреть на это через призму трассировки то масштаб проблемы будет на насколько порядков меньше чем в теме рекурсии. А вот плюсы от этой "странной идеи" реально есть и они в большинстве случаев перевешивают минусы, а если вдруг не перевешивают, то есть "кривой костыль" :).
Суть в том что если вы видите "лаконичный" код с использованием рекурсии, то на самом деле внутри него всегда скрывается мозгодоробительнейшая логика, которую никто в здравом уме не будет использовать (если будет её видеть).
Если я вижу простой лаконичный код, то каким образом он может быть мозгодробительнейшим? Или Вы имеете ввиду итоговый двоичный код, в которые его превратит компилятор?
Имеется в виду, что в простом коде будет вызов библиотечной функции, типа "а сделай-ка мне, уважемый, вот по этому массиву быстрое преобразование Имярека".
Ну про захламление стека и так много написано. Поэтому я про избыточную и трудно предсказуемую сложность рекурсивных алгоритмов. Чуть подробнее в моём соседнем ответе выше.
Дарю:
В высокоуровневых языках для стейтмашин хорошо подходит switch, кроме безусловного перехода еще и всякие pattern matching на него хорошо ложатся.
Есть и другой взгляд на вещи - существует только команда безусловного перехода и команда пропуска следующей команды. Такой подход позволяет просто реализовать конвейер
Понятно, что в каких-то случаях использовать GOTO нормальная практика. В ядре линукса например он используется в том числе чтобы реализовать что-то вроде деструктора - позакрывать там файлы при выходе из функции например. Но в целом для современных языков GOTO не очень нужен. GOTO прямо провоцирует нарушать правило одного выхода из функции (см. misra), которое не лишено смысла. Используя что-то современное вроде Python или C++ в отличие от Си у вас есть инструменты для этого вроде менеджера контекста with или идиома RAII, которую удобно реализовывать через деструкторы.
GOTO прямо провоцирует нарушать правило одного выхода из функции
Это как это?
Ну потому что у вас есть возможность прыгать почти в любое место в коде (в С++ например нельзя обходить инициализацию переменной).
А имея просто return шанс что выход будет один все же выше.
А еще у goto есть другие недостатки: усложнение статического анализа например.
В смысле вплоть до из одной функции в другую? В этом случае программиста уже никакие парадигмы не спасут.
Сам оператор GOTO осуществляет переход по метке, но только в рамках одной функции. Для прыжков между функциями используются функции Си
setjmp / longjmp
sigsetjmp / siglongjmp.
Префикс sig значит signal, сохраняет маску сигналов процесса (еë можно и потерять при прыжке)
В самом ассемблере есть три вида прыжков: shortJump, Jump, longJump. Последний позволяет прыгать на другие сегменты. А первые два отличаются набором допустимых адресов.
а чего вы так упёрлись в единственность return? Вообще-то есть противоположный и очень толковый паттерн про early return / fail fast.
Да и для функций с развесистыми деревьями решений множественный return рулит.
Очень хороший оператор. Мания удалять GOTO из языков программирования - это победа сил зла над силами разума.
Кстати, а в каких современных языках, кроме фортрана, он реально используется (а не формально присутствует для обратной совместимости), и в каком обычно контексте?
Я застал древний фортран, где GOTO использовался на каждом углу, и читать этот код было невозможно. Даже когда прыжки GOTO строго вложены друг в друга, это уже нечитаемо, имхо. А если траектории прыжков пересекаются (например, со строчки N1 прыжок на N4, а со строчки N2 прыжок на N5), то это та самая катастрофа, попав в которую хочется вырезать все GOTO каленым железом, включая и их использование
в быту ;-)
Как известно, в русском языке такой оператор появился очень задолго до первых языков программирования и вообще вычислительной техники ;-)
P.S. Вдогонку сообразил еще один аргумент супротив неправильного употребления GOTO: совсем небольшая модификация этого простонародного оператора запросто может приводить (и во многих алгоритмах из позапрошлого века реально приводит)
к UB
... иди туда, не знаю куда ....
;-)
Сейчас я по-прежнему пишу на фортране, но немного в другом стиле: а именно, почти без GOTO. Однако, есть пара контекстов, где я не просто не избегаю GOTO, а наоборот, использую его, как стандарт. Наиболее типичный случай - это переход из тела функции в самый ее конец (обычно на стандартную метку 900) при обнаружении каких-то проблем в любом месте функции. Код после метки 900 анализирует локальный номер ошибки и ее контекст, формирует диагностические сообщения для юзера и для протокола, восстанавливает среду (например, функция могла открыть какой-то файл или аллоцировать память - файл надо закрыть, а память освободить) и устанавливает возвращаемое значение с учетом вида ошибки. Такая схема позволяет:
1) приблизить проверочный код в теле функции к тому месту, где проверяемые условия непосредственно задействованы, не разрывая логику функции. Так, входные параметры проверяются сразу при входе в функцию, а условный знаменатель - непосредственно перед делением на него. Это упрощает модификацию кода: проверочные операторы легко менять одновременно с исполняемыми;
2) При этом код, выявляющий нештатную ситуацию, занимает минимальное место и не мешает чтению общей логики (обычно он состоит из одного-двух IF-ов с последующим GOTO);
3) Весь код восстановления собран в одном месте, что позволяет избежать его дублирования после каждой проверки. Причем, он знает, из какого именно места произошел переход на метку 900, и с учетом этого выполняет все нужные операции в нужном порядке.
Еще изредка у меня GOTO заменяет простой и короткий цикл WHILE с постусловием в тех случаях, ЕСЛИ
1) этот цикл практически всегда выполняется один раз (и лишь изредка надо его повторить два-три раза) И
2) запись постусловия в формате IF(...) GOTO улучшит читаемость кода.
Я таким способом подчеркиваю для себя линейность алгоритма. Но имхо этот случай гораздо более спорный, чем описанный выше. Даже несмотря на обязательное требование к таким GOTO, что они должны вести не более, чем на несколько операторов назад, и что во внутреннем блоке не должно быть никаких ветвлений или условий.
Кстати, а в каких современных языках, кроме фортрана, он реально используется
Внезапно, в C и C++ для выхода из вложенных циклов. Насколько мне известно, это последнее оставшееся в живых "легальное" использование goto в высокоуровневом коде на C/C++.:)
Ещё он есть в Rust для тех же целей, под именем labelled break.
Внезапно, в C и C++ для выхода из вложенных циклов. (...)
Ещё он есть в Rust для тех же целей, под именем labelled break.
А это точно GOTO?
Я не знаю ни C, ни Rust, но если я правильно понял пример по ссылке, то создается впечатление, что это больше похоже на обычный exit с указанием уровня вложенности (на какой уровень выйти). В фортране для этого точно так же надо присвоить нужному циклу имя (даже синтаксис совпадает ;-), после чего оператор "exit LoopName
" передает управление из любой точки внутри цикла LoopName
(включая внутренние циклы любой вложенности) на первый оператор ПОСЛЕ цикла LoopName
. С той символической разницей, что вместо закрывающей скобки цикл у нас закрывается фразой "end do
" либо (по желанию программиста) "end do LoopName
".
Разумеется, такой exit можно заменить на GOTO + метку, но зачем? В фортране такое сейчас и в голову никому не придет. Если в Rust эта конструкция работает аналогично, то мне очень трудно сассоциировать ее со спагетти-кодом.
А это точно GOTO?
Точно.
Собственно, можете посмотреть какое-нибудь ядро линукс, там именно для этого goto широко используется.
Допустим, функция должна захватить ресурсы А и В, и ещё выделить память. Тогда у неё в конце три-четыре метки выхода:
exit3: kfree(mem); // освободить память
exit2: fclose(B); // освободить ресурс В
exit1: fclose(A); // освободить ресурс А
exit: return result; // выйти
Соответственно, если где-то возникла ошибка, будет goto на одну из этих меток.
В языках без ключевого слова goto, а также без defer
и/или RAII можно заменить это циклами do
или while
и использовать break <метка>
. Но это лишние уровни вложенности кода.
А это точно GOTO?
Да. Просто они называются по разному но по сути:
switch, if, break, return, catch, finally
они все под капотом меняют ход программы и по сути - GOTO. (или в терминах асемблера - jump)
Очень хороший оператор
Соглашусь с комментатором выше. Вы просто не видели программ, построенных на базе пересекающихся goto. Какая-нибудь машина состояний. например. Я понимаю, что там это оправданно, и компилятор в итоге именно такую лапшу из goto (jmp, br, you name it) и выдаст. Но лучше пусть он этим и занимается.
Мысль о том, что что-то можно довести до абсурда, а потому вообще не следует это употреблять, никогда не казалась мне разумным аргументом. Даже в эпоху яростной борьбы с goto, которую я застал. Плохо и неправильно можно употребить вообще что угодно. На мой взгляд, из этого не следует совсем ничего.
На моей памяти с goto яростно боролись в университетской среде - потому что так проще проверять работы студентов. Из реальных языков никто goto не выпиливал. Ну точнее если Вирт ещё оправдывался, что goto оставлен для переноса функций из фортрана, то в js ключевое слово goto не реализовали, но зарезервировали, а главное сам goto имплементирован.
В "классических" Бейсиках GOTO, кажется, бывал и вычисляемым и даже использование функции RND напрямую иногда мог допускать (?!!).
В классических бейсиках с помощью гото можно было изнутри подпрограммы в основной код обратно вынырнуть. И потом обратно занырнуть.
Вот это фича так фича )
По-моему, там чаще всего просто не контролировался выход из цикла, например цикла FOR, и таким образом можно было неограниченное число раз выпрыгнуть оттуда и запустить этот цикл снова и так далее, и других средств контроля и стека возвратов не было - по крайней мере, в самых ранних версиях. Во всяком случае, раннний Бейсик типа WANG был этими "фичами" очень богат.
Showing my age, in VIC20 basic you could say
On X Goto 100, 200, 300, 400
which would Goto line 100, 200 etc depending on X being 1, 2, 3 etc. And the good news is, it works in VB6 too! You'll get an error if the line numbers you're going to don't exist though.
ON x GOTO 100, 130, 150, 190, 100
Рефлексы не пропьёшь!
Он ещё оказался хорош при обучении детей С++: https://dtf.ru/u/541204-huldra/1154069-kak-nauchitsya-pisat-igry-na-s-chast-3
Это гениально.
Мне голову сломал FORTH. До сих пор, как вспомню так вздрогну :)
Тогда возможно понравится исследование, насколько он может быть быстр (спойлер: очень быстр) https://habr.com/ru/articles/856480/
Помню, познакомился с ним в школьные годы на клоне ZX Spectrum. После Pascal и Basic "идеология" написания кода в Forth действительно взрывала мозг. Но этим и запал в душу )))
FORTH, наверное, нельзя в полной мере назвать языком, скорее это способ реализации 0-адресной машины. Те, кто помнит калькулятор Электроника Б3-21, ностальгируют. Стек на 60 слов, и ни в чем себе не отказывай. А реально на FORTH выполняли управляющие системы, существовала даже м-схема, реализующая ядро.
Lazarus Pascal
Вообще-то Free Pascal. Lazarus - это IDE для Free Pascal, использующее компилятор fpc.
в детстве для меня самое сильное озарение в программировании было, когда я узнал, что в языке есть возможность написать функцию, которая вернет значение. было круто.
Какой любвеобильный автор, который восхищается непонятно чем. Ему надо бы поработать с XSLT, интересно было бы послушать впечатления...
В языке Basic оператор Dim объявляет любую переменную, совершенно не обязательно массив
Сегодня Prolog практически не существует.
Сейчас у автора оригинальной англоязычной статьи будет очередной "взрыв мозга". Не поверите! Реализаций языков Prolog более сотни. Из этой сотни в настоящее время активно (коммерчески и промышленно) применяют более тридцати реализаций. Например, почитайте про XSB, SWI, Ciao.
В Prolog вы не пишете алгоритмы, а учите программу, как думать.
Вот эта фраза — это оговорка по Фрейду, которая говорит о том, что автор оригинальной статьи никогда не программировал ни на одном из языков Prolog. Программисты (любые, в т.ч. и логические) не учат компьютеры "думать". Они всего-лишь пишут программы, компилированный код которых машины (тупо) исполняют. И машине всё равно на чём там изначально писали — на ассемблере, на языке Visual Prolog или на языке Python.
SQL в большой мере основан на подмножестве Prolog с большим упором на оптимизацию в ущерб простоте.
Автор совершенно безграмотный. Грамотный специалист отметил бы что языки SQL и языки Prolog относят к группе декларативных языков программирования. Ни одна классификация не относит ни одну из реализаций SQL (кстати, их тоже много) ни к какому "подмножеству" языков Prolog. Если разработчикам PostgreSQL рассказать, что по мнению пиндосских блогеров они "подмножество" Visual Prolog, они будут долго и нервно курить в углу.
Множество математических теорем было доказано благодаря превращению их в код на Coq.
Реклама такая реклама.
Названия этих многих теорем в статье стыдливо опущены
Я как то даказал на Coq комутативность и ассоциативность сложения и умножения. Ещё дистрибутивность. А на большее меня не хватило, забросил Coq.
Теорема Пифагора была доказана ещё древними вавилонянами.
Теорема о четырёх красках была доказана перебором 1936 вариантов. В 2005 её только повторили.
Теорема о корректности системы типов в dependent dependency calculus и прочие удручающе напоминают те самые особенные интегралы, которые можно взять... но которые встречаются только в курсе интегрального анализа.
Очень многие математики относятся к Coq крайне скептически - фактически, он требует просто записывать готовое доказательство, разворачивая все "после тривиальных преобразований получим".
Думаю, за меня неплохо ответит книжка Имре Лакатоса "Доказательства и опровержения". Там довольно подробно расписана проблема того, как одна тривиальность проваливается в другую.
Поэтому открытие теорем кажется мне более важным, чем их формальное доказательство.
Из материалов этой книги довольно ясно следует, что однозначное определение доказательства вообще невозможно. Можно говорить только о тавтологических утверждениях, но пользы от них немного (они не увеличивают знание). Ну или о консенсусе математиков по какому-то вопросу.
Ну и вспоминайте получше кандидатский курс - то неужели там не рассказывают, что до конца неясно, на каких аксиомах основана математическая модель ОТО (понятно, что пространство неэвклидово, но как его описать неясно) и что мы не знаем, выполняется ли там, к примеру, аксиома Архимеда? Однако эта нехватка формализации не мешает вести исследования. Есть даже подходы, которые прямо отрицают важность этих оснований.
Невозможно отличить теоремы от не-теорем без доказательства, что утверждение теоремы действительно следует из данного набора аксиом.
То есть великая теорема Ферма до 1995 года не была теоремой? Это новое слово в истории математики.
Из этой заявления следует много не менее странных: например, неясно, как быть с невыводимыми теоремами, которые описываются теоремой Гёделя о неполноте. Их истинность доказать можно, а вывести из аксиом - нельзя.
Упомянутая вами в скобках часть прямо мешает.
И какие же математические проблемы стоят перед современной физикой? Для многих случаев ситуация прямо обратная: из формул следует существование мультивселенных, физического смысла парадокса Баноха-Тарского и т.п. Проблем с математическими методами (как было при открытии дифференциального счисления или комплексных чисел) нет.
Исследования Воеводского или Вопенки ничего не меняют в современной физике. Даже теория струн в ней ничего принципиально не поменяет (КЭД и так даёт избыточную точность для практически всех экспериментов).
Довольно странно, что вам это неизвестно. Если вы учились в аспирантуре, то хотя бы лекции Фейнмана по КЭД должны быть вам знакомы.
Это новое слово только для тех, кто с математикой знаком по научно-популярным видосам на ютубе.
Ну назовите мне хотя бы 5 математиков, которые не считали Великую Теорему Ферма теоремой до того момента, как она была доказано.
Только, пожалуйста, с точными ссылками на соответствующие места в их работах.
Это теоремы метаязыка (в котором мы рассуждаем об аксиомах языка, и в котором их истинность доказывается), а не языка.
В первой теореме Гёделя речь идёт именно о теоремах языка (в его классическом доказательстве - над числами).
Следовательно, отсутствие этих формул (выводимых и обосновываемых средствами математической логики) не дало бы вам шанса на соответствующее физическое содержание.
У вас большие проблемы с логикой. И комментарием выше вы признавали, что эти формулы невыводимы средствами математической логики, потому что для ОТО нет системы аксиом.
Я и спрашиваю - какие ПРОБЛЕМЫ у ОТО из-за отсутствия системы аксиом? Формулы спокойно получены и подтверждены и без этой базы.
Если в нашем мире не выполняется аксиома Архимеда - как это изменит современную физику?
И опять же. по какой из математических логик вы собираетесь их выводить? По аристотелевой? Что изменится, если мы будет выводить, пользуясь интуиционной? А есть ещё темпоральная и много какая ещё.
Хотя конкретно книжечку Фейнмана я просмотрел ещё в старшей школе.
Вы, видно, имеете в виду его автобиографию. Его книга по КЭД посвящена другим вопросам.
До формализации математики этому действительно не было альтернатив. С развитием матлогики решать корректность теоремы большинством голосов больше не нужно.
Из курса истории математики вам должно быть известно, что и формализм Гилберта, и логицизм Рассела так и не нашли окончательного решения.
А ваше трепетное отношение к формальным доказательствам вызвано тем, что вы больше просто ничего не умеете.
Не знаю уж, сколько ему лет, но мне кажется, что даже 5-6 языков с детства/подросткового возраста и вплоть лет до 40 выучить и применять на регулярной основе, причем так, чтобы оценить преимущества/недостатки в полной мере, очень тяжело.
"функциональность с языками, в которых они появились не впервые"...
Кто они? В ком появились?
PHP, javaScript — неплохие языки (особенно когда работаешь с вебом).
А сейчас Python завоевал всю молодёжь... Не знаю, насколько это хорошо или плохо )
Паскаль рулит.
Тоже с Turbo Pascal 5.5 хотел на Turbo C 2.0 перейти - не смог. Тупизна страшная. А в паскале - всё логично и понятно.
Ну не только он с 8-и лет.
Так же начинал где-то в 7-8 лет на спектруме )
Надеялся прочитать про Лисп (или Схему).
А где же bash или perl с их незабываемой работой с текстом
bash, в отличие от perl, с текстом работать практически не умеет. В bash-скриптах почти вся работа с текстом осуществляется внешними программами.
(Презрительно сплёвывая через зубы:) Пацан! А собирал ли ты свой первый элемент ИЛИ из овна и палок проволочек и диодов?!
Я бы в топ-3 поставил Haskel, Lisp и Forth. Правда, я не знаком с Elixir, Coq и Rust.
Как мне кажется, название статьи не соответствует содержанию. Например, у автора Basic - это взрыв мозга просто потому, что это первый ЯП, с которым он познакомился. Pascal - потому что автор первый раз увидел IDE. И так далее.
Автор описывает свою историю. Соответственно, и взрывы мозга у него свои. Более того, взрывы эти обусловлены не столько конкретным языком, сколько технологиями и парадигмами, связвнными с этим языком прямо (параметрический полиморфизм в Ocaml) или косвенно (IDE в Turbo Pascal). Если бы он это встречал первый раз в других языках, последовательность вщрывающих языков была бы иной.
Странно, что раз его мозг перестали взрывать, то это не он стал автором Брэйнфака.
Автор упомянул, что писал на UnrealScript, при этом в числе мозговынносящих языков ему нет отдельно посвященного параграфа.
Этого я не понимаю. У меня был опыт с этим ЯП — я не знаю до чего он дорос или скатился сейчас, я говорю о языке образца 98-го года.
И до сих пор у меня именно от него сильнейший вау-эффект.
С одной стороны, код на этом ЯП прозрачно и непринужденно (динамически) линкуется с функциями, классами (методами классов) и операторами, написанными на C++ и живущими в DLL. Это значит, что под капотом у языка такое же простое и прчмолинейное ABI, как у C/C++, где int-переменная — это реально всего лишь 4 байта, а не хренов контейнер с кучей полей о типе, владельце и инфой о том, кто на ком стоял. Чуть допилив исполнительную среду, можно, по идее, и вызов WinAPI было сделать таким же прозрачным и непринуждённым, как вызов соседней процедуры.
С другой стороны, в этом языке классы были, пардон за вынужденную тавтологию, гражданами первого класса.
Это значит, что упомянание класса или ссылку на класс (именно на сам класс, а не на экземпляр класса) можно записать в переменную, эту переменную передать куда-нибудь в функцию, а там внутри переданное значение могут передать в качестве операнда оператора new (правда там он назывался Spawn, а не new). Причем можно объявить переменную или аргумент так, что в неё можно будет записать только классы, являющиеся подклассами определенного базового типа.
Стейты у объектов из коробки: еще одна примечательная функция. Если у класса 7 методов и объект может пребывать в 3 разных состояниях, не нужно в каждом из 7 методов делать switch или каскад if-ов: можно, используя синтаксические возможности, определить 3 стейта, которые породят 3 скоупа, в каждом из которых можно написать собственную имплементацию метода, специфичную для данного стейта.
Я бы хотел такой язык с такими фишечками и таким же приятным синтаксисом для написания general-purpose программ и прикладного софта. Особенно если подружить его ООП-механизм с COM.
Языки программирования, взорвавшие мой мозг