Визначення нових вбудованих операторів
Стандартний інтерпретатор 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
на стек, це робити гольфіст, $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
Функція не автоматичне обруча рідні номери Ruby, рядки або масиви в типах відповідних GS, так що вам часто доводиться робити це самостійно явним викликом , наприклад Gstring.new()
. Якщо ви натиснете на стек що-небудь, крім одного з типів значень GS, будь-який код, який пізніше намагається маніпулювати ним, може зірватися.
Типи значень GS також мають .factory
метод, який викликає конструктор типу, який може бути корисним, наприклад, для перемотування масивів / рядків після маніпулювання їх вмістом. Усі типи також мають .coerce
метод, який виконує примус типу : a.coerce(b)
повертає пару, що містить a
і b
примушує до одного типу.
... x
на... [x]
? Найкраще, що я бачу, - це[.;]
.