Язык программирования, где каждое выражение имеет смысл

23

В соответствии с рекомендацией я публикую это из Переполнения стека .

Недавно я думал о следующей проблеме.

Рассмотрим код для стандартного "Hello world!" программа:

main()
{
    printf("Hello World");

}

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

main(5
{
    printf("Hello World");

}

Теперь к актуальному вопросу. Существует ли язык программирования, где каждая возможная комбинация символов - то есть каждое выражение - имеет смысл? Я попытался придумать какое-то решение и придумал два:

  1. Постфикс с ограниченным количеством переменных. По сути, все переменные уже определены до того, как вы напишите какой-либо код, и вам придется работать только с ними. Теоретически вы можете выполнить произвольное количество операций, сформировав цепочку из множества простых программ, каждая из которых передает результаты другим. Код можно записать в виде последовательности символов в постфиксной нотации;

  2. «Постфикс» со стеком переменных. Переменные хранятся в стеке; каждая операция берет две переменные сверху и помещает результат на их место. Программа заканчивается, когда она достигает последней операции или переменной.

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

У кого-нибудь есть другие идеи, как решить эту проблему?

user1561358
источник
48
Учитывая компилятор , создать новый компилятор , который работает следующим образом : данный источник , передать его в . Если доволен этим и создает исполняемый файл, то это так, но если жалуется, выведите исполняемый файл, который печатает . Компилятор принимает каждую строку в качестве допустимой программы. C ' S C C C C 'CCsCCCYou are a bimbo.C
Андрей Бауэр
1
BF нужны соответствующие [ ]команды (согласно вики-странице). Я думал о том, чтобы посмотреть на коды операций процессора. Но даже в этом случае некоторые шаблоны могут привести к проблеме (например, если код операции равен 3 битам, а ваша программа - только 2 бита). За исключением этой проблемы возможного заполнения дополнительными 0 битами, можно подумать о любом процессоре с полный набор кодов операций, который будет удовлетворять требованию "каждая строка является допустимой программой". Может быть бессмысленно, но все еще в силе.
Ран Г.
1
Пусть ваше оборудование представляет собой процессор Z-80 с 64 КБ ОЗУ. Напишите компилятор, который просто копирует ASCII-кодированный исходный код в память 64 КБ (усечение или заполнение нулями, если необходимо). Этот компилятор никогда не дает синтаксической ошибки.
Бен Кроуэлл
1
@RanG. Я думаю, что «компилятор», который обрабатывает любой битовый поток и исправляет его, чтобы он был действительным битом объектного кода для данного процессора, отвечал бы требованиям OP. Вероятно, это не будет ужасно сложно даже для систем со сложными наборами команд, таких как x86. Несколько лет назад я прочитал статью о достоверности случайных байтов в качестве программ для x86, и оказалось, что x86 на самом деле гораздо надежнее, чем первоначально ожидали авторы.
otakucode
2
Без дальнейших условий этот вопрос скучен: комментарий Андрея и ответ Дэвида дают «тривиальные» ответы. Вы должны более точно зафиксировать, что вы хотите.
Рафаэль

Ответы:

31

Redcode, язык ассемблера, стоящий за codewars, был явно написан так, чтобы иметь очень мало инструкций по остановке, потому что код часто искажается до того, как он наконец-то выдастся, и чем больше у него возможностей для остановки, тем менее интересна игра.

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

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

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

Корт Аммон - Восстановить Монику
источник
1
Генетика не кажется хорошим примером. С точки зрения действительного или недействительного, вы просто говорите о репликации? Потому что, конечно, каждая строка будет допустимой программой для языка, на котором есть единственная возможная инструкция replicate this string. Это не очень важный язык программирования, так как он совсем не близок к Turing Complete.
тел.
2
@tel: Корт, вероятно, говорит о синтезе белка с помощью мРНК, а не репликации. Практически любую генетическую последовательность можно транскрибировать и затем поместить в механизм синтеза белка: достаточно ли стабилен выходящий белок, чтобы он не разлагался к моменту его создания, и если да, то делает ли он что-нибудь полезное для организм, это другое дело ...
Стив Джессоп
3
Генетический код не является кодом для воспроизведения самого себя. Это (вообще) код для белка. Полезен ли белок - это часто другой вопрос. Конечно, становится интереснее. Некоторые фрагменты «кода» в генетической последовательности в конечном итоге становятся больше похожими на инструкции, «которые кодируют несколько строк ниже - иногда вы должны просто игнорировать это». Там всякие классные "программы", написанные клетками и вирусами, сражающиеся друг с другом.
Джоэл
TECO - это еще один пример из реальной жизни.
CJM
1
@ cjm вау. «API заканчивается не после того, как вы все добавили, а когда вы все удалили». Если вы не TECO, то вы закончили, когда у вас закончились символы для назначения значения.
Cort Ammon - Восстановить Монику
16

Идея универсальной машины Тьюринга использует именно такой «язык программирования»: кодирование машин Тьюринга в виде натуральных чисел, представленных, например, в двоичном виде, так что каждое натуральное число обозначает машину Тьюринга, то есть программу. На этом языке каждая строка нулей и единиц является программой.

nn

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

Дэвид Ричерби
источник
13

Расширение языка программирования так, чтобы каждое выражение имело смысл, всегда возможно, но не интересно. Например, вы можете просто присвоить значение «ничего не делать» любому выражению, которое отклоняет исходный язык.

Разработка языка программирования, в котором каждое выражение имеет смысл способом «вы можете его выполнить», не особенно полезна. Хороший язык программирования - это не просто язык, на котором обезьяна может печатать на клавиатуре и написать правильную программу, а тот, где программист может легко написать программу, которую они намеревались написать. Написание действительных программ - не сложная часть программирования: сложная часть - написание программы, которая выполняет то, что от нее ожидали. Отказ от явно некорректных программ очень полезен в этом отношении.

Другим способом решения этой проблемы является полное определение семантики всех возможных входных данных, включая указание того, какие ошибки времени компиляции, загрузки или времени выполнения должны генерироваться для каждого ввода, если таковые имеются. То есть «отмена программы после печати Syntax error at line 42в стандартном потоке ошибок» является частью определенной семантики языка. Каждое выражение «имеет смысл» в том смысле, что оно имеет определенное значение. Это полезный смысл? Возможно - в конце концов, если программа явно неправильна, отклонение это полезно.

Жиль "ТАК - перестань быть злым"
источник
12

Проверьте Jot , полный по Тьюрингу язык, основанный на комбинаторной логике, где каждая последовательность из 0 и 1 (включая пустую последовательность) является допустимой программой.

Петр Пудлак
источник
2
Это не ответ по информатике .
Рафаэль
2
@Abdulrhman Нетрудно определить биекцию между двоичными строками и натуральными числами. Таким образом, вы можете закодировать любую программу как натуральное число, если хотите.
CodesInChaos
7
@ Рафаэль Пожалуйста, уточните или предложите улучшить ответ, я буду рад его улучшить, если вы предоставите основания для своей критики.
Петр Пудлак
+1, я собирался дать аналогичный ответ для вымышленного языка программирования, основанного на натуральных числах, но это похоже. AFAIK нет программирования (в практическом использовании), у которого есть эта особенность, но можно построить его, используя только цифры, скажем, где каждая комбинация имеет значение (действует как операторы, а также операнды). Это ключ
Никос М.
8

Один хороший пример - пробелы . В собственном языке допустимы любые комбинации операторов. Операторы: пробел, табуляция и новая строка (в частности, "\ n"). Все остальные персонажи считаются комментариями .

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

slebetman
источник
Я просто думал об этом после публикации своего ответа на ваш мозг (ваш лучше, поскольку он правильный), но мне интересно - пустая программа все еще является программой? (т.е. если эти три символа отсутствуют во всем файловом потоке). - Например, если бы в моей машине не было всего того, что делало ее машиной, это все равно была бы машина?
BrainSlugs83
Это не ответ по информатике . (Кроме того, «каждая строка пробела»! = «Каждая строка».)
Рафаэль
2
@Raphael: Но все возможные строки (включая те, которые не содержат пробелов) являются допустимыми программами для пробелов - обратите внимание, что любой символ, который не является пробелом, является просто комментариями на языке программирования пробелов
slebetman
2
@slebetman Вы слишком буквально интерпретировали мой комментарий в скобках. Я говорил о непарных жетонах петель. Некоторые подобные проблемы в пробелах могут быть: Работает ли возврат без предшествующего вызова? (закодировано как [LF][Tab][LF]) Что произойдет, если вы вставите пустой стек? Что произойдет, если вы перейдете на неопределенный ярлык? Что произойдет, если вы определите дубликаты ярлыков?
CodesInChaos
7

Я хотел бы затронуть идею многих авторов, что такой язык будет «бесполезным». Возможно, для людей было бы бесполезно писать вручную с намерением решить какую-то конкретную задачу. Однако, несмотря на то, что он используется в большинстве случаев для языков программирования, это, конечно, не единственный вариант использования. Несколько примеров использования приходят на ум, где такой язык полезен, и мы можем посмотреть на эти поля примеры таких языков.

Во - первых , аллюзия Cort Аммона к генетике пятно на: преобразования программы в вопросе (заменив )на 5) можно увидеть , как мутации . Этот вид манипуляции распространен в области эволюционных вычислений ; в частности, генетические алгоритмы выполняют такие преобразования над строками , в то время как генетическое программирование преобразует программы . В любом случае мы обычно хотим присвоить значение каждой возможности, так как это даст наиболее компактное пространство поиска.

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

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

  • Если есть какая-то структура в языке, например. мы можем присвоить значение подстрокам, это может быть потеряно при переводе в натуральные числа. В этом случае мы можем предпочесть использовать строки, чтобы рассуждать и преобразовывать подстроки локально, а не представлять целую программу как число. Это аналогично тому, как мы можем предпочесть использовать побитовые операции над целыми числами, а не арифметическими выражениями, когда каждый бит имеет индивидуальное значение. Это в основном обобщение эволюционного сценария.
  • Мы можем создавать программы по запросу; например, мы могли бы начать выполнение программы, которая полностью не определена, и генерировать (например, случайным образом) отдельные инструкции (например, символы), когда / если указатель инструкции достигает их. Это распространено в алгоритмической теории информации, где программа представляет собой ленту Тьюринга, и ее цель - охарактеризовать поведение случайно сгенерированных программ. Например, мы можем сформулировать Соломонова перед произвольными строками как вероятность того, что универсальная машина Тьюринга со случайной лентой выведет эту строку.

