Definizione di nuovi operatori integrati
L'interprete GolfScript standard ha una funzione usata raramente che consente il codice Ruby interpolato in valori letterali di stringa tra virgolette doppie.
Uno dei motivi per cui questa funzione non è più comunemente usata è che, a disagio, il codice interpolato viene eseguito in fase di compilazione e l'output viene memorizzato nella cache dall'interprete GolfScript in modo che la stessa stringa letterale da allora in poi produrrà sempre lo stesso valore, anche all'interno valutazione delle stringhe.
Tuttavia, una cosa per cui questa funzionalità risulta utile è la definizione di nuovi operatori GolfScript implementati nel codice Ruby. Ad esempio, ecco come definire un nuovo operatore di aggiunta binaria che funziona esattamente come l' +
operatore integrato standard :
"#{var'add','gpush a+b'.cc2}";
Non importa dove metti la definizione nel tuo codice; il nuovo operatore viene definito non appena viene analizzata la stringa tra virgolette doppie contenente il codice Ruby. L' add
operatore sopra definito funziona esattamente come l' +
operatore integrato e può essere utilizzato esattamente allo stesso modo:
1 2 add # evaluates to 3
"foo" "bar" add # evaluates to "foobar"
Naturalmente, definire un nuovo operatore addizione è abbastanza inutile, a meno che tu non abbia fatto qualcosa di stupido come cancellare l' +
operatore incorporato . Ma puoi usare lo stesso trucco per definire nuovi operatori che fanno cose che Golfscript non può (facilmente) fare nativamente come, per esempio, mescolare uniformemente un array:
"#{var'shuf','gpush a.factory(a.val.shuffle)'.cc1}";
10,shuf # evaluates to 0,1,2,...,9 in random order
o stampando il contenuto dell'intera pila:
"#{var'debug','puts Garray.new($stack).ginspect'.cc}";
4,) ["foo" debug # prints ["" [0 1 2] 3 "foo"], leaving the stack untouched
o input interattivo:
"#{var'gets','gpush Gstring.new(STDIN.gets)'.cc}";
]; { "> " print gets ~ ]p 1 } do # simple GolfScript REPL
o persino l'accesso al web:
"#{
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
Naturalmente, un'implementazione un po 'più golfosa (e più rischiosa!) Di quest'ultima sarebbe ad esempio:
"#{var'get','gpush Gstring.new(`curl -s #{a}`)'.cc1}";
Sebbene non sia particolarmente golfoso in sé, ciò consente di estendere le capacità di GolfScript oltre a ciò che offrono i comandi integrati.
Come funziona?
Il riferimento autorevole su come definire i nuovi operatori GolfScript in questo modo è, ovviamente, il codice sorgente per l'interprete . Detto questo, ecco alcuni suggerimenti rapidi:
Per definire un nuovo operatore name
che esegue il codice Ruby code
, utilizzare:
var'name','code'.cc
All'interno del codice, utilizzare gpop
per leggere un valore dallo stack e gpush
per reinserirne uno. È inoltre possibile accedere allo stack direttamente tramite l'array $stack
. Ad esempio, per spingere entrambi a
e b
nello stack, è più golfoso di $stack<<a<<b
quello gpush a;gpush b
.
- Le posizioni dei
[
marker di inizio dell'array sono memorizzate $lb
nell'array. La gpop
funzione si occupa di regolare questi marker verso il basso se lo stack si riduce al di sotto della loro posizione, ma la manipolazione $stack
diretta dell'array no.
Il .cc
metodo stringa che compila il codice Ruby in una stringa in un operatore GolfScript è solo un comodo wrapper Gblock.new()
. Ha anche le varianti .cc1
, .cc2
e .cc3
che rendono l'operatore pop automaticamente 1, 2 o 3 argomenti dallo stack e li assegna alle variabili a
, b
e c
. C'è anche un .order
metodo che funziona come .cc2
, tranne che ordina automaticamente gli argomenti per tipo di priorità .
Tutti i valori sullo stack GolfScript sono (e dovrebbe essere!) Oggetti di tipo Gint
, Garray
, Gstring
o Gblock
. L'intero o l'array nativo sottostante, ove necessario, è accessibile tramite il .val
metodo.
- Tuttavia, si noti che
Gstring.val
restituisce una matrice di Gint
s! Per trasformare a Gstring
in una stringa Ruby nativa, chiamala .to_s
invece (o usala in un contesto che lo fa automaticamente, come l'interpolazione di stringhe). La chiamata .to_gs
a qualsiasi valore GS lo trasforma in a Gstring
, quindi è possibile stringere qualsiasi valore GS .to_gs.to_s
.
La gpush
funzione non avvolge automaticamente numeri, stringhe o matrici di Ruby nativi nei corrispondenti tipi GS, quindi spesso dovrai farlo tu stesso chiamando esplicitamente ad es Gstring.new()
. Se si inserisce nello stack qualcosa di diverso da uno dei tipi di valore GS, è probabile che si verifichi un arresto anomalo di qualsiasi codice che successivamente tenta di manipolarlo.
I tipi di valore GS hanno anche un .factory
metodo che chiama il costruttore del tipo, che può essere utile, ad esempio, per riavvolgere matrici / stringhe dopo averne manipolato il contenuto. Tutti i tipi hanno anche un .coerce
metodo che esegue la coercizione del tipo : a.coerce(b)
restituisce una coppia contenente a
e b
costretta allo stesso tipo.
... x
in... [x]
? Il meglio che posso vedere è[.;]
.