Wskazówki do gry w golfa w GolfScript

35

Co, ten post jeszcze nie istnieje?

Oczywiście, GolfScript jest stworzony do gry w golfa, więc możesz pomyśleć, że tak naprawdę nie są potrzebne żadne konkretne wskazówki. Aby jednak w pełni wykorzystać funkcje GolfScript, musisz nauczyć się kilku nieoczywistych sztuczek. Ten post służy do zbierania przydatnych porad i wskazówek.

Na początek oto oficjalne strony referencyjne GolfScript. Najpierw powinieneś naprawdę zapoznać się z tymi:

W szczególności bardzo polecam przeczytanie stron w tej kolejności - szybki przegląd jest mało przydatny, dopóki nie zaznajomisz się z wbudowanymi funkcjami, a samouczek zawiera kilka ważnych szczegółów, które nie zostały wyjaśnione na innych stronach .


Ps. Ze względu na inspirację i osobiste zainteresowania, oto kilka pytań , na które chciałbym znaleźć miłe odpowiedzi:

  • Jak wykonać ograniczoną transliterację w GolfScript? {FROM?TO=}%działa, jeśli możesz być pewien, że wszystkie dane wejściowe znajdują się w FROM(lub nie przejmuj się, że wszystkie są mapowane do ostatniego elementu TO), ale wszystkie sposoby, w jakie widziałem pozostawienie niezmapowanych wartości bez zmian, były mniej lub bardziej klugey.

  • Jak najlepiej przekonwertować ciąg znaków na tablicę kodów ASCII i odwrotnie? Jakie operacje powodują to jako efekt uboczny? Jaki jest najlepszy sposób na zrzucenie znaków ciągu znaków na stos (podobnie jak w ~przypadku tablic)?

Ilmari Karonen
źródło
Kolejne pytanie: czy jest to dobry sposób, aby przekształcić ... xsię ... [x]? Najlepsze, co mogę zobaczyć, to [.;].
Peter Taylor
@Peter: Jeśli xjest liczbą, to []+działa i jest o jeden znak krótszy. I oczywiście, jeśli xjest to jedyna rzecz na stosie, to po prostu ]zrobi.
Ilmari Karonen
Chciałbym zapytać o najlepsze sposoby: wartość minimalną, maksymalną i bezwzględną. Wszystkie moje rozwiązania wydają się przyjmować znacznie więcej postaci niż powinny.
Claudiu
Jaki jest najlepszy sposób modyfikacji tablicy przy danym indeksie?
user1502040
@ user1502040: poniżej udzielono odpowiedzi. (Jeśli ktoś zna lepszy sposób, podziel się nim!)
Ilmari Karonen

Odpowiedzi:

29

Racjonalny / zmiennoprzecinkowy / złożony

Czytałem tyle razy, że GolfScript ma tylko liczby całkowite, że zacząłem w to wierzyć. Cóż, to nieprawda.

2-1? # Raise 2 to the power of -1. Result: 0.5
4-1? # Raise 4 to the power of -1. Result: 0.25
+    # Add. Result: 0.75

Dane wyjściowe to

3/4

ze standardowym interpreterem GolfScript i

0.75

na Web GolfScript .

Podobne hacki pozwalają rzucać na Rational, Float lub nawet Complex:

