Интроспективное программирование: код, который анализирует его источник и вывод


13

Напишите программу, которая выводит общее количество символов и частоту каждого символа в его источнике и выводе. Вы должны следовать формату, показанному в примере.

пример

Если ваш код был

abb1

Его выход должен быть

My source has 4 characters.
1 is "a"
2 are "b"
1 is "1"
Besides unquoted numbers, my output has 383 characters.
34 are "
"
79 are " "
63 are """
2 are "'"
2 are ","
4 are "."
2 are "1"
2 are "B"
2 are "I"
2 are "M"
39 are "a"
4 are "b"
6 are "c"
4 are "d"
38 are "e"
3 are "g"
5 are "h"
4 are "i"
4 are "m"
3 are "n"
8 are "o"
3 are "p"
2 are "q"
38 are "r"
12 are "s"
8 are "t"
7 are "u"
3 are "y"
It's good to be a program.

(Вывод должен идти в стандартный вывод.)

Обратите внимание, например, что вывод содержит два заглавных m. Один за Myи один за 2 are "M". Это должно выполняться для всех символов, чтобы вывод не противоречил самому себе.

Номера без кавычек игнорируются на выходе, чтобы избежать неудовлетворительных наборов частот. Например,1 is "1" неверно, если подсчитаны обе единицы. Это должно прочитать 2 are "1", но тогда есть только один 1 снова.

Формат разъяснений

  • «is» должен использоваться для вхождений одного символа.

  • «are» должен использоваться для нескольких вхождений символов.

  • «is» никогда не должно появляться в списке выходных символов, потому что это было бы излишним. 1 is 'Z'относится к самому Z, поэтому вся строка может быть удалена.

  • Три фразы в полном предложении должны появляться в том порядке, в котором между ними расположены списки частот символов (как показано в примере). Таким образом, ваш вывод будет начинаться с My source...и заканчивается...be a program. . Обратите внимание, что в конце вывода нет новой строки.

  • Сами списки частот символов могут быть в любом порядке.

  • Новые строки считаются одним символом (если они \ r \ n).

Проверка формата

Следующий скрипт Python принимает ваш код и его вывод в виде строк и утверждает, что вывод не имеет противоречий. Он предоставляет полезное сообщение об ошибке, если что-то не так. Вы можете запустить его в Интернете по адресу http://ideone.com/6H0ldu , разветвив его, заменив строки CODE и OUTPUT, а затем запустив его. Он никогда не даст ложных срабатываний или негативов (при условии отсутствия ошибок).

#Change the CODE and OUTPUT strings to test your program

CODE = r'''abb1'''

OUTPUT = r'''My source has 4 characters.
1 is "a"
2 are "b"
1 is "1"
Besides unquoted numbers, my output has 383 characters.
34 are "
"
79 are " "
63 are """
2 are "'"
2 are ","
4 are "."
2 are "1"
2 are "B"
2 are "I"
2 are "M"
39 are "a"
4 are "b"
6 are "c"
4 are "d"
38 are "e"
3 are "g"
5 are "h"
4 are "i"
4 are "m"
3 are "n"
8 are "o"
3 are "p"
2 are "q"
38 are "r"
12 are "s"
8 are "t"
7 are "u"
3 are "y"
It's good to be a program.'''

#######################################################

import re

amountPattern = r'(\d+) (is|are) "(.)"\n'

class IntrospectionException(Exception):
    pass

def getClaimedAmounts(string, errorOnIs):
    groups = re.findall(amountPattern, string, re.DOTALL)

    for amount, verb, char in groups:
        if verb == 'is':
            if errorOnIs:
                raise IntrospectionException('\'1 is "%s"\' is unnecessary' % char)
            elif amount != '1':
                raise IntrospectionException('At "%s", %s must use "are"' % (char, amount))
        elif verb == 'are' and amount == '1':
            raise IntrospectionException('At "%s", 1 must use "is"' % char)

    amounts = {}
    for amount, verb, char in groups:
        if char in amounts:
            raise IntrospectionException('Duplicate "%s" found' % char)
        amounts[char] = int(amount)
    return amounts

