Czy umiesz Meta Quine?

25

Podobnie jak w przypadku innych zagadek z quine (a konkretniej tej ), napisz program, który sam wytwarza źródło.

Oto nowy zwrot: wygenerowany kod NIE powinien być identyczny ze źródłem. Zamiast tego powinien wypisać inny program, który utworzy pierwszy.

Wyzwanie związane z powyższym osiągnięto poprzez przeskakiwanie między dwoma językami. Myślę, że ten zostałby zrobiony tylko w jednym języku , ale dwie (lub więcej) wersje źródła powinny się znacznie różnić (patrz reguły poniżej). Przy takim ograniczeniu odpowiedzi jednoznakowe byłyby niedozwolone, co wymagałoby nieco więcej refleksji nad ostatecznym poddaniem się.


ZASADY

  1. Twój kod musi być napisany tylko w jednym języku. (Wiele zgłoszeń, po jednym dla każdego języka jest całkowicie do przyjęcia).
  2. Różne wersje kodu muszą być odrębne pod względem składniowym. Innymi słowy, jeśli narysujesz abstrakcyjne drzewo składniowe dla swojego kodu, powinien istnieć co najmniej jeden inny węzeł.
    • Doprowadzania AST nie będzie konieczne, ale jeśli czujesz się skłonny dostarczyć po jednym dla każdego z programów, to by pomóc w ocenie.
  3. Możesz wykonać tyle iteracji, ile chcesz, o ile wszystkie pozostają odrębne pod względem składniowym. (Więcej pomoże w uzyskaniu wyniku, patrz poniżej.)

PUNKTACJA

Twój końcowy wynik będzie średnią długością wszystkich programów podzieloną przez liczbę programów.

Przykład 1:

A (źródło dla B) = 50 znaków
B (źródło dla A) = 75 znaków
Wynik końcowy = 31,25

Przykład 2:

A (źródło dla B) = 50 znaków
B (źródło dla C) = 75 znaków
C (źródło dla A) = 100 znaków
Wynik końcowy = 25

Gaffi
źródło
18
Raz meta quine.
mellamokb
1
@mellamokb har har ;-)
Gaffi,
To właściwie bardziej ogólna wersja tego wyzwania quine, a odpowiedzi tam udzielone również tutaj wygrywają.
przestał obracać przeciwnie do zegara
@leftaroundabout, wymaganie dotyczące różnic składniowych unieważnia „obracający się quine”, więc nie jest to bardziej ogólne.
stoisko
2
Nigdy nie meta quine nie lubiłem.
Stack Tracer

Odpowiedzi:

35

Python, 0 (limit (68 + 3 n ) / (16 n ))

Jeśli dwa abstrakcyjne drzewa składniowe są różne, jeśli mają różne stałe,

r='r=%r;n=(0x%XL+1)%%0x10...0L;print r%%(r,n)';n=(0xF...FL+1)%0x10...0L;print r%(r,n)

istnieje 16 n programów o długości najwyżej 68 + 3n, co daje wynik asymptotyczny równy 0.

Jeśli potrzebujesz programów o zmiennej strukturze, możemy zaimplementować adder binarny na n bitach. Tutaj są 2 n programów o długości O ( n 2 ). Idzie w cyklu z powodu upuszczonego bitu przenoszenia.

s="""
print 's='+'"'+'"'+'"'+s+'"'+'"'+'"'
n=lambda m:reduce(lambda (s,c),y:(s+(c^y,),c&y),m,((),1))[0]
print s[:112]
t=n(t)
print "t=(%s,)+(0,)*%s"%(t[0],len(t)-1)
for i in range(len(t)-1):
    print i*' '+'for i in range(2):'
    print ' '+i*' '+['pass','t=n(t)'][t[i+1]]
print s[113:-1]
"""

print 's='+'"'+'"'+'"'+s+'"'+'"'+'"'
n=lambda m:reduce(lambda (s,c),y:(s+(c^y,),c&y),m,((),1))[0]
print s[:112]
t=(0,)+(0,)*10
for i in range(2):
 t=n(t)
 for i in range(2):
  t=n(t)
  for i in range(2):
   t=n(t)
   for i in range(2):
    t=n(t)
    for i in range(2):
     pass
     for i in range(2):
      t=n(t)
      for i in range(2):
       pass
       for i in range(2):
        pass
        for i in range(2):
         pass
         for i in range(2):
          t=n(t)
t=n(t)
print "t=(%s,)+(0,)*%s"%(t[0],len(t)-1)
for i in range(len(t)-1):
    print i*' '+'for i in range(2):'
    print ' '+i*' '+['pass','t=n(t)'][t[i+1]]