Что касается примеров языков, многие эволюционные вычислительные системы основаны на языках стека, таких как семейство Push . Они, как правило, допускают произвольные потоки токенов (которые мы могли бы представлять как отдельные символы). Иногда (как в примере с Brainfuck в BrainSlugs83) существуют ограничения на балансировку скобок; Однако, мы можем связать это с саморазграничны программами , в том , что строка , как [не может быть действительной программой , но она является действительным префиксом программы . Если мы представим, что компилятор / интерпретатор читает исходный код из stdin, то он не будет отклонять строку наподобие этого [, он просто будет ждать большего ввода, прежде чем продолжить.

Такие языки, как двоичная комбинаторная логика и двоичное лямбда-исчисление, возникли непосредственно из работы над алгоритмической теорией информации, например. от http://tromp.github.io/cl/cl.html

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

Warbo
источник
2

Настоящие языки программирования должны передавать значение людям , а не компьютерам. Поскольку множество забавных текстов с почти случайно перемешанными буквами, плавающими вокруг «Нэт-шоу», люди могут читать тарабарщину и придавать ей смысл, даже не замечая явного искажения. Вспомните, как трудно найти опечатки и другие подобные ошибки в текстах.

Язык программирования, подобный тому, что вы просите, заставит людей понять, что они хотят читать, а не то, что записано. Отладка в языках, где существует ограниченный набор юридических утверждений, где возможна небольшая двусмысленность, уже достаточно сложна. Хорошие языки уменьшают возможные интерпретации, например, из-за транспонированных символов или опечаток. Естественные языки также известны своей избыточностью по той же причине.

vonbrand
источник
0

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

( Правка. Оказывается, вы должны сопоставлять открывающие и закрывающие квадратные скобки, но в остальном вышеприведенное верно.)

Это достигается с помощью этих двух простых методов:

  1. Все команды, которые он понимает, представляют собой один байт (в BF все они, например, одиночные символы ASCII *).

  2. Все символы, которые он не понимает, он отбрасывает как комментарии.

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

*: Оказывается, что не все команды BF представляют собой один байт ASCII, т. Е. Квадратные скобки ДОЛЖНЫ совпадать, так что язык там не соответствует первым критериям. - Но любой язык, который соответствует обоим критериям, будет соответствовать тому, о чем просит ФП.

BrainSlugs83
источник
2
Это не только не отвечает на вопрос, это не ответ информатики .
Рафаэль
1
Вы можете переопределить язык, чтобы разобраться с ними каким-то разумным способом. например, вставив достаточное количество открывающих скобок в начале программы и закрывающих скобок в конце программы, чтобы сбалансировать ее. Нетрудно написать интерпретатор, который обрабатывает программы так, как если бы эти скобки существовали, фактически не переписывая программу. Конечно, запуск программы «мозгового пота» с открывающей скобкой довольно бесполезен, поскольку она будет игнорировать все, вплоть до соответствующей закрывающей скобки.
CodesInChaos
1
Вопрос Рафаэля ОП: «Есть ли язык программирования, в котором каждая возможная комбинация символов, то есть каждое выражение, имеет смысл?» - мой ответ: «Да, вот пример того, что близко, и вот теория, стоящая за этим». - кроме того, что я изложил точные правила для одного класса языков, которые отвечали бы требованиям ОП, я не уверен, насколько здесь больше места для науки . Можете ли вы привести пример или ссылку на ресурс, что именно вы надеетесь увидеть здесь? -- Спасибо.
BrainSlugs83
2
Дэвид и Жиль дают ответы по информатике. Они исследуют принципы и не просто говорят: «Язык X делает это (почти)». Если вы прочитаете их ответы, то узнаете, что ответы в последней форме тоже довольно скучные. Это не ваша вина, но ОП - вопрос (как вопрос информатики) скучен; есть ложное чувство сложности.
Рафаэль
Можно легко «исправить» BF так, чтобы была принята любая строка: вы просто притворяетесь, что ]в конце исходного кода достаточно символов, чтобы соответствовать всем непревзойденным [s, и [в начале достаточно, чтобы соответствовать всем непревзойденным ]. Семантика [и ]может быть легко изменена, чтобы сделать их эквивалентными этому. (например , если нет соответствия , ]то [просто останавливает выполнение , если байты по указателю данных равны нуль. ]просто переходит к началу программы в аналогичной ситуации.) В результате языка будет Тьюринг и будет принимать любую строку.
Натаниэль