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. add
Operator 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, name
który uruchamia kod Ruby code
, użyj:
var'name','code'.cc
Wewnątrz kodu użyj, gpop
aby odczytać wartość ze stosu i gpush
wypchnąć ją z powrotem. Możesz również uzyskać dostęp do stosu bezpośrednio przez tablicę $stack
. Na przykład, aby wcisnąć oba a
i b
na stos, jest bardziej golfisty $stack<<a<<b
niż gpush a;gpush b
.
- Pozycje
[
markerów początkowych tablicy są przechowywane w $lb
tablicy. gpop
Funkcja dba o dostosowanie tych markerów w dół, gdy psychiatrzy stosu poniżej ich pozycji, ale manipulowanie $stack
tablicę bezpośrednio nie.
Metoda .cc
cią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
, .cc2
a .cc3
które sprawiają, że operator automatycznie pop 1, 2 lub 3 argumenty ze stosu i przypisać je do zmiennych a
, b
i c
. Istnieje również .order
metoda, 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
, Gstring
lub Gblock
. Dostęp do podstawowej natywnej liczby całkowitej lub tablicy, w razie potrzeby, można uzyskać za pomocą tej .val
metody.
- Zauważ jednak, że
Gstring.val
zwraca tablicę Gint
s! Aby zamienić Gstring
natywny ciąg Ruby, wywołaj .to_s
go zamiast tego (lub użyj go w kontekście, który robi to automatycznie, np. Interpolacja łańcucha). Wywołanie .to_gs
dowolnej wartości GS zamienia ją w a Gstring
, więc dowolną wartość GS można powiązać .to_gs.to_s
.
Ta gpush
funkcja 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ż .factory
metodę 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ż .coerce
metodę, która wykonuje wymuszenie typu : a.coerce(b)
zwraca parę zawierającą a
i b
wymuszoną na ten sam typ.
... x
się... [x]
? Najlepsze, co mogę zobaczyć, to[.;]
.