Страницы

Поиск по вопросам

воскресенье, 30 сентября 2018 г.

Перевести программу с Паскаля на [любой-язык]

Конкурс окончен, смотрите результаты в конце вопроса.

Я думаю, многие из вас видели вопросы, состоящие из просьбы перевода программы с одного языка на другой. Давайте-ка покажем, как делать такие вещи правильно! В нашем конкурсе мы исходим из такой простой программы на Паскале:
program test; var a, b, c: integer; begin readln(a); readln(b); c := a + b; writeln(c); end.
(Она же на ideone.)
Задание состоит в следующем: вы должны перевести программу на любой-язык так, чтобы сохранить как можно больше от исходного текста программы. В качестве целевого языка, понятно, исключаются языки группы Паскаля (Delphi, Algol, Oberon, Modula, etc., все языки, в которых используется begin/end для группировки команд в составную команду).
Вы можете дописывать конструкции до и после данного в условии текста, но не внутри его (точнее, можете и внутри, но это будет считаться изменением — смотрите ниже условия подсчёта). Сам текст желательно менять как можно меньше.
Ограничения: Строки исходной программы между begin и end должны сохранять свой смысл. Они должны выполняться, и при их выполнении должно происходить в общих чертах то же, что и в исходной программе: readln должно считывать значения с консоли, c := a + b должно складывать значения двух переменных (или что там есть в вашем языке) в третью, writeln должно выводить правильный результат на консоль. (Это означает, что вы не можете просто закомментировать код первоначальной программы.)
Определение победителя: Выигрывает код, в котором исходный текст менее всего изменён по сравнению с полным первоначальным вариантом (количество добавленных символов + количество удалённых символов + количество изменённых символов), считая и строки с program и end.. Разница в больших/маленьких буквах, а также замена символа на одинаковый по начертанию (например, русское «с»/английское «c») считается за пол-символа. Если два решения имеют одинаковое количество отличий (например, ноль), выигрывает то, у которого меньше добавленного кода (в символах). Если несколько решений одинаковы и по этому критерию, выигрывает то решение, которое получит больше голосов (как обычно, «за» минус «против»).
В частности, полное совпадение кода выигрывает у неполного независимо от количества подготовительного кода.
Для того, чтобы было легче проверять ваш код, старайтесь публиковать ссылку на онлайн-компилятор с вашим кодом. Код должен компилироваться без ошибок (пусть даже с предупреждениями) и правильно работать в диапазоне входных чисел от 0 до 1000.
Продолжительность конкурса — 1 неделя.

Для исключения разночтений, при неясности в правилах пожалуйста переспрашивайте в комментариях или в чате, посвящённом code golf

Для примера, вот внеконкурсное решение на plain TeX:

