Определение новых встроенных операторов
Стандартный интерпретатор GolfScript имеет редко используемую функцию которая позволяет интерполировать код Ruby в строковых литералах в двойных кавычках.
Одна из причин, почему эта функция не используется чаще, состоит в том, что, к сожалению, интерполированный код выполняется во время компиляции , а выходные данные кэшируются интерпретатором GolfScript, так что один и тот же строковый литерал после этого всегда будет давать одно и то же значение, даже внутри Строка Eval.
Однако одна вещь, для которой эта функция полезна, - это определение новых операторов GolfScript, реализованных в коде Ruby. Например, вот как определить новый оператор двоичного сложения, который работает так же, как стандартный встроенный +
оператор:
"#{var'add','gpush a+b'.cc2}";
Неважно, где вы поместите определение в своем коде; новый оператор определяется, как только разбирается строка в двойных кавычках, содержащая код Ruby. add
Оператор , определенный выше работ точно , как встроенный +
оператор, и может быть использован точно таким же образом:
1 2 add # evaluates to 3
"foo" "bar" add # evaluates to "foobar"
Конечно, определение нового оператора сложения довольно бесполезно, если только вы не сделали что-то глупое, например, стерли встроенный+
оператор . Но вы можете использовать тот же прием для определения новых операторов, которые делают вещи, которые Golfscript не может (легко) сделать изначально, например, например, равномерно перемешивая массив:
"#{var'shuf','gpush a.factory(a.val.shuffle)'.cc1}";
10,shuf # evaluates to 0,1,2,...,9 in random order
или распечатать содержимое всего стека:
"#{var'debug','puts Garray.new($stack).ginspect'.cc}";
4,) ["foo" debug # prints ["" [0 1 2] 3 "foo"], leaving the stack untouched
или интерактивный ввод:
"#{var'gets','gpush Gstring.new(STDIN.gets)'.cc}";
]; { "> " print gets ~ ]p 1 } do # simple GolfScript REPL
или даже веб-доступ:
"#{
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
Конечно, несколько более подходящая (и более рискованная!) Реализация последней будет, например:
"#{var'get','gpush Gstring.new(`curl -s #{a}`)'.cc1}";
Несмотря на то, что сам по себе гольф не является особенным, это позволяет расширить возможности GolfScript за пределы возможностей встроенных команд.
Как это работает?
Авторитетная справка о том, как определить новые операторы GolfScript таким образом, является, конечно, исходным кодом для интерпретатора . Тем не менее, вот несколько быстрых советов:
Чтобы определить новый оператор, name
который запускает код Ruby code
, используйте:
var'name','code'.cc
Внутри кода используйте, gpop
чтобы прочитать значение из стека и gpush
вставить его обратно. Вы также можете получить доступ к стеку напрямую через массив $stack
. Например, чтобы подтолкнуть обе a
и b
на стек, это golfier делать $stack<<a<<b
чем gpush a;gpush b
.
- Позиции
[
маркеров начала массива сохраняются в $lb
массиве. gpop
Функция заботится о настройке этих маркеров вниз , если стек психиатры ниже своего положения, но манипулируя $stack
массив непосредственно не делает.
.cc
Строковый метод , который компилирует код на Ruby в строке в оператор GolfScript просто удобство обертка Gblock.new()
. Она также имеет варианты .cc1
, .cc2
и .cc3
что делает оператор автоматически выскочит 1, 2 или 3 -х аргументов из стека и назначить их переменным a
, b
и c
. Есть также .order
метод, который работает аналогично .cc2
, за исключением того, что он автоматически сортирует аргументы по приоритету типа .
Все значения в стеке GolfScript являются (и должны быть!) Объекты типа Gint
, Garray
, Gstring
или Gblock
. Базовое целое число или массив, где это необходимо, могут быть доступны через.val
метода.
- Однако обратите внимание, что
Gstring.val
возвращает массив Gint
s! Чтобы превратить a Gstring
в собственную строку Ruby, .to_s
вместо этого вызовите ее (или используйте ее в контексте, который делает это автоматически, например, при интерполяции строк). Вызов .to_gs
любого значения GS превращает его в a Gstring
, поэтому любое значение GS может быть зачеркнуто .to_gs.to_s
.
gpush
Функция не автоматически завернуть родные Рубиновые числа, строки или массивы в соответствующие типы GS, так что вы часто должны делать это сами явным вызовом , например ,Gstring.new()
. Если вы поместите в стек что-либо, кроме одного из типов значений GS, любой код, который позже попытается манипулировать им, может привести к сбою.
Типы значений GS также имеют .factory
метод, который вызывает конструктор типа, который может быть полезен, например, для переупаковки массивов / строк после манипулирования их содержимым. У всех типов также есть .coerce
метод, который выполняет приведение типов : a.coerce(b)
возвращает пару, содержащую a
и приведенную b
к одному и тому же типу.
... x
в... [x]
? Лучшее, что я могу видеть, это[.;]
.