Poliglota to program, który można uruchomić w 2 lub więcej różnych językach programowania.
Jakie masz ogólne wskazówki na temat tworzenia poliglotów lub wybierania języków, które można łatwo napisać dla konkretnego zadania?
Proszę zamieścić wskazówki, które można zastosować w większości sytuacji. Tzn. Nie powinny działać tylko w poliglotach dwóch określonych języków. (Możesz po prostu opublikować odpowiedź na pytanie dotyczące wielogłosu, jeśli masz zbyt konkretną wskazówkę). Możesz jednak wprowadzić funkcje języka, które ułatwiają pracę z wieloma językami lub można je dodać do dowolnych istniejących poliglotów.
Proszę zamieścić jedną wskazówkę na odpowiedź. Nie krępuj się zasugerować edycję, jeśli wskazówka dla konkretnego języka dotyczy także innego języka.
Dziel i rządź
Kiedy piszesz poliglotę w wielu językach, niekoniecznie będziesz w stanie od razu oddzielić wszystkie przepływy kontrolne języka od siebie. W związku z tym będziesz musiał „prawdziwie poliglotyczny” niektóre języki przez pewien czas, pozwalając na uruchomienie tego samego kodu w każdym z nich. Robiąc to, należy pamiętać o dwóch głównych zasadach:
Przepływ sterowania w dowolnych dwóch językach powinien być albo bardzo podobny, albo bardzo różny . Próba obsługi dużej liczby przeplatanych przepływów sterowania jest receptą na dezorientację i utrudnia modyfikację programu. Zamiast tego powinieneś ograniczyć ilość pracy, którą musisz wykonać, upewniając się, że wszystkie programy znajdujące się w tym samym miejscu są dostępne z tego samego powodu i mogą być uruchomione równolegle tak długo, jak potrzebujesz. Tymczasem, jeśli język jest bardzo różny od innych, chcesz, aby jego wykonanie jak najszybciej przeniosło się do zupełnie innej lokalizacji, abyś nie musiał próbować dostosowywać kodu do dwóch różnych modeli składniowych jednocześnie.
Szukaj możliwości rozdzielenia jednego języka lub grupy podobnych języków od siebie. Pracuj od większych grup do mniejszych grup. Kiedy już będziesz mieć grupę podobnych języków w pewnym momencie programu, będziesz musiał je w pewnym momencie podzielić. Na początku programu możesz, powiedzmy, chcieć podzielić języki używane
#
jako znacznik komentarza z dala od języków, które używają innego znacznika komentarza. Później być może masz punkt, w którym wszystkie języki używająf(x)
składni wywołań funkcji, oddzielają polecenia średnikami i mają podobne podobieństwa składniowe. W tym momencie możesz użyć czegoś znacznie bardziej specyficznego dla języka, aby je podzielić, np. Fakt, że Ruby i Perl nie przetwarzają sekwencji specjalnych w''
ciągach, ale Python i JavaScript to robią.Ogólnie rzecz biorąc, logiczny przepływ programu powinien skończyć się jako drzewo, wielokrotnie dzieląc się na grupy języków, które są bardziej do siebie podobne. To stawia większość trudności w pisaniu poliglota na samym początku, przed pierwszym podziałem. Ponieważ przepływ sterowania rozgałęzia się coraz bardziej, a języki działające w danym punkcie stają się coraz bardziej podobne, zadanie staje się łatwiejsze, ponieważ można używać bardziej zaawansowanej składni bez powodowania błędów składniowych.
Dobrym przykładem jest zestaw {JavaScript, Ruby, Perl, Python 3}; wszystkie te języki akceptują wywołania funkcji w nawiasach i mogą rozdzielić instrukcje średnikami. Wszystkie obsługują także
eval
instrukcję, która skutecznie pozwala na kontrolę przepływu w przenośny sposób. (Perl jest najlepszym z tych języków, aby wcześnie oddzielić się od grupy, ponieważ ma inną składnię dla zmiennych niż inne).źródło
Ukryj kod w literałach ciągów
W większości języków literał ciąg znaków sam w sobie nie robi nic lub robi coś, co można łatwo odwrócić (na przykład wepchnięcie łańcucha na stos). Składnia literału łańcuchowego jest również stosunkowo niestandardowa, szczególnie w przypadku alternatywnych składni, których wiele języków używa do obsługi ciągów znaków z osadzonymi znakami nowej linii; na przykład Python ma
""" ... """
, Perl maq( ... )
, a Lua ma[[ ... ]]
.Istnieją dwa główne ich zastosowania. Jednym z nich jest umożliwienie przeplatania sekcji przeznaczonych dla różnych języków poprzez rozpoczęcie łańcucha na końcu pierwszej sekcji jednego języka i wznowienie go na początku drugiego: powinno być dość łatwo uniknąć przypadkowego zamknięcia łańcucha ze względu na różnorodność ograniczniki ciągu między różnymi językami. Drugi polega na tym, że wiele ograniczników łańcuchów ma znaczenie jako polecenie w innych językach (często bardziej niż znaczniki komentarzy), więc możesz zrobić coś takiego
x = [[4] ]
, co jest nieszkodliwym przypisaniem w językach, które używają notacji JSON dla list, ale która zaczyna się ciąg znaków w Lua (a tym samym pozwala ci oddzielić kod Lua od reszty, biorąc pod uwagę, że skutecznie „przeskakuje” do następnego]]
).źródło
Zakończenie programu
Możesz zakończyć program nagle w jednym języku, aby zignorował kod w innym języku.
Zasadniczo można użyć tego formatu
gdzie
end_program_in_languageN
jest polecenie zakończenia programu.Na przykład w mojej odpowiedzi w temacie Co przyniesiesz na Święto Dziękczynienia? , Zakończyłem program w Dipie, a następnie napisałem kod dla innego języka, V, aby interpreter Dip go zignorował.
Ale nie wszystkie języki mają polecenie, które może tak zakończyć program. Jeśli jednak taki język ma tę funkcję, należy go używać mądrze.
Jak sugeruje @LuisMendo, możesz utworzyć błąd (jeśli jest dozwolony), aby zakończyć program, jeśli język nie ma jeszcze wbudowanego „programu końcowego”.
źródło
Zmienna lub kod w literałach łańcuchowych
Cudzysłowy ciąg literałów jest w większości języków nieszkodliwy. Ale w niektórych językach mogą również zawierać kod.
W Bash możesz użyć
`...`
(to nie kończy programu):W Tcl możesz użyć
[...]
:W PHP możesz użyć
${...}
(powoduje to błąd w Bash, więc musi pojawić się po kodzie Bash):W Ruby możesz użyć
#{...}
:Mogą być też inni.
Te gramatyki nie są kompatybilne. Oznacza to, że możesz umieścić cały kod tych języków w jednym ciągu w nieszkodliwym miejscu. I po prostu zignoruje nierozpoznany kod w innych językach i zinterpretuje je jako treść ciągu.
W wielu przypadkach można również łatwo skomentować znak podwójnego cudzysłowu i uczynić bardziej tradycyjnym poliglotą.
źródło
Zmienne aliasing
Jest to prawdopodobnie jedna z najprostszych, jak dotąd (IMO), sztuczek, których można użyć, zwłaszcza że może ona dotrzeć do tak wielu języków.
Przykład:
Działa to nie tylko w Javascript, ale także w Pythonie, Ruby itp. Więcej przykładów później, gdy pomyślę o kilku innych. Oczywiście mile widziane są sugestie dotyczące komentarzy / zmiany postów.
źródło
alert
doprint
w Pythonie (tylko 3), ponieważ składnia komentarza JS//
może być łatwo przetworzona w program Python, podczas gdy Python#
nie może być przetworzony w JS.#
komentarze na podstawieTa wskazówka jest podzbiorem symboli komentarzy wykorzystujących lukę i cytatów blokowych w co najmniej jednym języku
Podczas tworzenia poliglotów z wieloma językami, zwłaszcza językami gotowymi do produkcji w przeciwieństwie do esolangów, warto spojrzeć na języki, które są używane
#
w komentarzach blokowych lub jednowierszowych.#
, a po znakach jest duża różnorodność znaków#
.#
komentarz liniowy, co oznacza, że coś, co może rozpocząć komentarz blokowy w jednym języku, jest zwykłym komentarzem w innym, co ułatwia dopasowanie.Oto krótka lista języków używanych
#
w komentarzu blokowym (niewyczerpujący):Aby uzyskać więcej przykładów, zobacz Kod Rosetta .
Oto szybki i łatwy przykład, jako demonstracja:
źródło
#- ... -#
.Rozbieżności operatorów arytmetycznych
W przypadku podobnych języków lub prostych poliglotów czasem warto poszukać różnic w działaniu arytmetyki. Wynika to z faktu, że większość (nie-ezoterycznych) języków ma operatory arytmetyczne z poprawkami, a arytmetyka może być szybkim i łatwym sposobem wprowadzenia różnicy.
Na przykład:
^
jest bitowy XOR w niektórych językach i potęgowanie w innych/
jest dzieleniem liczb całkowitych w niektórych językach i dzieleniem zmiennoprzecinkowym w innych-1/2
występuje-1
w niektórych językach (zaokrąglenie w dół) i0
w innych (zaokrąglenie do zera)-1%2
jest-1
w niektórych językach i1
w innych--x
jest zakazem w niektórych językach (podwójne negowanie) i wstępnym zmniejszeniem w innych1/0
daje nieskończoność w niektórych językach i błędy w innych1<<64
daje 0 w niektórych językach (przepełnienie) i36893488147419103232
w innychźródło
x=1;["JS","Python"][--x]
zwrócenie nazwy języka, w którym jest uruchomiony (między JS a Pythonem).Użyj Brainfuck
Prawie wszystkie implementacje BF wyrzucają znaki, które nie są
+-<>[].,
, co akurat działa na naszą korzyść!BF jest prawdopodobnie jednym z najłatwiejszych języków do pracy w poliglocie z powodu tej funkcji, o ile najpierw piszesz część BF. Po wypisaniu kodu BF wystarczy jedynie modelować dowolny inny kod wokół struktury BF.
Oto naprawdę prosty przykład:
To prawie inkrementy i kodowanie znaków wyjściowych „na zawsze” (w zależności od ustawień środowiska wykonawczego). Teraz, jeśli chcesz napisać losowy fragment kodu, powiedzmy w JS, możesz:
Zauważ, jak JS jest formowany wokół BF.
Pamiętaj, że działa to najlepiej, jeśli naprawdę zaczynasz od BF; znacznie trudniej jest zacząć od innego języka i spróbować włączyć BF.
źródło
[]
jak to konieczne.x=>
zmienia komórkę, co w tym przypadku nie ma znaczenia, ale chciałem tylko powiedziećUżywaj języków, w których większość znaków nie ma znaczenia
Jest to uogólnienie twierdzenia Mama Fun Roll o BF . Esolang, który ignoruje większość postaci, jest bardzo przydatny w poliglotach. Przydatny także: esolang, w którym duży zestaw znaków jest wymienny. Kilka przykładów:
()[]{}<>
. (@
czasami powoduje błąd, gdy interpreter próbuje go parsować jako początek flagi debugowania).źródło
@
błąd.exec('''...\t\n\40''')
Uważaj na komentarze zagnieżdżonego bloku
Czasami wiele języków będzie używać tej samej składni do komentowania bloków, co często stanowi przeszkodę w tworzeniu poliglota z tymi dwoma językami. Jednak bardzo rzadko jeden z języków pozwala na zagnieżdżone komentarze blokowe, które można wykorzystywać do tworzenia oddzielnych ścieżek kodu.
Weźmy na przykład tę poliglota:
Nim i Lily zarówno używają, jak
#[
i]#
rozpoczynają i kończą komentarze blokowe, ale tylko Nim pozwala na zagnieżdżone komentarze blokowe.Lily uważa drugą
#[
za część pojedynczego komentarza do bloku, a pierwszą]#
za zakończenie komentarza do bloku. (#
Poniższa instrukcja drukowania Lily jest komentarzem do wiersza, który ukrywa kod Nima.)Nim alternatywnie, widzi
#[]#
jako zagnieżdżony (choć pusty) komentarz do bloku iprint("Lily")#
jako komentarz do bloku zewnętrznego.źródło
Nie jestem pewien, czy to się liczy, ale ...
Użyj linii shebang, aby zmienić wszystko w prawidłowy
perl
programZgodnie z tą odpowiedzią i dokumentacją Perla, jeśli przekażesz dowolny plik, który zaczyna się od linii shebang
perl
, wywołuje odpowiedni program do jego uruchomienia. Na przykład tozostanie wywołany przez interpreter Pythona, jeśli zadzwonisz
perl filename.py
.źródło
perl
, nie staje się on programem Perla.perl
”? Brzmi jak dobry mem filozof -aptor ...Wywołaj nieistniejące funkcje, a następnie wyjdź, oceniając ich argumenty
Wiele języków programowania jest w stanie analizować dowolny identyfikator, a następnie parę nawiasów z wyrażeniami wewnątrz:
Czasami forma omawianego identyfikatora może zostać naprawiona, ponieważ konieczne jest nadanie kodu w innym używanym języku. Może to początkowo sprawiać problemy, jeśli identyfikator nie odpowiada funkcji, którą faktycznie posiada język.
Jednak wiele języków programowania ocenia argumenty funkcji, zanim sprawdzą, czy sama funkcja faktycznie istnieje (np. Lua), więc i tak możesz użyć tego rodzaju konstrukcji; wystarczy wyjść z programu gdzieś w argumentach funkcji.
Oto przykład poliglota dc / Lua:
c2pq
to program dc do wypisania 2 i wyjścia; Lua postrzega to jako nazwę funkcji, ale Lua można zapobiec błędom poprzez umieszczenie komendy exit w argumencie. Dużą zaletą tej konstrukcji jest to, że w przeciwieństwie do przypisania (c2pq =
), nie jest ona automatycznie niezgodna z językami, w których nazwy zmiennych zaczynają się od sigil; składnia nazwy funkcji jest znacznie bardziej spójna we wszystkich językach niż składnia nazwy zmiennej.źródło