{-2.?./*}:rational
{2.-1??./*}:float
{-2.-1??./*}:complex
Dennis
źródło
9
OMGWTFHAX o_O !!!
Ilmari Karonen
3
WAT! Jestem pewien, że to błąd w tłumaczu, ale wow
Klamka
3
Linia 82 z najnowszych tłumacza: Gint.new(@val**b.val). Wygląda na to, że Gintkonstruktorowi brakuje int int ...
primo
10

Negowanie liczby

Jedną z rzeczy, której brakuje GolfScript, jest wbudowany operator negacji. Oczywiste sposoby przekonwertowania liczby ze stosu na liczbę ujemną, takie jak -1*lub 0\-, wymagają trzech znaków. Jest jednak sposób na zrobienie tego na dwa sposoby:

~)

Działa to, ponieważ GolfScript używa arytmetyki dopełniania dwóch , więc ~ x równa się - x −1.

Oczywiście wariant (~działa również; wybór między nimi jest ogólnie kwestią gustu.

Ilmari Karonen
źródło
9

Przetasowanie tablicy

Najprostszym sposobem przetasowania tablicy w GolfScript jest sortowanie według losowego klucza sortowania. Jeśli potrzebujesz tylko tandetnie przetasować kilka wartości, zrobi to następujący kod:

{;9rand}$

Pamiętaj, że nawet w przypadku krótkich list nie da to dobrego losowania. Z powodu paradoksu urodzinowego , aby uzyskać rozsądnie jednolite tasowanie, argument randmusi być znacznie większy niż kwadrat długości tasowanej listy.

Zastąpienie 9powyższego przez99 daje zatem całkiem dobre wyniki dla list składających się z maksymalnie dziesięciu elementów, ale wykazuje zauważalne odchylenie dla dłuższych list.

Poniższy kod, który używa 9 9 = 387,420,489 możliwych wartości, jest odpowiedni dla około 1000 pozycji (i jest akceptowalny dla około 20 000):

{;9.?rand}$

W przypadku naprawdę długich list dodaj jeszcze 9 dla wartości 99 99 ≈ 3,7 × 10 197 :

{;99.?rand}$

Testowanie:

Oto rozkład pierwszego elementu na 10-elementowej liście, przetasowanej przy użyciu różnych wariantów pokazanych powyżej, z próbkami ponad 10 000 prób:

  • Dane wyjściowe 10,{;9rand}$0=pokazują bardzo wyraźne uprzedzenie, przy 0czym prawdopodobieństwo trzykrotnego wylądowania na pierwszej pozycji jest następujące 1:

    0 16537 #######################################################
    1 5444  ##################
    2 7510  #########################
    3 8840  #############################
    4 9124  ##############################
    5 12875 ##########################################
    6 9534  ###############################
    7 8203  ###########################
    8 7300  ########################
    9 14633 ################################################
    
  • Z 10,{;99rand}$0=, większość odchyleń zniknęła, ale wciąż pozostaje zauważalna ilość:

    0 10441 ##################################
    1 9670  ################################
    2 9773  ################################
    3 9873  ################################
    4 10134 #################################
    5 10352 ##################################
    6 10076 #################################
    7 9757  ################################
    8 9653  ################################
    9 10271 ##################################
    
  • Z 10,{;9.?rand}$0=, wynik jest w zasadzie nie do odróżnienia od prawdziwie losowej próbki:

    0 9907  #################################
    1 9962  #################################
    2 10141 #################################
    3 10192 #################################
    4 9965  #################################
    5 9971  #################################
    6 9957  #################################
    7 9984  #################################
    8 9927  #################################
    9 9994  #################################
    

Ps. W przypadku naprawdę złego tasowania tablic lub ciągów liczbowych następujący kod może być czasem akceptowany:

{rand}$

Na ogół będzie on absurdalnie tendencyjny, ale dopóki wszystkie elementy tablicy wejściowej (lub wszystkie kody znaków w łańcuchu) będą większe niż jeden, ma niezerowe prawdopodobieństwo wytworzenia dowolnej permutacji tablicy, która czasami może spełnić źle napisane wymagania dotyczące wyzwań.

Ilmari Karonen
źródło
3
Pamiętam, że kiedyś sceptycznie zrobiłem matematykę na temat paradoksu urodzinowego po tym, jak mój brat powiedział mi o tym, miał rację :(
ajax333221,
8

Aby odpowiedzieć na konkretne pytanie:

Jak najlepiej przekonwertować ciąg znaków na tablicę kodów ASCII i odwrotnie? Jakie operacje powodują to jako efekt uboczny? Jaki jest najlepszy sposób na zrzucenie znaków ciągu na stos (tak jak ~ robi dla tablic)?

Dla tych, którzy nie rozumieją problemu, system typów GolfScript daje pierwszeństwo typom w kolejności: liczba całkowita, tablica, ciąg, blok. Oznacza to, że zwykłe operacje tablicowe zastosowane do ciągu prawie zawsze dają ciąg. Na przykład

'ABC123'{)}%

pozostawi 'BCD234'na stosie.

W rezultacie najlepszym sposobem na konwersję łańcucha znaków na tablicę kodów ASCII jest prawie na pewno zrzucenie znaków ze stosu, a następnie zebranie ich w tablicę.

Jaki jest najlepszy sposób na zrzucenie znaków w ciągu na stos? {}/

Jaki jest najlepszy sposób przekonwertowania ciągu znaków na tablicę kodów ASCII? [{}/](ze zwykłym zastrzeżeniem, że jeśli na stosie nie ma nic więcej, możesz pominąć [)

Jaki jest najlepszy sposób na konwersję tablicy kodów ASCII na ciąg? ''+(Zauważ, że to również spłaszcza tablicę, więc np. [65 [66 67] [[[49] 50] 51]]''+Daje 'ABC123')

Peter Taylor
źródło
Jaki jest najlepszy sposób na przekształcenie pojedynczego kodu ASCII w ciąg? []+''+? (wydaje się raczej długi)
Justin
@Quincunx, jest raczej długi, ale nie znam lepszego sposobu. Warto przyjrzeć się, skąd pochodzi kod ASCII i sprawdzić, czy można go dostać już w tablicy.
Peter Taylor
6

Jeśli Twój program w tajemniczy sposób się psuje, sprawdź swoje zmienne

Właśnie spędziłem chwilę na debugowaniu pozornie poprawnego programu, który był używany !jako zmienna (na tej podstawie, że nie zamierzałem go ponownie używać). Niestety nie używania if, a okazuje się, że realizacja ifpołączeń !zdecydować, który oddział do naśladowania.

Peter Taylor
źródło
6

Zawijanie górnego elementu stosu w tablicę

Czy jest to dobry sposób, aby przekształcić ... xsię ... [x]?

Dla pełnej ogólności najlepszą opcją wydają się 4 znaki. Jednak w niektórych szczególnych przypadkach można to zmniejszyć.

1 znak

]działa w specjalnym przypadku, który xjest jedyną rzeczą na stosie.

3 znaki

[]+działa w specjalnym przypadku, który xjest liczbą całkowitą.

.,/działa w specjalnym przypadku, którym xjest prawdziwa tablica lub ciąg znaków. Np "AB".,/daje ["AB"]; 3,.,/daje [[0 1 2]]. Jednak "".,/i [].,/oba dają [].

4 znaki

[.;] działa bezwarunkowo.

Peter Taylor
źródło
6

Jaki jest najlepszy sposób modyfikacji tablicy przy danym indeksie? - użytkownik1502040

To dobre pytanie. W GolfScript nie ma bezpośredniego sposobu przypisania wartości do elementu tablicy, więc w ten czy inny sposób będziesz musiał odbudować całą tablicę.

Najkrótszym ogólnym sposobem, jaki znam, aby wstawić nową wartość xindeksu ido tablicy, jest podzielenie tablicy pod danym indeksem i dołączenie xdo pierwszej połowy przed ponownym połączeniem ich ze sobą:

  • .i<[x]+\i>+(11 znaków) - wstaw wartość xdo tablicy o indeksie (0)i

Aby zastąpić wartości w indeksie iz x, po prostu trzeba skrócić drugą połowę tablicy o jeden element:

  • .i<[x]+\i)>+(12 znaków) - zamień element o indeksie (0) ina wartośćx

Alternatywnie, skrócenie pierwszej połowy zamiast tego skutecznie zrobi to samo, ale z indeksowaniem opartym na 1, co czasami może być preferowane:

  • .i(<[x]+\i>+(12 znaków) - zamień element o indeksie (1) iwartościąx

We wszystkich powyższych przykładach, jeśli xjest liczbą, nawiasy kwadratowe wokół niej można pominąć, aby zapisać dwa znaki, ponieważ i tak zostanie ona automatycznie zmuszona do tablicy +:

  • .i<x+\i>+(9 znaków) - wstaw liczbę xdo tablicy o indeksie (0)i
  • .i<x+\i)>+(10 znaków) - zamień element o indeksie (0) ina liczbęx
  • .i(<x+\i>+(10 znaków) - zamień element o indeksie (1) ina liczbęx

Nawiasy można również pominąć, jeśli jeden xlub wejściowa „tablica” (lub obie) są w rzeczywistości łańcuchami, w którym to przypadku wynik zostanie również wymuszony na ciąg znaków (przy użyciu zwykłych reguł konwersji tablic → ciągów znaków).


Ps. W szczególnym przypadku, jeśli wiemy, że tablica zawiera między i2 x 2 ielementy, możemy wstawić nowy element xw indeksie (0) za ipomocą i/[x]*(6 znaków). To, co tak naprawdę robi, polega na podzieleniu tablicy na części składające się z maksymalnie ielementów i wstawieniu xmiędzy każdą porcją. Zauważ, że w tym przypadku nawiasy są konieczne, nawet jeśli xjest liczbą.


Pps. Alternatywnym podejściem jest użycie dynamicznie nazwanych zmiennych. Na przykład,

 'foo' 42 ':x'\+~

przypisze wartość 'foo'do zmiennej x42, natomiast

 42 'x'\+~

odzyska to.

Możesz to dalej zoptymalizować, pomijając xprefiks i po prostu przypisując bezpośrednio do literałów liczbowych - jest to całkowicie legalne w GolfScript i pozwala zapisać jeden znak z kodu przypisania i skrócić kod pobierania do po prostu `~(lub w ogóle do niczego, jeśli wskaźnik jest stały!). Wadą jest oczywiście to, że przypisanie literału liczbowego zastąpi jego wartość w dowolnym miejscu w kodzie. Często jednak można uniknąć literałów liczbowych (lub przynajmniej ograniczyć się do początku programu, zanim którykolwiek z nich zostanie ponownie przypisany), w którym to przypadku ta sztuczka jest w porządku.

Ilmari Karonen
źródło
3
Zupełnie nie na temat: gratulacje za 10k! :-D
Klamka
1
Jeśli wiesz, że tablica nie ma zduplikowanych wartości, możesz zastąpić wartość indeksem idla 9 bajtów:.[i=]/[x]*
Martin Ender
5

Ostateczna manipulacja wyjściem

Domyślnie po zakończeniu programu interpreter GolfScript wyświetla wszystko na stosie oraz końcową nową linię, dokładnie tak, jakby Twój program zakończył się następująco:

]puts

Dokumentacja nie wspomina bezpośrednio, że interpreter dosłownie wywołuje wbudowane, putsaby wygenerować takie dane wyjściowe, i że to wbudowane jest dosłownie zdefiniowane jako:

{print n print}:puts;

W ten sposób można stłumić lub manipulować ostateczną wyjście poprzez przedefiniowanie puts, print i / lub n(lub  jeśli czujesz się naprawdę skręcone). Oto kilka przykładów:

Pomiń ostatnią linię:

'':n;

(Oczywiście możesz pominąć, ;jeśli nie przeszkadza ci dodatkowy pusty łańcuch na stosie.)

Całkowicie pomiń efekt końcowy:

:puts

To zastępuje putswszystko, co się dzieje na stosie. Jeśli jest to coś, czego nie chcesz wykonać, możesz użyć np 0:puts;. Zamiast tego. Zauważ, że to również pomija p(które jest zdefiniowane jako {`puts}:p;), ale nadal możesz użyć printwyjścia, jeśli chcesz.

Ilmari Karonen
źródło
I nothingmasz na myśli \n?
CalculatorFeline
Jeśli nie przeszkadza ci końcowy znak nowej linii, możesz także użyć ];do stłumienia końcowego wyniku.
wastl
5

Chciałbym zapytać o najlepsze sposoby: wartość minimalną, maksymalną i bezwzględną. Wszystkie moje rozwiązania wydają się przyjmować znacznie więcej postaci niż powinny. - Claudiu

minimum maksimum

Aby znaleźć najmniejszą / największą wartość w tablicy, po prostu posortuj ją i weź pierwszy / ostatni element:

  • $0= (3 znaki) - minimalny element w łuku
  • $-1= (4 znaki) - maksymalny element w tablicy

Jeśli znasz długość tablicy, która ma 10 elementów lub mniej, możesz znaleźć maksimum w trzech znakach, zastępując -1je indeksem ostatniego elementu.

Jeśli masz wartości na stosie, możesz je najpierw zebrać do tablicy. W tym celu przydatna jest niekiedy sztuczka polegająca na [\]zebraniu dwóch górnych elementów stosu do tablicy, podczas [@]gdy trzech górnych. W ten sposób otrzymujemy:

  • [\]$0= (6 znaków) - minimum dwie wartości na stosie
  • [@]$0= (6 znaków) - minimum trzy wartości na stosie
  • [\]$1= (6 znaków) - maksymalnie dwie wartości na stosie
  • [@]$2= (6 znaków) - maksymalnie trzy wartości na stosie

Tej samej sztuczki można również użyć do znalezienia mediany trzech wartości, które mogą być czasami przydatne:

  • [@]$1= (6 znaków) - mediana trzech wartości na stosie

Oto kolejna potencjalnie użyteczna sztuczka polegająca na znalezieniu min / max dwóch wartości , pozostawiając oryginalne wartości na stosie :

  • .2$>$ (5 znaków) - znajdź minimum dwie wartości na stosie, pozostawiając oryginalne wartości nietknięte
  • .2$<$ (5 znaków) - znajdź maksymalnie dwie wartości na stosie, pozostawiając oryginalne wartości nietknięte

Działa to tak, że .2$klonuje dwa górne elementy na stosie w odwrotnej kolejności (tj. a ba b b a), </ >porównuje kopie i zwraca 0 lub 1, a $następnie skalar kopiuje jedną z dwóch wartości wejściowych w zależności od wyniku porównania.


Jeśli masz na stosie dwie nieujemne liczby całkowite, możesz użyć ,\,&,(5 znaków), aby znaleźć ich minimum i ,\,|,(5 znaków), aby znaleźć ich maksimum. Ta sztuczka używa odpowiednio ustawiania przecięcia i połączenia w zakresach. Możesz zapisać inną postać, jeśli można zastosować ,do każdego argumentu osobno, bez konieczności ich wymiany. Ponieważ ta metoda oblicza zakres dla każdego argumentu, nie jest bardzo wydajna dla większych liczb, ale może być bardzo przydatna dla mniejszych danych wejściowych.

Jeszcze krótszym sposobem na znalezienie minimum dwóch nieujemnych liczb całkowitych na stosie jest ,<,(3 znaki). Niestety, ta sztuczka nie działa w celu znalezienia maksimum.


całkowita wartość

Wbudowany operator GolfScript wartości bezwzględnej to abs(3 znaki). Chociaż jest to o dwa znaki więcej niż wolałbym, ogólnie trudno go pokonać.

W niektórych przypadkach (np. Do sortowania według wartości bezwzględnej) kwadrat liczby może być odpowiednim substytutem jego wartości bezwzględnej; można to obliczyć na dwa znaki, albo 2?albo .*. W ten sposób otrzymujemy:

  • {.*}$0= (7 znaków) - minimalny element według wartości bezwzględnej w tablicy
  • {.*}$-1= (8 znaków) - maksymalny element według wartości bezwzględnej w tablicy

Podobnie, zamiast np. Testowania, czy wartość bezwzględna liczby jest mniejsza niż 3 za pomocą abs 3<(6 znaków, w tym spacja), możesz przetestować, czy jej kwadrat jest mniejszy niż 9 za pomocą .*9<(4 znaki, nie potrzeba spacji).

Ilmari Karonen
źródło
Jeśli masz na stosie dwie nieujemne liczby całkowite, możesz użyć ,\,&,(5 znaków), aby znaleźć ich minimum i ,\,|,(5 znaków), aby znaleźć ich maksimum. Ta sztuczka używa odpowiednio ustawiania przecięcia i połączenia w zakresach. Możesz zapisać inną postać, jeśli można zastosować ,do każdego argumentu osobno, bez konieczności ich wymiany. Ponieważ ta metoda oblicza zakres dla każdego argumentu, nie jest bardzo wydajna dla większych liczb, ale może być bardzo przydatna dla mniejszych danych wejściowych.
KirarinSnow
@KirarinSnow: Dzięki! Dodałem to do odpowiedzi.
Ilmari Karonen
4

Usuwanie duplikatów z tablicy

Operatory zbioru |(zjednoczenie), &(przecięcie) i ^(różnica symetryczna) zwiną wiele elementów tablicy w jeden. Zatem najprostszym sposobem na usunięcie zduplikowanych elementów z tablicy jest połączenie lub połączenie się ze sobą:

.|

lub:

.&

Operatory te będą traktować ciągi znaków jako tablice znaków, więc można ich również użyć do usunięcia zduplikowanych znaków z ciągów znaków.

Ilmari Karonen
źródło
4

Ograniczona transliteracja

Aby odpowiedzieć na konkretne pytanie: w jaki sposób najlepiej wykonać dany ciąg tr? Na przykładtr/ABC/abc/

Jeśli wpłynie to na wszystkie znaki w ciągu, jest to dość łatwe: {'ABC'?'abc'=}%(narzut: 9 znaków).

Jednak łamie się, jeśli niektóre postacie nie są transliterowane i 'ABC'?daje-1 .

Jeśli transliteracja nie jest cykliczna, można wykonać jedną zamianę na raz za pomocą ciągów znaków dzielących i łączących: 'AaBbCc'1/2/{~@@/\*}/(narzut: 15 znaków). Można to poprawić, ale istnieje alternatywne podejście, które jest obecnie lepsze i działa w przypadku transliteracji cyklicznych.

Obecnie najkrótsze ogólne rozwiązania mają narzut 14 znaków:

  • Jedno podejście obejmuje znak ucieczki: gdzie oznacza dosłowny bajt zerowy. (Oczywiście ta metoda nie jest całkowicie ogólna: nie może zamapować żadnego innego znaku na bajt zerowy).{.'ABC'?'abc0'=\or}%0

  • Alternatywnie, {.'ABC'?'abc'@),+=}%ma taki sam narzut, ale używa tylko drukowalnych znaków ASCII. @),+Jest zwinięty (ale, jak widać, najkrótsza) sposobem zapewnienia, że ciąg wymiana zawsze kończy się znakiem wejściowego.

Peter Taylor
źródło
Korzystając z ostatniego podejścia, dla ciągu wejściowego 'ABCDEF'otrzymuję wynik 'abc000', ale właściwy wynik byłby 'abcDEF'. Czy coś brakuje?
Cristian Lupascu,
1
@ w0lf, że 0 jest pogrubione, ponieważ jest to wspomniany wcześniej znak ucieczki - tj. bajt 0.
Peter Taylor
4

Zamień ciąg znaków na tablicę znaków char

Możesz to zrobić, wpisując: 1/ po nim.

Przykład: "String"1/wypycha, aby ułożyć tablicę w stos ['S''t''r''i''n''g'].

Jest to przydatne, gdy chcesz przenosić znaki po łańcuchu.

użytkownik3700847
źródło
1
Czy możesz podać przykład, jak to może być przydatne? Ciągi już działają jak tablice, więc nie wydaje się to przydatne.
Justin,
@Quincunx jest to przydatne, gdy chcesz wyskoczyć z postaci, a nie ich wartość ascii
user3700847
A kiedy chcesz to zrobić?
Justin
5
@Quincunx: Na przykład obracanie łańcucha. "abc"1/(+-> "bca", ale "abc"(+-> bc97.
Dennis,
4

Przypisywanie do literałów liczbowych

Często zamiast pisać, 1:xa następnie używać / aktualizować zmienną x, możesz po prostu użyć i zaktualizować 1bezpośrednio:

1:^;{^.p.+:^;}5*
{1.p.+:1;}5*       (4 bytes shorter)

Oczywiście działa to również w przypadku innych wartości początkowych, ale ulegnie awarii, jeśli ta wartość wystąpi gdziekolwiek indziej w kodzie.

Interpunkcja jako nazwy zmiennych

Jeśli masz do wykorzystania zmiennych, to również często mądry używać znaków interpunkcyjnych, który nie jest już w kodzie - wiele programów można zrobić bez &, |,^ , lub ?. W ten sposób możesz na przykład napisać &nzamiast x nwypychać zmienną, a następnie wypychać nowy wiersz.

Lynn
źródło
3
Jednak niektóre zadania mogą mieć nieoczekiwane skutki uboczne. W szczególności przypisanie !często jest to zły pomysł, ponieważ będzie przerwa ifi do(jak również while, until, and, ori xor). Podobnie,or jest definiowany przez tłumacza jako alias dla1$\if , więc zmianę definicji 1, $lub \również złamać. Przedefiniowanie `przerw p.
Ilmari Karonen,
3

Filtrowanie tablicy

Najbardziej ogólnym sposobem filtrowania tablicy jest użycie { },, która ocenia blok kodu dla każdego elementu tablicy i wybiera te elementy, dla których wynikowa wartość jest prawdziwa (tj. Działa jak grepw Perlu).

Jednak użycie operatora odejmowania tablic -jest często krótsze. Ten operator pobiera dwie tablice i usuwa z pierwszego każdy element występujący w drugiej tablicy. Tak nie jest zmienia się kolejność elementów w pierwszej tablicy lub zwinąć duplikatów. Przydatną sztuczką jest dwukrotne zastosowanie operacji odejmowania w celu uzyskania nie przecinającego się operatora przecięcia tablicy:

  • a b -: usuń wszystkie elementy znalezione w tablicy b z tablicya
  • a. b --: usuń wszystkie elementy, które nie zostały znalezione w tablicy bz tablicya

W szczególności można tego użyć do zliczenia liczby wystąpień elementu w tablicy:

  • a.[c]--,: policz liczbę razy element c występuje w tablicya

Ogólnie rzecz biorąc, ta metoda nie jest optymalna, ponieważ:

  • a[c]/,(: policz liczbę razy element c występuje w tablicya
  • a{c=},,: policz liczbę razy element c występuje w tablicya

jest o jeden znak krótszy (i, jeśli liczenie jest wyłączone o jeden, a[c]/,zapisuje on o jeden znak więcej). Jednak w szczególnym przypadku, gdy cjest liczbą i ajest normalną tablicą (nie ciągiem), nawiasy kwadratowe wokół cmożna pominąć, ponieważ -operator wymusza argumenty na ten sam typ:

  • a.c--,: policz ile razy liczba cwystępuje w tablicy (nie łańcuch!)a

(Jeśli ajest łańcuchem i cjest liczbą od 0 do 9,a.c-- policzy liczbę razy cyfra c występuje w a.)


Podobną sztuczkę można zastosować do znalezienia najczęstszego elementu w tablicy :

:a{a\[.]-,}$0=

Ponownie, jeśli dane wejściowe są tablicą liczb, cała [.]sekwencja może zostać pominięta. Niestety, nie działa to na ciągi bez [.].

Ilmari Karonen
źródło
Do zliczania wystąpień (przypadek ogólny) a[c]/,(i a{c=},,są o jeden bajt krótsze.
Dennis
@Dennis: Dzięki! Zredagowałem to w.
Ilmari Karonen
3

Czytaj ze STDIN

GolfScript może czytać ze standardowego:

"#{STDIN.read}"

To będzie kontynuować czytanie od STDIN aż do osiągnięcia EOF. Alternatywnie:

"#{STDIN.gets}"

lub

"#{STDIN.readline}"

Inne dostępne rzeczy:

getbyte
getc
gets([sep])
gets(limit)
gets(sep, limit)
inspect # perhaps useful for an underhanded contest
isatty
read([length])
readbyte
readchar
readline([sep])
readline(limit)
readline(sep, limit)
readlines([sep])
readlines(limit)
readlines(sep, limit)
readpartial(maxlen [, outbuf])

Dla każdego z nich można ich użyć tylko raz (i także raz przy każdej zmianie parametru, także jeszcze raz z pustymi nawiasami); potem oryginalna wartość będzie tym, co otrzymasz zamiast nowej wartości.

Justin
źródło
2
Możesz dodać uwagę, {"#{STDIN.readline}"p}2*która nie odczytuje 2 wierszy, ale zamiast tego łańcuch jest oceniany tylko raz.
Howard
2
Jeśli zainicjujesz idowolną liczbę całkowitą, '"#{'i):i';STDIN.gets}"'++~da ona inny wynik za każdym razem, gdy będzie ona oceniana. Warto również wspomnieć o backtickach. Jeśli założymy Linuksa, możemy użyć np. `head -1`Zamiast STDIN.gets.
Dennis
@Dennis: "#{var'g','gpush Gstring.new(STDIN.gets)'.cc}";pozwala także zdefiniować nowego operatora GolfScript, g który odczytuje linię ze standardowego wejścia i wypycha ją na stos.
Ilmari Karonen,
2

Dekodowanie danych szesnastkowych

GolfScript nie ma liter szesnastkowych liczb całkowitych, więc niestety nie można po prostu parsować danych szesnastkowych ~ . Zamiast tego, jeśli twój kod musi pobierać dane szesnastkowe, musisz go przeanalizować ręcznie.

Ta 8-znakowa pętla zastosowana do łańcucha przekształci małe cyfry szesnastkowe na ich numeryczne odpowiedniki:

{39%9-}%

Jeśli musisz (również) zaakceptować wielkie cyfry szesnastkowe, najłatwiejszym (i prawdopodobnie najkrótszym) rozwiązaniem jest najpierw użycie małych liter 32|, w sumie 11 znaków:

{32|39%9-}%

Zauważ, że wyjściowo technicznie nadal będzie to ciąg znaków (składający się ze znaków ASCII 0–15), ale większość funkcji tablicy GolfScript również akceptuje ciągi znaków. Jeśli absolutnie potrzebujesz tablicy, zawsze możesz jej użyć [{39%9-}/](gdzie pierwsza[ jest opcjonalna, jeśli stos jest inaczej pusty).

Aby przekonwertować wynik powyższego kodu na liczbę całkowitą, możesz po prostu użyć 16base(6 znaków). Jeśli zamiast tego chcesz tablicę bajtów, najkrótszym rozwiązaniem, jakie znalazłem, było po prostu zdekodowanie każdej pary cyfr szesnastkowych za pomocą 2/{16base}%(11 znaków). Podsumowując, najkrótszy kod, który znalazłem, aby przekształcić ciąg szesnastkowy w tablicę bajtów, to 8 + 11 = 19 znaków:

{39%9-}%2/{16base}%

Zauważ, że wyjście tego kodu jest rzeczywiście tablicą, a nie łańcuchem. W razie potrzeby można go stringify przez złączenie go np ""+, a jeśli nie masz nic przeciwko dodatkowej nowej linii na końcu n+.

Ilmari Karonen
źródło
2

Definiowanie nowych wbudowanych operatorów

Standardowy interpreter GolfScript ma rzadko używaną funkcję która umożliwia interpolację kodu Ruby w literałach łańcuchowych z podwójnym cudzysłowem.

Jednym z powodów, dla których ta funkcja nie jest częściej używana, jest to, że niezręcznie interpolowany kod jest wykonywany w czasie kompilacji , a dane wyjściowe są buforowane przez interpreter GolfScript, dzięki czemu ten sam literał łańcuchowy zawsze będzie dawał tę samą wartość, nawet wewnątrz ciąg znaków eval.

Jednak okazuje się, że ta funkcja jest dobra dla definiowania nowych operatorów GolfScript zaimplementowanych w kodzie Ruby. Na przykład, oto jak zdefiniować nowy operator dodawania binarnego, który działa tak jak standardowy wbudowany +operator:

"#{var'add','gpush a+b'.cc2}";

Tak naprawdę nie ma znaczenia, gdzie umieścisz definicję w kodzie; nowy operator zostaje zdefiniowany, gdy tylko parsowany jest ciąg cudzysłowu zawierający kod Ruby. addOperator zdefiniowany powyżej działa dokładnie jak wbudowana +operatora i mogą być stosowane w taki sam sposób:

1 2 add          # evaluates to 3
"foo" "bar" add  # evaluates to "foobar"

Oczywiście, zdefiniowanie nowego operatora dodawania jest dość bezużyteczne, chyba że zrobiłeś coś głupiego jak wymazanie wbudowanego +operatora . Ale możesz użyć tej samej sztuczki, aby zdefiniować nowe operatory, które robią rzeczy, których Golfscript nie może (łatwo) robić natywnie, na przykład równomiernie tasuje tablicę:

"#{var'shuf','gpush a.factory(a.val.shuffle)'.cc1}";

10,shuf          # evaluates to 0,1,2,...,9 in random order

lub drukowanie zawartości całego stosu:

"#{var'debug','puts Garray.new($stack).ginspect'.cc}";

4,) ["foo" debug  # prints ["" [0 1 2] 3 "foo"], leaving the stack untouched

lub interaktywne wejście:

"#{var'gets','gpush Gstring.new(STDIN.gets)'.cc}";

]; { "> " print gets ~ ]p 1 } do   # simple GolfScript REPL

a nawet dostęp do sieci:

"#{
  require 'net/http'
  require 'uri'
  var'get','gpush Gstring.new(Net::HTTP.get_response(URI.parse(a.to_s)).body)'.cc1
}";

"http://example.com" get

Oczywiście nieco bardziej golfową (i bardziej ryzykowną!) Implementacją tego drugiego byłoby np .:

"#{var'get','gpush Gstring.new(`curl -s #{a}`)'.cc1}";

Chociaż sama w sobie nie jest golfistą, pozwala to rozszerzyć możliwości GolfScript poza to, co zapewniają wbudowane polecenia.


Jak to działa?

Autorytatywnym odniesieniem do sposobu definiowania nowych operatorów GolfScript w ten sposób jest oczywiście kod źródłowy interpretera . To powiedziawszy, oto kilka krótkich wskazówek:

  • Aby zdefiniować nowego operatora, namektóry uruchamia kod Ruby code, użyj:

    var'name','code'.cc
  • Wewnątrz kodu użyj, gpopaby odczytać wartość ze stosu i gpushwypchnąć ją z powrotem. Możesz również uzyskać dostęp do stosu bezpośrednio przez tablicę $stack. Na przykład, aby wcisnąć oba ai bna stos, jest bardziej golfisty $stack<<a<<bniż gpush a;gpush b.

    • Pozycje [markerów początkowych tablicy są przechowywane w $lbtablicy. gpopFunkcja dba o dostosowanie tych markerów w dół, gdy psychiatrzy stosu poniżej ich pozycji, ale manipulowanie $stacktablicę bezpośrednio nie.
  • Metoda .ccciągów, która kompiluje kod Ruby w ciągu znaków do operatora GolfScript, jest jedynie wygodnym rozwiązaniem Gblock.new(). Posiada również warianty .cc1, .cc2a .cc3które sprawiają, że operator automatycznie pop 1, 2 lub 3 argumenty ze stosu i przypisać je do zmiennych a, bi c. Istnieje również .ordermetoda, która działa podobnie .cc2, ale automatycznie sortuje argumenty według priorytetu typu .

  • Wszystkie wartości na stosie GolfScript są (i powinny być!) Obiektów typu Gint, Garray, Gstringlub Gblock. Dostęp do podstawowej natywnej liczby całkowitej lub tablicy, w razie potrzeby, można uzyskać za pomocą tej .valmetody.

    • Zauważ jednak, że Gstring.valzwraca tablicę Gints! Aby zamienić Gstringnatywny ciąg Ruby, wywołaj .to_sgo zamiast tego (lub użyj go w kontekście, który robi to automatycznie, np. Interpolacja łańcucha). Wywołanie .to_gsdowolnej wartości GS zamienia ją w a Gstring, więc dowolną wartość GS można powiązać .to_gs.to_s.
  • Ta gpushfunkcja nie automatycznie zawija natywne liczby Ruby, ciągi znaków lub tablice w odpowiednie typy GS, więc często będziesz musiał zrobić to sam, jawnie wywołując np Gstring.new(). Jeśli wypchniesz na stos coś innego niż jeden z typów wartości GS, dowolny kod, który później spróbuje nim manipulować, może ulec awarii.

  • Typy wartości GS mają również .factorymetodę wywołującą konstruktor typu, co może być przydatne np. Do ponownego pakowania tablic / ciągów po manipulowaniu ich zawartością. Wszystkie typy mają również .coercemetodę, która wykonuje wymuszenie typu : a.coerce(b)zwraca parę zawierającą ai bwymuszoną na ten sam typ.

Ilmari Karonen
źródło