Інтроспективне програмування: Код, який аналізує його джерело та вихід


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.

(Вихід повинен перейти до stdout.)

Зауважте, наприклад, що висновок містить два великих літери m. Один за Myі один для 2 are "M". Це має бути справедливим для всіх символів, щоб вихід ні в якому разі не суперечив собі.

Невизначені числа ігноруються у висновку, щоб уникнути незадовільних наборів частот. Наприклад, 1 is "1"неправильно, якщо зараховуються обидва 1. Він повинен читати 2 are "1", але тоді знову є лише 1.

Роз'яснення форматування

  • "є" необхідно використовувати для одиночних подій символів.

  • "are" повинні використовуватися для кількох подій символів.

  • "є" ніколи не повинна відображатися у списку вихідних символів, оскільки це було б зайвим. 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 Regexps дозволяє деякі помилкові позитиви, так. Щоб вирішити проблему з косою рисою всередині потрійних лапок, використовуйте необроблені рядки ( 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.