Definindo novos operadores internos
O intérprete GolfScript padrão possui um recurso raramente usado que permite o código Ruby interpolado em literais de string com aspas duplas.
Uma razão pela qual esse recurso não é mais comumente usado é que, desajeitadamente, o código interpolado é executado em tempo de compilação e a saída é armazenada em cache pelo intérprete GolfScript, para que a mesma sequência literal sempre produza o mesmo valor, mesmo dentro avaliação de string.
No entanto, uma coisa para a qual esse recurso é bom é definir novos operadores GolfScript implementados no código Ruby. Por exemplo, veja como definir um novo operador de adição binária que funciona exatamente como o +operador interno padrão :
"#{var'add','gpush a+b'.cc2}";
Realmente não importa onde você coloca a definição no seu código; o novo operador é definido assim que a string de aspas duplas que contém o código Ruby é analisada. O addoperador definido acima funciona exatamente como o +operador interno e pode ser usado exatamente da mesma maneira:
1 2 add # evaluates to 3
"foo" "bar" add # evaluates to "foobar"
Obviamente, definir um novo operador de adição é inútil, a menos que você tenha feito algo bobo como apagar o +operador interno . Mas você pode usar o mesmo truque para definir novos operadores que fazem coisas que o Golfscript não pode (facilmente) fazer de forma nativa, como, por exemplo, embaralhar uniformemente uma matriz:
"#{var'shuf','gpush a.factory(a.val.shuffle)'.cc1}";
10,shuf # evaluates to 0,1,2,...,9 in random order
ou imprimir o conteúdo de toda a pilha:
"#{var'debug','puts Garray.new($stack).ginspect'.cc}";
4,) ["foo" debug # prints ["" [0 1 2] 3 "foo"], leaving the stack untouched
ou entrada interativa:
"#{var'gets','gpush Gstring.new(STDIN.gets)'.cc}";
]; { "> " print gets ~ ]p 1 } do # simple GolfScript REPL
ou mesmo acesso à 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
Obviamente, uma implementação um pouco mais arriscada (e mais arriscada!) Deste último seria, por exemplo:
"#{var'get','gpush Gstring.new(`curl -s #{a}`)'.cc1}";
Embora não seja particularmente um jogador de golfe, isso permite ampliar os recursos do GolfScript além do que os comandos internos fornecem.
Como funciona?
A referência autorizada sobre como definir novos operadores GolfScript dessa maneira é, obviamente, o código fonte do intérprete . Dito isto, aqui estão algumas dicas rápidas:
Para definir um novo operador nameque execute o código Ruby code, use:
var'name','code'.cc
Dentro do código, use gpoppara ler um valor da pilha e gpushempurrá-lo de volta. Você também pode acessar a pilha diretamente através da matriz $stack. Por exemplo, para empurrar ambos ae bpara a pilha, é Golfier para fazer $stack<<a<<bdo que gpush a;gpush b.
- As posições dos
[marcadores de início da matriz são armazenadas na $lbmatriz. A gpopfunção cuida de ajustar esses marcadores para baixo se a pilha encolher abaixo de sua posição, mas manipular a $stackmatriz diretamente não.
O .ccmétodo string que compila o código Ruby em uma string em um operador GolfScript é apenas um invólucro de conveniência Gblock.new(). Tem também as variantes .cc1, .cc2e .cc3que fazem o operador automaticamente pop 1, 2 ou 3 argumentos na pilha e atribuí-los às variáveis a, be c. Há também um .ordermétodo que funciona assim .cc2, exceto que ele classifica automaticamente os argumentos por tipo de prioridade .
Todos os valores na pilha GolfScript são (e devem ser!) Objetos do tipo Gint, Garray, Gstringou Gblock. O número inteiro nativo ou matriz subjacente, quando necessário, pode ser acessado por meio do .valmétodo
- No entanto, observe que
Gstring.valretorna uma matriz de Gints! Para transformar uma Gstringem uma sequência Ruby nativa, chame .to_s-a (ou use-a em um contexto que faça isso automaticamente, como interpolação de sequência). A chamada .to_gsde qualquer valor de GS o transforma em um Gstring, para que qualquer valor de GS possa ser especificado .to_gs.to_s.
A gpushfunção não quebra automaticamente os números, seqüências de caracteres ou matrizes Ruby nativas nos tipos GS correspondentes; portanto, você terá que fazer isso sozinho chamando explicitamente, por exemplo Gstring.new(). Se você colocar algo diferente de um dos tipos de valor GS na pilha, qualquer código que posteriormente tente manipulá-lo provavelmente trava.
Os tipos de valor GS também possuem um .factorymétodo que chama o construtor do tipo, que pode ser útil, por exemplo, para reorganizar matrizes / seqüências de caracteres após manipular seu conteúdo. Todos os tipos também têm um .coercemétodo que executa coerção de tipo :a.coerce(b) retorna um par contendo ae bcoagido para o mesmo tipo.
... xem... [x]? O melhor que posso ver é[.;].