def getActualAmounts(string):
    amounts = {}
    for char in string:
        if char in amounts:
            amounts[char] += 1
        else:
            amounts[char] = 1
    return amounts

def compareAmounts(claimed, actual):
    for char in actual:
        if char not in claimed:
            raise IntrospectionException('The amounts list is missing "%s"' % char)
    for char in actual: #loop separately so missing character errors are all found first
        if claimed[char] != actual[char]:
            raise IntrospectionException('The amount of "%s" characters is %d, not %d' % (char, actual[char], claimed[char]))
    if claimed != actual:
        raise IntrospectionException('The amounts are somehow incorrect')

def isCorrect(code, output):
    p1 = r'^My source has (\d+) characters\.\n'
    p2 = r'Besides unquoted numbers, my output has (\d+) characters\.\n'
    p3 = r"It's good to be a program\.$"
    p4 = '%s(%s)*%s(%s)*%s' % (p1, amountPattern, p2, amountPattern, p3)

    for p in [p1, p2, p3, p4]:
        if re.search(p, output, re.DOTALL) == None:
            raise IntrospectionException('Did not match the regex "%s"' % p)

    claimedCodeSize = int(re.search(p1, output).groups()[0])
    actualCodeSize = len(code)
    if claimedCodeSize != actualCodeSize:
        raise IntrospectionException('The code length is %d, not %d' % (actualCodeSize, claimedCodeSize))

    filteredOutput = re.sub(r'([^"])\d+([^"])', r'\1\2', output)

    claimedOutputSize = int(re.search(p2, output).groups()[0])
    actualOutputSize = len(filteredOutput)
    if claimedOutputSize != actualOutputSize:
        raise IntrospectionException('The output length (excluding unquoted numbers) is %d, not %d' % (actualOutputSize, claimedOutputSize))

    splitIndex = re.search(p2, output).start()

    claimedCodeAmounts = getClaimedAmounts(output[:splitIndex], False)
    actualCodeAmounts = getActualAmounts(code)
    compareAmounts(claimedCodeAmounts, actualCodeAmounts)

    claimedOutputAmounts = getClaimedAmounts(output[splitIndex:], True)
    actualOutputAmounts = getActualAmounts(filteredOutput)
    compareAmounts(claimedOutputAmounts, actualOutputAmounts)

def checkCorrectness():
    try:
        isCorrect(CODE, OUTPUT)
        print 'Everything is correct!'
    except IntrospectionException as e:
        print 'Failed: %s.' % e

checkCorrectness()

счет

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


Разрешено ли чтение собственного исходного файла?
Вентеро

