Визначення нових вбудованих операторів
Стандартний інтерпретатор 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повертає масив Gints! Щоб перетворити 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]? Найкраще, що я бачу, - це[.;].