print s[113:-1]
boothby
źródło
Czy mogę się mylić? Wygląda na to, że wyjście jest identyczne jak źródło (nie cel tego wyzwania)?
Gaffi
Zajrzyj do zagnieżdżonego bloku. passzmieni się do t=n(t)i z powrotem we wszystkich 2 ^ n kombinacjach.
stoisko
Teraz to widzę. Myliłeś mnie z całym powtórzeniem!
Gaffi
22
z jakiegoś powodu lubię bardzo długie rozwiązania golfowe z małymi wynikami.
stoisko
Wow, jesteś całkowicie tego właścicielem! Bardzo dobrze.
Claudiu
4

Perl, wynik 110,25

Muszę przyznać, że nie jestem zbyt dobry w quines. Jestem w 100% pewien, że jest miejsce na poprawę. Rozwiązanie opiera się na tej samej zasadzie rozwiązania elementu poniżej.

Pierwszy program ma 264 znaków.

$s='$a=chr(39);print"\$s=$a$s$a;";$s=reverse$s;for(1..87){chop$s}$s=reverse$s;print$s;$f++;if($f==0){$a=chr(39);print"\$s=$a$s$a;$s"}';$a=chr(39);print"\$s=$a$s$a;";$s=reverse$s;for(1..87){chop$s}$s=reverse$s;print$s;$f++;if($f==0){$a=chr(39);print"\$s=$a$s$a;$s"}

Drugi program ma 177 znaków.

$s='$a=chr(39);print"\$s=$a$s$a;";$s=reverse$s;for(1..87){chop$s}$s=reverse$s;print$s;$f++;if($f==0){$a=chr(39);print"\$s=$a$s$a;$s"}';if($f==0){$a=chr(39);print"\$s=$a$s$a;$s"}

Pracuję nad AST dla tego wpisu (i wpisu Element).


Element , wynik 47,25

Pierwszy program ma 105 znaków.

\ \3\:\$\'\[\\\\\`\(\`\]\#\2\1\'\[\(\#\]\`\ \3\:\$\'\[\\\\\`\(\`\]\#\` 3:$'[\\`(`]#21'[(#]` 3:$'[\\`(`]#`

Drugi program ma 84 znaki.

\ \3\:\$\'\[\\\\\`\(\`\]\#\2\1\'\[\(\#\]\`\ \3\:\$\'\[\\\\\`\(\`\]\#\` 3:$'[\\`(`]#`

Jestem pewien, że jest wiele miejsca na ulepszenia.

W pierwszym programie jest jeden ciąg znaków (w którym każdy znak jest uciekany, pomimo dużej nadmiarowości), po którym następują części wykonywalne A i B. Część A robi kilka rzeczy: drukuje ciąg i ucieka z każdego znaku, drukuje ostatnią połowę ciągu (który jest źródłem części B), a następnie uniemożliwia zrobienie czegokolwiek następującej po nim części B.

Drugi program to ten sam ciąg, po którym następuje część B. Część B oparta jest na prostym quine; wypisuje ciąg poprzedzony jego ucieczką. Oznacza to, że drukuje ciąg oraz obie części A i B.

PhiNotPi
źródło
Myślę, że to definitywnie, ponad wszelką wątpliwość, potwierdza ważność elementu jako języka programowania. Jest tak łatwy w użyciu, że ja, tak niedoświadczony, że udało mi się napisać tylko jednego kompletnego tłumacza dla Elementu, byłem w stanie odpowiedzieć na to pytanie, zanim jakakolwiek inna osoba na całej planecie złożonej z 7 milionów milionów ludzi. Paradygmat elementu „jeden znak, jedna funkcja, cały czas” oznacza, że ​​cały kod jest całkowicie jednoznaczny. Język jest wszechstronny: z wyjątkiem []{}dowolnego polecenia można umieścić w dowolnym miejscu w całym programie bez powodowania błędu składniowego. To jest idealne.
PhiNotPi
4
Trochę stronniczy, prawda? ;-)
Gaffi,
3

VBA: (251 + 216) / 2/2 = 116,75

251

Sub a()
r=vbCrLf:c="If b.Lines(4, 4) = c Then"&r &"b.InsertLines 8, d"&r &"b.DeleteLines 4, 4"&r &"End If":d="b.InsertLines 6, c"&r &"b.DeleteLines 4, 2"
Set b=Modules("Q")
If b.Lines(4, 4) = c Then
b.InsertLines 8, d
b.DeleteLines 4, 4
End If
End Sub

216

Sub a()
r=vbCrLf:c="If b.Lines(4, 4) = c Then"&r &"b.InsertLines 8, d"&r &"b.DeleteLines 4, 4"&r &"End If":d="b.InsertLines 6, c"&r &"b.DeleteLines 4, 2"
Set b=Modules("Q")
b.InsertLines 6,c
b.DeleteLines 4,2
End Sub

Jest to uruchamiane w MSAccess, aby skorzystać z Moduleobiektu. Moduł nosi nazwę "Q"gry w golfa. Różnica w składni wynika z If ... Thenbraku w krótszej wersji.

