Pemrograman Introspektif: Kode yang menganalisis sumber dan outputnya

13

Tulis program yang menampilkan jumlah total karakter dan frekuensi setiap karakter dalam sumbernya dan hasilnya. Anda harus mengikuti format yang diilustrasikan dalam contoh ini.

Contoh

Jika kode Anda

abb1

Hasilnya harus

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.

(Output harus menuju stdout.)

Perhatikan, misalnya, bahwa output mengandung dua huruf kapital m. Satu untuk Mydan satu untuk 2 are "M". Ini harus berlaku untuk semua karakter sehingga output tidak bertentangan dengan cara apa pun.

Angka yang tidak dikutip diabaikan dalam output untuk menghindari set frekuensi yang tidak memuaskan. Misalnya, 1 is "1"salah jika keduanya dihitung. Seharusnya membaca 2 are "1", tetapi kemudian hanya ada satu 1 lagi.

Klarifikasi Format

  • "is" harus digunakan untuk kemunculan karakter tunggal.

  • "are" harus digunakan untuk kemunculan beberapa karakter.

  • "is" seharusnya tidak pernah muncul dalam daftar karakter keluaran karena itu akan berlebihan. 1 is 'Z'mengacu pada Z itu sendiri, sehingga seluruh baris dapat dihapus.

  • Tiga frasa kalimat lengkap harus muncul sesuai dengan daftar frekuensi karakter di antaranya (seperti yang ditunjukkan contoh). Jadi output Anda akan mulai dengan My source...dan diakhiri dengan ...be a program.. Perhatikan bahwa tidak ada baris baru di akhir output.

  • Daftar frekuensi karakter itu sendiri mungkin berurutan.

  • Baris baru dihitung sebagai satu karakter (seandainya itu adalah \ r \ n).

Pemeriksa format

Skrip Python berikut mengambil kode Anda dan hasilnya sebagai string dan menyatakan bahwa output tidak memiliki kontradiksi. Ini memberikan pesan kesalahan yang berguna jika ada sesuatu yang salah. Anda dapat menjalankannya secara online di http://ideone.com/6H0ldu dengan memalsunya, mengganti string KODE dan OUTPUT, kemudian menjalankannya. Itu tidak akan pernah memberikan positif atau negatif palsu (dengan asumsi kesalahan bebas).

#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()

Mencetak gol

Ini adalah kode-golf. Kiriman dengan karakter paling sedikit menang. Pengajuan harus melewati pemeriksa format agar valid. Celah standar berlaku, meskipun Anda dapat membaca kode sumber Anda sendiri dan / atau membuat hardcode keluaran Anda .

Hobi Calvin
sumber
Apakah membaca file sumber Anda sendiri diperbolehkan?
Ventero
@ MrLore Mungkin ada kesalahan lain tapi saya baru menyadari bahwa tanda kutip tiga ('' ') masih lolos dengan backslash. Ini mungkin terkait dengan masalah Anda. Saya memperbaikinya sekarang.
Hobi Calvin
@Ventero Pasti!
Hobi Calvin
@ MrLore Regexps memungkinkan beberapa kesalahan positif, ya. Untuk memperbaiki masalah dengan garis miring terbalik di dalam tanda kutip tiga, gunakan string mentah ( r'''CODE''').
Ventero
1
@MrLore Memperbaiki titik yang tidak terhapus. Terima kasih telah memperhatikan!
Hobi Calvin

Jawaban:

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];}_~

Cobalah di http://cjam.aditsu.net/

Keluaran:

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.
aditsu berhenti karena SE adalah JAHAT
sumber
11

Ruby, 269 (311, 367) karakter

Saya punya tiga solusi berbeda untuk tantangan ini. Masing-masing dari mereka menggunakan serangkaian trik yang berbeda:

Solusi "Tepat", 367 karakter:

Solusi terpanjang kurang lebih hanya merupakan bukti konsep bahwa mungkin untuk menyelesaikan tantangan ini tanpa trik - dan hampir tidak sepenuhnya bermain golf. Ini adalah quine sejati (yaitu menghasilkan kode sumbernya sendiri daripada membacanya dari file) dan benar-benar menghitung semua angka yang dicetaknya (panjang kode, panjang keluaran, kejadian karakter). Karena cara quine bekerja, semua kode harus berada pada satu baris dan di dalam string literal.

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"

Keluaran dengan hardcode sebagian, 311 karakter:

Solusi terpendek berikutnya menggunakan dua trik, tetapi masih quine benar: - Tidak ada karakter yang muncul tepat sekali dalam kode sumber. Dengan begitu, saya tidak perlu memutuskan apakah saya harus mencetak isatau arepada paruh pertama dari output. Ini juga membuatnya lebih mudah untuk menghitung ukuran total output (meskipun saya sebenarnya tidak perlu melakukan itu). - Ukuran total output adalah hardcode. Karena ini hanya tergantung pada jumlah karakter berbeda dalam kode sumber (dan dalam kasus umum, berapa banyak karakter yang muncul hanya sekali), mudah untuk menghitungnya terlebih dahulu.

Perhatikan bahwa kode didahului oleh dua baris baru yang sangat signifikan, yang tidak akan ditampilkan oleh StackExchange di blok kode. Untuk alasan itu, saya telah menambahkan baris tambahan di depan jika baris-baris baru itu, yang bukan bagian dari kode.

#


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"

Solusi terpendek, 269 karakter:

Solusi terpendek juga membuat kode sumber panjangnya sendiri. Dengan menggunakan nama variabel yang / belum menjadi bagian dari kode sumber, dimungkinkan untuk menemukan "titik perbaikan" di mana semua karakter dalam kode sumber (termasuk digit dari panjang hardcoded!) Terjadi setidaknya dua kali.

Solusi ini juga menyimpan beberapa karakter lebih banyak dengan hanya membaca kode sumbernya sendiri dari file kode, bukan menghasilkannya. Sebagai efek samping yang bagus, ini membuat kode jauh lebih "terbaca" (tetapi siapa yang peduli tentang kode yang dapat dibaca dalam ...), karena sekarang kode tersebut tidak harus berada di dalam string literal lagi.

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

Saya juga sedikit memodifikasi script tes untuk mengurangi copy-paste yang diperlukan untuk memeriksa kode. Dengan mengganti definisi CODEdan OUTPUTdengan

import subprocess

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

print CODE
print len(CODE)

skrip sekarang secara otomatis menjalankan kode saya, membaca hasilnya, dan mengambil kode sumber dari file kode.


Inilah output yang dihasilkan oleh kode terpendek:

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.
Ventero
sumber
Bisakah Anda memposting salinan pasti dari kode dan hasil Anda sehingga saya dapat dengan mudah mengujinya? Kode tidak boleh menampilkan dirinya sendiri dan output harus berakhir dalam periode bukan baris baru.
Hobi Calvin
@ Calvin'sHobbies Blok kode pertama adalah kode saya yang sebenarnya. Itu memang mencetak output dengan baris baru final, jadi beri saya beberapa menit untuk memperbaikinya (ini adalah sesuatu yang Anda harus sebutkan dalam spesifikasi).
Ventero
Tentu saja, saya baru saja memperbarui spec.
Hobi Calvin
@ CalvinHobbies Selesai. Blok kode pertama adalah kode aktual yang dihasilkan oleh blok kode kedua (sehingga saya tidak harus mengurus pelarian string dan semuanya saat menulis kode).
Ventero