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 add
operador 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 name
que execute o código Ruby code
, use:
var'name','code'.cc
Dentro do código, use gpop
para ler um valor da pilha e gpush
empurrá-lo de volta. Você também pode acessar a pilha diretamente através da matriz $stack
. Por exemplo, para empurrar ambos a
e b
para a pilha, é Golfier para fazer $stack<<a<<b
do que gpush a;gpush b
.
- As posições dos
[
marcadores de início da matriz são armazenadas na $lb
matriz. A gpop
função cuida de ajustar esses marcadores para baixo se a pilha encolher abaixo de sua posição, mas manipular a $stack
matriz diretamente não.
O .cc
mé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
, .cc2
e .cc3
que fazem o operador automaticamente pop 1, 2 ou 3 argumentos na pilha e atribuí-los às variáveis a
, b
e c
. Há também um .order
mé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
, Gstring
ou Gblock
. O número inteiro nativo ou matriz subjacente, quando necessário, pode ser acessado por meio do .val
método
- No entanto, observe que
Gstring.val
retorna uma matriz de Gint
s! Para transformar uma Gstring
em 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_gs
de qualquer valor de GS o transforma em um Gstring
, para que qualquer valor de GS possa ser especificado .to_gs.to_s
.
A gpush
funçã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 .factory
mé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 .coerce
método que executa coerção de tipo :a.coerce(b)
retorna um par contendo a
e b
coagido para o mesmo tipo.
... x
em... [x]
? O melhor que posso ver é[.;]
.