Вещественные числа можно представлять в виде функций, принимающих требуемую точность и возвращающих приближение в виде рационального числа (пары длинно-арифметических (т.е. произвольно больших) целых). Рациональные числа (3, 0.1, ...) в таком функциональном представлении независимо от требуемой точности возвращают самих себя в виде пары. Иррациональные числа (число пи, число Эйлера, ...) описываются вычисляющей их с заданной точностью функцией. Операции и функции (+, корень, ...) принимают и возвращают вещественные числа -- то есть функции (в соответствии с представлением). Возвращаемая функция (результат) должна запомнить аргументы операции -- при передаче ей точности она вычисляет требуемую точность подвыражений, запрашивает их приближения и вычисляет своё приближение. Если надо посчитать сумму с абсолютной погрешностью не более d, можно запросить подвыражения с абсолютной погрешностью не более d/2 и сложить полученные рациональные приближения (пример на Python):
def add(a, b):
return (lambda d: a(d/2) + b(d/2))
Необязательно представлять эти числа именно в виде функций (в чистом Си нет лямбд) -- можно и в виде дерева, но мысль останется та же. Число пи в этом представлении, как и в математике -- это не полная запись его значения в виде числа с плавающей точкой, но запись его определения в виде вычисляющей его функции -- важно отметить, что числа при этом не просто большие, а произвольно большие (и произвольно точные -- но не бесконечно). Называется это всё символьными вычислениями, и, конечно, у них есть минусы, но сейчас не об этом.
Но самое важное здесь это второе предложение "Иррациональное число может быть представлено в виде бесконечной непериодической десятичной дроби.".
Что это значит? Заметили, что в примерах я дал вам буквенное обозначение? Это не просто так, это представление иррационального числа, ВАЖНО - сама запись π это не само иррациональное число, это всего лишь его представление, и оно является чем угодно, но не иррациональным числом. Само Иррациональное число оно бесконечно. Понимаете?
То есть его невозможно записать по определению. Никакой памяти в компьютере не хватит чтобы его записать. Это невозможно!
И в самом деле, самое важное -- что "иррациональное число может бытьпредставлено в виде бесконечной непериодической десятичной дроби". А может и не быть (кроме того, тут слово "представлено" имеет значение отличное от "записано" и ближе к "воображено"). Запись π ровно так же отражает стоящее за ней иррациональное число, как и воображаемое бесконечное число с плавающей точкой, только вот второе и в самом деле нельзя записать, а потому оно может помочь говорить о некоторых свойствах числа пи, но никак не может быть его определением (в некотором смысле как раз "является чем угодно, но не иррациональным числом"). Настоящим определением числа пи может служить, скажем, "периметр единичной окружности" или "корень из ряда обратных квадратов, умноженного на шесть". И вот у нас уже есть запись числа π, вмещающаяся не то что в память компьютера, а даже на клочок бумаги.
На этом конструктив и критику заканчиваю -- спасибо автору за внимание к такому тонкому вопросу как представление чисел в компьютерах и языках программирования!
К сожалению я тут немного пессимистичен, Никита ещё более -- опыт PL/I, например, видимо не был учтён, многие языки сегодня всё так же безразмерно разбухают. Боюсь что с Си будет то же -- он уйдёт, на его место придёт почти такой же, не сделавший выводов из ошибок прошлого. Надеюсь эта статья поможет кому-то в будущем сделать лучше, хотя это и излишне оптимистично. =)
Что касается заданных вами вопросов -- я тоже не имею на них ответов. =)
Дополню ответ @netch80с которым согласен. Вы описываете Си как язык высокого уровня, и конечно тогда он имеет право давать любые гарантии и любую модель исполнения. Вот только как язык высокого уровня он плох и даёт сложные для понимания и контроля гарантии (если для вас это не так -- поздравляю, вы в сотни раз умнее меня =) ) -- и тогда вопрос -- а зачем он нужен? Ведь когда мы пишем программы мы хотим что бы они работали -- а в силу сложности Си гарантировать это ... сложно. И при этом не важно насколько быстро они работают -- если они работают с ошибками, неправильно -- то они просто не работают.
В Rust много здравых идей, но мне он кажется избыточно сложным. А сложность языка имеет свойство перекладываться на сложность программ, что в свою очередь провоцирует ошибки. Кроме того, про Rust важно понимать и помнить, что он не избавляет от всех ошибок, а только от некоторых (прошу прощения если оскорбил -- есть знакомые растовцы, которые считают что их код абсолютно безопасен и корректен ведь написан на Rust).
Что касается замены Си, Rust мне кажется больше заменой плюсам, но если потянет -- интересно будет посмотреть на мир, где железо заржавело. =)
Отчасти это и правда решение -- насколько знаю Линукс это активно использует, обвешивая весь свой код сложной системой сборки, где указывается какие правила где применить.
Из проблем тут могу назвать то, что всё же набор всех возможных правил оптимизаций уже невероятно увесистый, и он только продолжает расти -- поэтому хотя в стандарт их включить и можно, но его придётся постоянно обновлять, дописывая новые появившиеся оптимизации. А это в свою очередь значит, что оптимизирующие компиляторы (gcc и clang в том числе, а они сейчас самые популярные), в рамках которых новые оптимизации будут разрабатываться, будут несовместимы со стандартом большую часть времени.
Более того, думаю критически неудобно, что по сути в таком случае мы мыслим на многих языках сразу -- не считая необходимости постоянно осекаться и спрашивать "а включил ли я все необходимые свойства сборки?". Кажется что ассемблер в связке с ещё одним языком тут будет банально проще.
Ну и конечно "должен умереть" это преувеличение -- никому он ничего не должен, на нём вполне можно писать рабочий код, а если бы мог стать лучше -- это было бы прекрасно. Но кажется что этот язык по сути свернул куда-то не туда и шансов на улучшение у него мало. Но всё может быть, успехов вам! =)
Не совсем. Дело в том что с точки зрения стандарта есть вещи, которые вы видимо имеете в виду под выстрелом в ногу -- провоцирующие UB. Когда разработчики компиляторов дошли до того, что стали трактовать UB как запрещёнку, они начали ломать существующий код, который был написан не столько с опорой на стандарт, сколько на по факту наблюдаемое поведение -- и такого кода весьма много. В общем-то правы тут разработчики компиляторов, вот только жить от этого не легче -- и вся эта ситуация в целом показывает, чем плох Си -- это высокоуровневый язык, но мимикрирующий под низкий уровень, причём не самым умелым образом, ведь сложился он больше исторически (читайте: слеплен на коленке и допилен по ходу дела). При этом если писать с использованием реально низкоуровневых вещей, в отрыве от стандарта, разработчики компиляторов имеют полное право сломать такой код при очередном обновлении (естественно можно применить ассемблерные вставки -- но тогда и пишем мы не на Си, а на ассемблере). Надеюсь теперь стало понятней. =)
Почему вы думаете, что парадигмы привносят только ограничения? Мне кажется довольно странным мир, где есть некоторое универсальное супер-программирование в котором всё можно, и где вся наша задача в том, что бы ограничить свои возможности, что бы не выстрелить себе же в ногу. На ассемблере можно написать всё, да, но то что на самом деле привнесли первые языки программирования -- новый способ мыслить о программах. Именно привнесли, а не ограничили. Например, в создании Тони Хоаром быстрой сортировки огромную роль сыграло его знакомство с Алголом, где крайне элегантно вводилась рекурсия -- он просто не думал так до этого (https://youtu.be/tAl6wzDTrJA -- около 14:40). Так же и другие парадигмы больше привносят новых способов мыслить о программе, чем ограничивают уже существующие. Подчеркну, что формулировка структурного программирования как "не используй goto" -- крайнее упрощение.
При этом я конечно понимаю основную мысль, что программировать сложно, возможностей невероятно много (вероятно даже избыточно много), и хорошо себя ограничивать и поддерживать дисциплину. Только вот например Дейкстра в своей Дисциплине Программирования имел в виду (насколько я понял) не следование некоторым ограничивающим правилам, которые приведут вас к безошибочному коду -- но напротив, он признавал принципиальную сложность программирования и предлагал дисциплину в смысле научной дисциплины, когда вы полностью осознаёте свои программы и можете доказать их корректность, а не просто показывать тестами и поддерживать различными методиками их около-работоспособность (вспомните его "Program testing can be used to show the presence of bugs, but never to show their absence!" или даже "software engineering has accepted as its charter "How to program if you cannot."").
Я не пытаюсь сказать что тестирование не важно, а ограничивать программиста не нужно, хочу просто подчеркнуть, что, по-моему, статья несколько утрирует проблему, а предлагаемые ей решения, если их воспринимать слишком серьёзно, могут привести к серьёзным же проблемам.
Мне кажется выбор лицензии дело вкуса -- принципиально тут что она свободная. Но вы видимо хотите именно копилефт. Так что, если кто-то выложит под MIT/BSD/иной пермессивной -- это искуплением не будет?
Простите, но в ваших проблемах я не вижу, почему они вызваны именно тем, что вы реализуете свою модель как библиотеку.
Я не предлагаю отказываться от трансплайтера, я предлагаю подготовить библиотеку на языке реализации, которая позволит на начальном этапе отказаться от трансплайтера, а впоследствии упростить его разработку.
Новые языки именно потому и нужны, что они позволяют проще программировать в рамках некоторой модели, позволяют думать о программе по-другому. Каждый язык поощеряет некоторые модели — так Хаскель об одном, Си о другом, Лисп о третьем, Пролог ещё об одном, Verilog и VHDL — вообще из другой оперы немного. Имея библиотеку в рамках которой вы можете проводить вычисления по своей модели уже проще создавать наиболее подходящий язык. Я описал путь возможной реализации такой библиотеки, который может упростить последующую разработку.
В любом случае удачи в этом проекте — жду следующей статьи, где, надеюсь, будет продемонстрирована работа программ такого стиля. =)
PS: а есть ли репозиторий с текущим кодом?
По первому пункту, простите, а вы рассматриваете только библиотеку для реализации языка? Я писал о реализации в библиотеке модели вычислений вашего языка. При этом я согласен, что если парсить язык, то это делается или полуавтоматически из текстового описания синтаксиса, или полностью руками. Если же вы говорили о том, откуда получать синтаксическое древо программы на вашем языке, то я предложил способ, отличный как от разбора текста программы, так и от задания дерева непосредственно через функции (насколько понимаю сейчас именно так). Напомню, что я предложил моделировать синтаксис языка через операции классов — где операции языка тривиально отображаются в них. Или это также запредельно сложно?
По второму пункту я просто не вижу, как вам это мешает опробовать вашу модель вычислений в виде библиотеки. Насколько я понимаю, вы беспокоитесь заранее о некоторых пользователях, которых думаю пока что нет. Отмечу, что если реализовать библиотеку с использованием операций для моделирования синтаксиса близкого к предложенному, то трансляция станет тривиальной (так как операции языка переводятся в операции библиотеки непосредственно), и когда библиотека будет готова, транслятор можно будет написать без больших усилий. Вновь спрошу: чем вам так принципиально создавать новый язык, притом именно сейчас, когда модель вычислений не была ещё реализована даже в прототипе (насколько я понял)?
А в чём ваше достижение? Проблема комбинаторного взрыва в таком случае уже решена — нейросети применяются уже давно и обильно. Для их задач проблемы комбинаторного взрыва и не стоит. И разумеется, само существовании таких решений обусловлено тем, что решение полноценной задачи занимает слишком много времени.
Что касается частного решения при отсутствии алгоритмического — само это частное решение обязано быть алгоритмическим. Иначе оно невозможно к исполнению на компьютере. Конечно алгоритм этого решения может быть записан в разных стилях, будь то императивный, логический, функциональный или иной стиль.
В любом случае предложение описывать нейросети в рамках логического программирования — интересное. Вы уже проверили его работоспособность? Ваш язык уже можно протестировать? Если нет — я вновь напомню, что для проверки работоспособности модели вычисления вполне может хватить расширения существующего языка. Та модель которую вы озвучили, как минимум в том виде как я её понял, выразима в качестве библиотеки. При этом вы быстрей довели бы модель до состояния, когда её можно протестировать и не было бы костыля в виде императивной части языка. Вновь спрошу — почему вам принципиально важно создание нового языка для выражения этой модели? Обычно сначала вырабатывается модель, а затем создаётся язык заточенный под неё — и не без причины — так просто проще. =)
Заранее прошу прощения, если язык уже реализован.
Согласен, спасибо за замечание, неаккуратно написал.
Вещественные числа можно представлять в виде функций, принимающих требуемую точность и возвращающих приближение в виде рационального числа (пары длинно-арифметических (т.е. произвольно больших) целых). Рациональные числа (3, 0.1, ...) в таком функциональном представлении независимо от требуемой точности возвращают самих себя в виде пары. Иррациональные числа (число пи, число Эйлера, ...) описываются вычисляющей их с заданной точностью функцией. Операции и функции (+, корень, ...) принимают и возвращают вещественные числа -- то есть функции (в соответствии с представлением). Возвращаемая функция (результат) должна запомнить аргументы операции -- при передаче ей точности она вычисляет требуемую точность подвыражений, запрашивает их приближения и вычисляет своё приближение. Если надо посчитать сумму с абсолютной погрешностью не более d, можно запросить подвыражения с абсолютной погрешностью не более d/2 и сложить полученные рациональные приближения (пример на Python):
Необязательно представлять эти числа именно в виде функций (в чистом Си нет лямбд) -- можно и в виде дерева, но мысль останется та же. Число пи в этом представлении, как и в математике -- это не полная запись его значения в виде числа с плавающей точкой, но запись его определения в виде вычисляющей его функции -- важно отметить, что числа при этом не просто большие, а произвольно большие (и произвольно точные -- но не бесконечно). Называется это всё символьными вычислениями, и, конечно, у них есть минусы, но сейчас не об этом.
И в самом деле, самое важное -- что "иррациональное число может быть представлено в виде бесконечной непериодической десятичной дроби". А может и не быть (кроме того, тут слово "представлено" имеет значение отличное от "записано" и ближе к "воображено"). Запись π ровно так же отражает стоящее за ней иррациональное число, как и воображаемое бесконечное число с плавающей точкой, только вот второе и в самом деле нельзя записать, а потому оно может помочь говорить о некоторых свойствах числа пи, но никак не может быть его определением (в некотором смысле как раз "является чем угодно, но не иррациональным числом"). Настоящим определением числа пи может служить, скажем, "периметр единичной окружности" или "корень из ряда обратных квадратов, умноженного на шесть". И вот у нас уже есть запись числа π, вмещающаяся не то что в память компьютера, а даже на клочок бумаги.
На этом конструктив и критику заканчиваю -- спасибо автору за внимание к такому тонкому вопросу как представление чисел в компьютерах и языках программирования!
Мы вроде такого не писали. =)
Спасибо, хотя это скорее крик души, чем предсказание. =)
Всё течёт, всё меняется. =)
К сожалению я тут немного пессимистичен, Никита ещё более -- опыт PL/I, например, видимо не был учтён, многие языки сегодня всё так же безразмерно разбухают. Боюсь что с Си будет то же -- он уйдёт, на его место придёт почти такой же, не сделавший выводов из ошибок прошлого. Надеюсь эта статья поможет кому-то в будущем сделать лучше, хотя это и излишне оптимистично. =)
Что касается заданных вами вопросов -- я тоже не имею на них ответов. =)
Дополню ответ @netch80с которым согласен. Вы описываете Си как язык высокого уровня, и конечно тогда он имеет право давать любые гарантии и любую модель исполнения. Вот только как язык высокого уровня он плох и даёт сложные для понимания и контроля гарантии (если для вас это не так -- поздравляю, вы в сотни раз умнее меня =) ) -- и тогда вопрос -- а зачем он нужен? Ведь когда мы пишем программы мы хотим что бы они работали -- а в силу сложности Си гарантировать это ... сложно. И при этом не важно насколько быстро они работают -- если они работают с ошибками, неправильно -- то они просто не работают.
Спасибо, мы старались! =)
В Rust много здравых идей, но мне он кажется избыточно сложным. А сложность языка имеет свойство перекладываться на сложность программ, что в свою очередь провоцирует ошибки. Кроме того, про Rust важно понимать и помнить, что он не избавляет от всех ошибок, а только от некоторых (прошу прощения если оскорбил -- есть знакомые растовцы, которые считают что их код абсолютно безопасен и корректен ведь написан на Rust).
Что касается замены Си, Rust мне кажется больше заменой плюсам, но если потянет -- интересно будет посмотреть на мир, где железо заржавело. =)
Именно так. =)
Спасибо!
Отчасти это и правда решение -- насколько знаю Линукс это активно использует, обвешивая весь свой код сложной системой сборки, где указывается какие правила где применить.
Из проблем тут могу назвать то, что всё же набор всех возможных правил оптимизаций уже невероятно увесистый, и он только продолжает расти -- поэтому хотя в стандарт их включить и можно, но его придётся постоянно обновлять, дописывая новые появившиеся оптимизации. А это в свою очередь значит, что оптимизирующие компиляторы (gcc и clang в том числе, а они сейчас самые популярные), в рамках которых новые оптимизации будут разрабатываться, будут несовместимы со стандартом большую часть времени.
Более того, думаю критически неудобно, что по сути в таком случае мы мыслим на многих языках сразу -- не считая необходимости постоянно осекаться и спрашивать "а включил ли я все необходимые свойства сборки?". Кажется что ассемблер в связке с ещё одним языком тут будет банально проще.
Ну и конечно "должен умереть" это преувеличение -- никому он ничего не должен, на нём вполне можно писать рабочий код, а если бы мог стать лучше -- это было бы прекрасно. Но кажется что этот язык по сути свернул куда-то не туда и шансов на улучшение у него мало. Но всё может быть, успехов вам! =)
Не совсем. Дело в том что с точки зрения стандарта есть вещи, которые вы видимо имеете в виду под выстрелом в ногу -- провоцирующие UB. Когда разработчики компиляторов дошли до того, что стали трактовать UB как запрещёнку, они начали ломать существующий код, который был написан не столько с опорой на стандарт, сколько на по факту наблюдаемое поведение -- и такого кода весьма много. В общем-то правы тут разработчики компиляторов, вот только жить от этого не легче -- и вся эта ситуация в целом показывает, чем плох Си -- это высокоуровневый язык, но мимикрирующий под низкий уровень, причём не самым умелым образом, ведь сложился он больше исторически (читайте: слеплен на коленке и допилен по ходу дела). При этом если писать с использованием реально низкоуровневых вещей, в отрыве от стандарта, разработчики компиляторов имеют полное право сломать такой код при очередном обновлении (естественно можно применить ассемблерные вставки -- но тогда и пишем мы не на Си, а на ассемблере). Надеюсь теперь стало понятней. =)
Если бы проблема была только в переполнении знакового... Но да, уже это весьма неприятно. =)
Бывает по-разному, можно написать -1, можно +1, у меня при использовании clang работает один вариант, а при gcc -- другой.
Почему вы думаете, что парадигмы привносят только ограничения? Мне кажется довольно странным мир, где есть некоторое универсальное супер-программирование в котором всё можно, и где вся наша задача в том, что бы ограничить свои возможности, что бы не выстрелить себе же в ногу. На ассемблере можно написать всё, да, но то что на самом деле привнесли первые языки программирования -- новый способ мыслить о программах. Именно привнесли, а не ограничили. Например, в создании Тони Хоаром быстрой сортировки огромную роль сыграло его знакомство с Алголом, где крайне элегантно вводилась рекурсия -- он просто не думал так до этого (https://youtu.be/tAl6wzDTrJA -- около 14:40). Так же и другие парадигмы больше привносят новых способов мыслить о программе, чем ограничивают уже существующие. Подчеркну, что формулировка структурного программирования как "не используй goto" -- крайнее упрощение.
При этом я конечно понимаю основную мысль, что программировать сложно, возможностей невероятно много (вероятно даже избыточно много), и хорошо себя ограничивать и поддерживать дисциплину. Только вот например Дейкстра в своей Дисциплине Программирования имел в виду (насколько я понял) не следование некоторым ограничивающим правилам, которые приведут вас к безошибочному коду -- но напротив, он признавал принципиальную сложность программирования и предлагал дисциплину в смысле научной дисциплины, когда вы полностью осознаёте свои программы и можете доказать их корректность, а не просто показывать тестами и поддерживать различными методиками их около-работоспособность (вспомните его "Program testing can be used to show the presence of bugs, but never to show their absence!" или даже "software engineering has accepted as its charter "How to program if you cannot."").
Я не пытаюсь сказать что тестирование не важно, а ограничивать программиста не нужно, хочу просто подчеркнуть, что, по-моему, статья несколько утрирует проблему, а предлагаемые ей решения, если их воспринимать слишком серьёзно, могут привести к серьёзным же проблемам.
Мне кажется выбор лицензии дело вкуса -- принципиально тут что она свободная. Но вы видимо хотите именно копилефт. Так что, если кто-то выложит под MIT/BSD/иной пермессивной -- это искуплением не будет?
Почему именно под LGPL?
Поправьте, пожалуйста, Nokoli на Nikoli.
Я не предлагаю отказываться от трансплайтера, я предлагаю подготовить библиотеку на языке реализации, которая позволит на начальном этапе отказаться от трансплайтера, а впоследствии упростить его разработку.
Новые языки именно потому и нужны, что они позволяют проще программировать в рамках некоторой модели, позволяют думать о программе по-другому. Каждый язык поощеряет некоторые модели — так Хаскель об одном, Си о другом, Лисп о третьем, Пролог ещё об одном, Verilog и VHDL — вообще из другой оперы немного. Имея библиотеку в рамках которой вы можете проводить вычисления по своей модели уже проще создавать наиболее подходящий язык. Я описал путь возможной реализации такой библиотеки, который может упростить последующую разработку.
В любом случае удачи в этом проекте — жду следующей статьи, где, надеюсь, будет продемонстрирована работа программ такого стиля. =)
PS: а есть ли репозиторий с текущим кодом?
По второму пункту я просто не вижу, как вам это мешает опробовать вашу модель вычислений в виде библиотеки. Насколько я понимаю, вы беспокоитесь заранее о некоторых пользователях, которых думаю пока что нет. Отмечу, что если реализовать библиотеку с использованием операций для моделирования синтаксиса близкого к предложенному, то трансляция станет тривиальной (так как операции языка переводятся в операции библиотеки непосредственно), и когда библиотека будет готова, транслятор можно будет написать без больших усилий. Вновь спрошу: чем вам так принципиально создавать новый язык, притом именно сейчас, когда модель вычислений не была ещё реализована даже в прототипе (насколько я понял)?
Что касается частного решения при отсутствии алгоритмического — само это частное решение обязано быть алгоритмическим. Иначе оно невозможно к исполнению на компьютере. Конечно алгоритм этого решения может быть записан в разных стилях, будь то императивный, логический, функциональный или иной стиль.
В любом случае предложение описывать нейросети в рамках логического программирования — интересное. Вы уже проверили его работоспособность? Ваш язык уже можно протестировать? Если нет — я вновь напомню, что для проверки работоспособности модели вычисления вполне может хватить расширения существующего языка. Та модель которую вы озвучили, как минимум в том виде как я её понял, выразима в качестве библиотеки. При этом вы быстрей довели бы модель до состояния, когда её можно протестировать и не было бы костыля в виде императивной части языка. Вновь спрошу — почему вам принципиально важно создание нового языка для выражения этой модели? Обычно сначала вырабатывается модель, а затем создаётся язык заточенный под неё — и не без причины — так просто проще. =)
Заранее прошу прощения, если язык уже реализован.