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.
... xsię... [x]? Najlepsze, co mogę zobaczyć, to[.;].