@MrLore Могут быть и другие ошибки, но я только что понял, что тройные кавычки ('' ') по-прежнему избегают обратной косой черты. Это может быть связано с вашей проблемой. Я исправляю это сейчас.
Увлечения Кэлвина

@ Ventero Определенно!
Увлечения Кэлвина

@MrLore Регулярные выражения допускают некоторые ложные срабатывания, да. Чтобы исправить проблему с обратными слешами внутри тройных кавычек, используйте raw-строки ( r'''CODE''').
Вентеро

1
@MrLore Исправлены неэкранированные точки. Спасибо, что заметили!
Увлечения Кельвина,

Ответы:


2

CJam - 189

{`"_~"+:T;"Besides unquoted numbers, my output has &It's good to be a program.&My source has & characters.
"'&/~_]:X2=T,X3=3i({T_&:B{TI/,(" are ":AM`I*N}fIXK=]o
XBA`N+f+2*+s:T,X3=}fK'q];}_~

Попробуйте это на http://cjam.aditsu.net/

Выход:

My source has 189 characters.
3 are "{"
3 are "`"
6 are """
4 are "_"
3 are "~"
4 are "+"
5 are ":"
5 are "T"
2 are ";"
3 are "B"
8 are "e"
9 are "s"
2 are "i"
3 are "d"
17 are " "
6 are "u"
2 are "n"
2 are "q"
8 are "o"
6 are "t"
3 are "m"
2 are "b"
7 are "r"
4 are ","
2 are "y"
2 are "p"
3 are "h"
7 are "a"
5 are "&"
4 are "I"
3 are "'"
2 are "g"
2 are "."
2 are "M"
3 are "c"
2 are "
"
2 are "/"
3 are "]"
5 are "X"
2 are "2"
4 are "="
3 are "3"
2 are "("
2 are "A"
2 are "*"
2 are "N"
3 are "}"
3 are "f"
2 are "K"
Besides unquoted numbers, my output has 988 characters.
3 are "B"
108 are "e"
11 are "s"
3 are "i"
5 are "d"
214 are " "
8 are "u"
4 are "n"
3 are "q"
9 are "o"
9 are "t"
5 are "m"
4 are "b"
108 are "r"
3 are ","
4 are "y"
4 are "p"
6 are "h"
108 are "a"
3 are "I"
3 are "'"
4 are "g"
5 are "."
3 are "M"
7 are "c"
102 are "
"
2 are "{"
198 are """
2 are "`"
2 are "_"
2 are "~"
2 are "+"
2 are ":"
2 are "T"
2 are ";"
2 are "&"
2 are "/"
2 are "]"
2 are "X"
2 are "2"
2 are "="
2 are "3"
2 are "("
2 are "A"
2 are "*"
2 are "N"
2 are "}"
2 are "f"
2 are "K"
It's good to be a program.

11

Рубин, 269 (311, 367) символов

У меня есть три разных решения этой проблемы. Каждый из них использует свой набор трюков:

«Правильное» решение, 367 символов:

Самое длинное решение - более или менее всего лишь доказательство концепции того, что эту задачу можно решить без каких-либо хитростей, - и почти не в полной мере. Это настоящая квинна (т.е. она генерирует свой собственный исходный код вместо чтения его из файла) и фактически вычисляет все числа, которые она печатает (длина кода, длина вывода, вхождения символов). Из-за того, как работает quine, весь код должен быть в одной строке и внутри строкового литерала.

eval r="S='eval r=%p'%r;O=-~$.;q=\"My source has \#{S.size}\"+(X=' characters.\n')+S.chars.uniq.map{|c|[k=S.count(c),k>O ? :are: :is,?\"+c+?\"]*' '}*$/+'\nBesides unquoted numbers, my output has ';r=(w=q+X+s=\"It's good to be a program.\").scan(D=/\\D/).uniq;$><<q<<(w+v=r.map{|c|j=' are \"\n\"';(-~(w+j*r.size).count(c)).to_s+(j[~O]=c;j)}*$/+$/).scan(D).size<<X+v+s"

Частично закодированный вывод, 311 символов:

Следующее самое короткое решение использует два трюка, но все еще является истинной формулой: - Ни один символ не встречается ровно один раз в исходном коде. Таким образом, мне не нужно решать, печатать ли мне isили areв первой половине вывода. Это также облегчает вычисление общего размера вывода (хотя на самом деле мне это не нужно). - Общий размер вывода жестко закодирован. Поскольку это зависит только от количества различных символов в исходном коде (и в общем случае, сколько из этих символов встречается только один раз), его легко рассчитать заранее.

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

#


eval R="I=$/+$/+'eval R=%p'%R;?\\4>w='%d are \"%s\"';B=\"My source has \#{I.size}\#{X=\" characters.\n\"}\#{z=(m=I.chars.uniq).map{|x|w%[I.count(x),x]}*$/}\nBesides unquoted numbers, my output has 1114\"+X;$><<B+m.map{|c|w%[(B+z+$M=\"\nIt's good to be a program.\").gsub!(/\\d++(?!\")/,'').count(c),c]}*$/+$M"

Кратчайшее решение, 269 символов:

Самое короткое решение дополнительно жестко кодирует собственную длину источника. Используя имена переменных, которые уже не являются частью исходного кода, можно найти «точку фиксации», в которой все символы в исходном коде (включая цифры из жестко закодированных длин!) Встречаются как минимум дважды.

Это решение также экономит еще несколько символов, просто читая собственный исходный код из файла кода, а не генерируя его. Как приятный побочный эффект, это делает код намного более «читабельным» (но кому небезразличен читаемый код в …), поскольку теперь код больше не должен находиться внутри строкового литерала.

U='%d are "%s"'
O=IO.read$0
?\126>B="My source has 269#{X=" characters.
"}#{z=(m=O.chars.uniq).map{|c|U%[O.count(c),c]}*$/}
Besides unquoted numbers, my output has 1096"+X
$><<B+m.map{|c|U%[(B+z+$M="
It's good to be a program.").gsub!(/\d++(?!")/,"").count(c),c]}*$/+$M

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

import subprocess

CODE = open("packed.rb").read()
OUTPUT = subprocess.check_output(["ruby", "packed.rb"])

print CODE
print len(CODE)

теперь скрипт автоматически запускает мой код, читает его вывод и извлекает исходный код из файла кода.


Вот вывод, сгенерированный самым коротким кодом:

My source has 269 characters.
3 are "U"
7 are "="
3 are "'"
4 are "%"
6 are "d"
17 are " "
11 are "a"
9 are "r"
9 are "e"
11 are """
11 are "s"
6 are "
"
4 are "O"
2 are "I"
10 are "."
6 are "$"
2 are "0"
2 are "?"
2 are "\"
2 are "1"
2 are "2"
3 are "6"
2 are ">"
4 are "B"
3 are "M"
2 are "y"
9 are "o"
10 are "u"
12 are "c"
4 are "h"
2 are "9"
2 are "#"
4 are "{"
2 are "X"
8 are "t"
4 are "}"
2 are "z"
6 are "("
7 are "m"
5 are "n"
2 are "i"
2 are "q"
6 are ")"
4 are "p"
4 are "|"
2 are "["
4 are ","
2 are "]"
2 are "*"
4 are "/"
3 are "b"
7 are "+"
2 are "<"
3 are "g"
2 are "!"
Besides unquoted numbers, my output has 1096 characters.
2 are "U"
2 are "="
3 are "'"
2 are "%"
5 are "d"
238 are " "
120 are "a"
120 are "r"
120 are "e"
222 are """
11 are "s"
114 are "
"
2 are "O"
3 are "I"
5 are "."
2 are "$"
2 are "0"
2 are "?"
2 are "\"
2 are "1"
2 are "2"
2 are "6"
2 are ">"
3 are "B"
3 are "M"
4 are "y"
9 are "o"
8 are "u"
7 are "c"
6 are "h"
2 are "9"
2 are "#"
2 are "{"
2 are "X"
9 are "t"
2 are "}"
2 are "z"
2 are "("
5 are "m"
4 are "n"
3 are "i"
3 are "q"
2 are ")"
4 are "p"
2 are "|"
2 are "["
3 are ","
2 are "]"
2 are "*"
2 are "/"
4 are "b"
2 are "+"
2 are "<"
4 are "g"
2 are "!"
It's good to be a program.

Не могли бы вы опубликовать точную копию вашего кода и вывод, чтобы я мог легко проверить его? Код не должен выводиться сам, а вывод должен заканчиваться точкой, а не переводом строки.
Увлечения Кэлвина

@ Calvin'sHobbies Первый блок кода - это мой настоящий код. Тем не менее, он выводит вывод с последней строкой, так что дайте мне несколько минут, чтобы это исправить (это то, что вы обязательно должны упомянуть в спецификации).
Вентеро

Конечно, я только что обновил спецификацию.
Увлечения Кэлвина

@ Calvin'sHobbies Готово. Первый кодовый блок - это фактический код, который генерируется вторым кодовым блоком (так что мне не нужно заботиться о экранировании строк и всем остальном при написании кода).
Вентеро
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.