ewcount\tmp
ewcount\c
\def\uncatcodeletters{\uncatcoderange{`a}{`z}\uncatcoderange{`A}{`Z}} \def\uncatcoderange#1#2{% \tmp=#1 \advance\tmp -1 \loop\advance\tmp 1 \expandafter\catcode
umber\tmp=11 \ifnum\tmp<#2
epeat}
\def\s{\begingroup\uncatcodeletters\shlp} {\def~ := #1 + #2;{% \global\expandafter\c\csname #1\endcsname \global\expandafter\advance\expandafter\c\csname #2\endcsname \endgroup} \global\let\shlp=~}
\def\inp{\begingroup\uncatcodeletters\inphelper} \def\inphelper eadln(#1);{\endlinechar=-1 \escapechar=-1 \expandafter\inphelperi\csname #1\endcsname\endgroup} \def\inphelperi{\global
ead16 to }
\def\out{\begingroup\uncatcodeletters\outhelper} \def\outhelper riteln(#1);{\message{\expandafter\the\csname #1\endcsname}\endgroup}
\let\DEF\def \let\END\end \catcode`r=13 \let r=\inp \catcode`w=13 \let w=\out \catcode`p=14 \catcode`v=14 \catcode`b=14 \let~=\catcode ~`c=13 \let c=\s ~`e=13 \DEF end.{\END}
program test; var a, b, c: integer; begin readln(a); readln(b); c := a + b; writeln(c); end.
(Если кому интересно, гольфированный вариант.) Транскрипт компиляции:
~>tex golf.tex This is TeX, Version 3.14159265 (MiKTeX 2.9 64-bit) (golf.tex a=5 % <-- 5 введено с консоли
b=8 % <-- 8 введено с консоли 13 ) No pages of output. Transcript written on golf.log.

Просьба к отвечающим писать в начале решения ваш язык, количество изменённых символов и количество символов в подготовительном коде. Также просьба давать не более одного решения на ответ.

Обновление: Конкурс окончен, вот результаты.
Побеждает ответ @Mike, сумевший уложиться в 78 подготовительных символов, и не поменять ни символа в исходном коде.
Другой ответ того же автора выглядит чрезвычайно изящно (подключение паскалевского синтаксиса как внешний модуль, хей!), и почти выиграл приз зрительских симпатий, но проигрывает по количеству символов вследствие своей большей общности. Оба решения пользуются особенностью языка Perl, который в своих модулях позволяет предобработку текста на Perl самим Perl'ом. Мощный язык, мощные средства управления синтаксисом, заслуженная победа.
Второй в списке победителей — ответ @Qwertiy. Это решение продолжает идею «получить текст как строку, обработать, чтобы получился код на нужном языке, и выполнить над ней eval», с симпатичной, очень техничной и компактной реализацией (регулярки!).
Приз зрительских симпатий получает неожиданный ответ @kmv. В этом решении текст исходной программы не объявляется строкой, а «вытягивается» из кода функции! (Это, формально говоря, решение не по стандарту, но фактически в распространённых браузерах toString() работает именно так.)
Третье место получает решение @pavel с комбинацией Unix shell/C, которое обходится без eval за счёт замены строк до компиляции и использования препроцессора C. Такой подход позволяет справиться с двоеточиями, которые вызывают затруднения для препроцессора у чистых решений на C/C++.
Вообще, идея со строкой и eval оказалась наиболее популярной, её реализуют также ответы @edem Perl, построчная замена, практически интерпретация, @Red Skotina (замена строк на Питоне, построчная адаптация текста, оставаясь в рамках правил, хотя и на грани), @gil9red (то же, но более универсально, Питон), @nuts119 на C# (да, в C# можно сделать eval, вы не знали?) и @Streletz на Java (интерпретатор из сторонней библиотеки).
Тему интерпретации продолжает ещё одно решение @nuts119 на C# с использованием DataTable как arithmetic engine. Это решение, при всей его сложности, имеет дальний прицел на построение полноценного интерпретатора.
Оставшиеся решения на чистом C/C++ и Javascript/Typescript без eval вынуждены модифицировать исходный текст, хотя они смогли обойтись минимальным количеством изменений. Из этих решений наилучшие с одним удалённым символом решения @kmv (C, препроцессор, использование битовых полей) и @Qwertiy (C++, тонкости препроцессора). Интересно, что эти оба решения убирают из исходного текста соседние символы: из := убрано в первом случае двоеточие, а во втором — знак = (!).
Оставшиеся четыре решения (@Qwertiy, typescript, @pavel, C++, препроцессор (заработало больше голосов, чем победитель), снова @Qwertiy, javascript и @Grundy, C, препроцессор) меняют больше символов в исходном коде, но также интересны и стоят вашего внимания.
Большое спасибо всем, кто принимал участие в конкурсе!

Таблица лидеров: (спасибо @Grundy за адаптацию скрипта и @jfs за идею)
function getAnswers(questionId, answer_filter, page) { return jQuery.ajax({ url: '//api.stackexchange.com/2.2/questions/' + questionId + '/answers?page=' + page + '&pagesize=100&order=desc&sort=activity&site=ru.stackoverflow&filter=' + answer_filter, method: "get", dataType: "jsonp", crossDomain: true }).then(function(data) { if (data.has_more) { return getAnswers(questionId, answer_filter, page + 1).then(function(d) { return data.items.concat(d.items); }) } return data.items; }); } function getAuthorName(e) { return e.owner.display_name } function process(items) { return items.map(function(item) { var matched = item.body.match(/\s*(.+?)\s*?,.*?(\d+),.*?(\d+)\s*?(?:[.;,(].*)?<\/h/); if (matched) { return { lang: matched[1], setup: +matched[2], changes: +matched[3], link: item.share_link, author: getAuthorName(item) }; } else { return { lang: "N/A", setup: "N/A", changes: "N/A", link: item.share_link, author: getAuthorName(item) } } }); } function sort(items) { return items.sort(function(a, b) { if (a.lang == "N/A") return 1; if (a.changes != b.changes) return a.changes - b.changes; return a.setup - b.setup; }) } function fillTemplate(sortedItems) { $('#leadership').append(sortedItems.map(function(item, index) { return $('').append($('').html(index + 1)) .append($('').html(item.author)) .append($('').html(item.lang)) .append($('').html(item.setup)) .append($('').html(item.changes)) .append($('').append($('').attr('href', item.link).text('Link'))); })); return sortedItems; } var QUESTION_ID = 526265, ANSWER_FILTER = "!4*SyY(4Kifo3Mz*lT", startPage = 1; getAnswers(QUESTION_ID, ANSWER_FILTER, startPage) .then(process) .then(sort) .then(fillTemplate); #leadership { border-collapse: collapse; } #leadership td, #leadership th { padding: 5px; } #leadership th { text-align: center; }

Таблица лидеров

Автор Язык Подготовка Изменено


Ответ

Раз в конкурсе участвует размер, вот еще вариант (лично мне нравится меньше, потому как большая ориентировка именно на этот код):
perl, подготовка 97, измененных 0
perl, подготовка 78, измененных 0
sub r{$_[0]=<>} sub wr{print@_} $_=q.
program test; var a, b, c: integer; begin readln(a); readln(b); c := a + b; writeln(c); end.
;s/\b(\w)\b/\$$1/g ;s/^\S.*|.{4}n|://gm ;eval
Тест на ideone
В коде происходит:
Создаем функции r/wr аналогичные по действию паскалевским read/write ln. $_=q. присваивает "переменной по умолчанию" весь текст до следующего знака . Все последующие строки работают с этой "переменной по умолчанию", так как явных переменных в них не указано. Перед переменными (однобуквенными словами) ставим знак $ Удаляем строки program, var, begin, end, части названий функций (readln=r, writeln=wr), двоеточия Выполняем полученный текст:
r($a); r($b); $c = $a + $b; wr($c);

Комментариев нет:

Отправить комментарий