Gaffi
źródło
najprawdopodobniej uda Ci się zmienić vbCrLFnavbCr
Taylor Scott
3

C ++, wynik 0,734194

Poniższy kod źródłowy wypisuje na konsoli metazwin 999 (wyjaśnienie poniżej):

#define X 1*(1+1)
#include<iostream>
#include<vector>
#define Q(S)auto q=#S;S
Q( \
  main() \
  { \
      using namespace std; \
      cout<<"#define X 1"; \
      int x=X==2?1000:X-1; \
      vector<int> factors; \
      for ( int p = 2; p <= x; ++p) \
      { \
        while ( x % p == 0 ) \
        { \
          factors.push_back( p ); \
          x /= p; \
        } \
      } \
      for ( int factor : factors ) \
      { \
        cout<<"*(1"; \
        for ( int i=1;i<factor;++i) \
          cout<<"+1"; \
        cout<<")"; \
      } \
      cout<<"\n#include<iostream>\n#include<vector>\n#define Q(S)auto q=#S;S\nQ("<<q<<")"; \
  })

Jedyny wiersz, który się zmienia, to pierwszy wiersz. Wartość Xbędzie wynosić 1000, 999, 998, ..., 3, 2, a następnie rozpocznie się od nowa. Jednak w celu uzyskania różnych drzew składniowych za każdym razem Xjest reprezentowany w kategoriach jego pierwszej faktoryzacji, gdzie każda liczba pierwsza jest zapisywana jako suma 1s. Wartości AST są różne, ponieważ rozkład na czynniki pierwsze liczb całkowitych jest różny dla każdej wartości.

Program wydrukuje się sam, z wyjątkiem zmiany pierwszego wiersza i usunięcia ukośników odwrotnych, podziałów linii i wcięć Q(...).

Poniższy program oblicza wynik mojej odpowiedzi:

#include <iostream>

const int n = 1000;

int getProgramLength( int n )
{
  int sum = 442;
  for ( int p = 2; p*p <= n; ++p )
  {
    while ( n % p == 0 )
    {
      sum += 2 * ( 1 + p );
      n /= p;
    }
  }
  if ( n > 1 )
    sum += 2 * ( 1 + n );
  return sum;
}

int main()
{
  int sum = 0;
  for ( int i = 2; i <= n; ++i )
    sum += getProgramLength( i );
  std::cout << (double)sum/(n-1)/(n-1) << '\n';
}

Wydrukował 0.734194 na konsoli. Oczywiście 1000 można zastąpić większymi liczbami całkowitymi, a wynik zbliży się do zera jako granicy. Matematyczny dowód na to, że funkcja Ziem Riemanna jest nieco skomplikowana. Zostawiam to jako ćwiczenie dla czytelnika. ;)

Ralph Tandetzky
źródło
2

JavaScript, 84,5 64 61

Dwa programy, oba o długości 169 128 122.

(function c(){alert(/*
2/*/1/**/);return ('('+c+')()').replace(/\/([/\*])/,function(m,a){return a=='*'?'/\/':'/\*'});
})()

Zanim grałem w golfa, dla twojej przyjemności oglądania:

(function c() {
    var r = /\/([/\*])/;
    var f = function(m, a) { return a === '*' ? '/\/' : '/\*' };
    var p = '(' + c + ')();';
    p = p.replace(r, f);
    /* This is just a comment!
    console.log('Quine, part two!'); /*/
    console.log('Quine, part one!'); /**/
    return p;
})();

Zwraca nowy program i wyświetla bieżącą część! Prawdopodobnie mógłbym go skrócić bez wyrażenia regularnego, ale ... nie chcę.

Ry-
źródło
Nie, są one odrębne pod względem składniowym. To znaczy po dodaniu nowych linii.
Ry-
2

J - (24 + 30) / 2/2 = 13,5 pkt

Należy zauważyć, że ciągi w J nie są backslash-uciekł, ale cytat-uciekł à la Pascal: 'I can''t breathe!'.

30$(,5#{.)'''30$(,5#{.)'         NB. program 1, 24 char
'30$(,5#{.)''''''30$(,5#{.)'''   NB. program 2, 30 char

Program 1 ma AST, noun verb hook nouna program 2 ma AST noun. Program 2 to cytowana wersja programu 1, który po uruchomieniu zwróci program 1, więc tej metody nie można łatwo rozszerzyć na trzy kopie: P

Program 1 działa, pobierając kopię części kodu źródła, z cytatem dołączonym z przodu i dodając pięć z tych cytatów na końcu ( (,5#{.)). Następnie cyklicznie pobiera 30 znaków z tego 16-znakowego ciągu, co daje dokładnie Program 2 w rezultacie.

algorytmshark
źródło