англійськафранцузькаіспанська

Значок OnWorks

PDL::PPp - онлайн у хмарі

Запустіть PDL::PPp у постачальника безкоштовного хостингу OnWorks через Ubuntu Online, Fedora Online, онлайн-емулятор Windows або онлайн-емулятор MAC OS

Це команда PDL::PPp, яку можна запустити в постачальнику безкоштовного хостингу OnWorks за допомогою однієї з наших численних безкоштовних робочих станцій, таких як Ubuntu Online, Fedora Online, онлайн-емулятор Windows або онлайн-емулятор MAC OS.

ПРОГРАМА:

ІМ'Я


PDL::PP - генерувати підпрограми PDL з стислих описів

СИНТАКСИС


наприклад

pp_def(
'сумовер',
Pars => 'a(n); [o]b();',
Код => q{
подвійний tmp=0;
цикл(n) %{
tmp += $a();
%}
$b() = tmp;
},
);

pp_done();

ФУНКЦІЇ


Ось короткий довідковий список функцій, наданих PDL::PP.

pp_add_boot
Додайте код до розділу BOOT згенерованого файлу XS

pp_add_exported
Додайте функції до списку експортованих функцій

pp_add_isa
Додайте записи до списку @ISA

pp_addbegin
Встановлює код для додавання у верхній частині створеного файлу .pm

pp_addhdr
Додайте код і включите в розділ C згенерованого файлу XS

pp_addpm
Додайте код до згенерованого файлу .pm

pp_addxs
Додайте додатковий код XS до згенерованого файлу XS

pp_beginwrap
Додайте обгортання блоків BEGIN до коду для згенерованого файлу .pm

pp_bless
Встановлює пакет, до якого додається код XS (за замовчуванням – PDL)

pp_boundscheck
Контроль стану активності перевірки меж PDL

pp_core_importList
Вкажіть, що імпортується з PDL::Core

pp_def
Визначте нову функцію PDL

pp_deprecate_module
Додайте попередження про час виконання та POD про те, що модуль застарів

pp_done
Позначте кінець визначень PDL::PP у файлі

pp_export_nothing
Очистіть список експорту для згенерованого модуля

pp_line_numbers
Додайте інформацію про номер рядка, щоб спростити налагодження коду PDL::PP

pp_setversion
Установіть версію для файлів .pm і .xs

ОГЛЯД


Навіщо нам ПП? Кілька причин: по-перше, ми хочемо мати можливість генерувати підпрограму
код для кожного з типів даних PDL (PDL_Byte, PDL_Short тощо). АВТОМАТИЧНО. по-друге,
коли йдеться про фрагменти PDL-масивів у Perl (наприклад, "$a->slice('0:10:2,:')" або інші
такі речі, як транспонування) приємно мати можливість робити це прозоро і мати можливість
зробити це "на місці" - тобто не потрібно робити копію розділу в пам'яті. ПП ручки
всі необхідні елементи та арифметика зміщення для вас. Існують також поняття про
потокова обробка (повторний виклик однієї і тієї ж процедури для кількох фрагментів, див. PDL::Індексування)
та потоку даних (див. PDL::Dataflow), який дозволяє використання PP.

У більшій частині того, що буде далі, ми будемо припускати знайомство читача з поняттями
неявні та явні маніпуляції з потоками й індексами в PDL. Якщо у вас ще немає
чули про ці поняття або не дуже влаштовують їх, настав час перевірити
PDL::Індексування.

Як ви можете зрозуміти з його назви, PDL::PP є попереднім процесором, тобто він розширює код за допомогою
заміни для створення реального C-коду. Технічно на виході є код XS (див perlxs) але
що дуже близько до C.

Отже, як ви використовуєте PP? Здебільшого ви просто пишете звичайний код C, за винятком
спеціальні конструкції PP, які мають вигляд:

$щось (щось інше)

або:

функція PP %{

%}

Найважливішою конструкцією PP є форма "$array()". Розглянемо дуже простий ПП
функція для підсумовування елементів одновимірного вектора (насправді це дуже схоже на фактичний
код, який використовує 'sumover'):

pp_def('sumit',
Pars => 'a(n); [o]b();',
Код => q{
подвійний tmp;
tmp = 0;
цикл(n) %{
tmp += $a();
%}
$b() = tmp;
}
);

Що відбувається? Рядок "Pars =>" дуже важливий для PP - він визначає всі
аргументи та їх розмірність. Ми називаємо це підпис функції PP (пор
також пояснення в PDL::Indexing). У цьому випадку програма приймає одновимірну функцію
вхідний і повертає 0-D скаляр як вихід. Для доступу використовується конструкція PP "$a()".
елементи масиву a(n) для вас - PP заповнює весь необхідний код C.

Ви помітите, що ми використовуємо оператор одинарних лапок "q{}". Це не
нещасний випадок. Зазвичай ви хочете використовувати одинарні лапки для позначення розділів коду PP. PDL::PP
використовує "$var()" для свого аналізу, і якщо ви не використовуєте одинарні лапки, Perl спробує
інтерполювати "$var()". Крім того, використання оператора "q" з одинарними лапками з фігурними дужками робить це
Схоже, ви створюєте блок коду, що ви маєте на увазі. (Perl достатньо розумний, щоб
шукайте вкладені фігурні дужки і не закривайте цитату, поки вона не знайде відповідну фігурну дужку
дужку, тому безпечно мати вкладені блоки.) За інших обставин, наприклад, коли ви
зшивання блоку коду за допомогою конкатенації рядків, часто найпростіше використовувати
справжні одинарні лапки як

Код => 'щось'.$interpolatable.'somethingelse;'

У простому випадку, коли доступ до всіх елементів здійснюється, конструкція PP "loop(n) %{ ...
%}" використовується для перебору всіх елементів у розмірі "n". Зверніть увагу на цю особливість PP: ALL
РОЗМІРИ ВКАЗАНІ ЗА НАЗВОЮ.

Це стає зрозумілішим, якщо ми уникаємо ПП цикл () побудувати та записати цикл явно
використовуючи звичайний C:

pp_def('sumit',
Pars => 'a(n); [o]b();',
Код => q{
int i,n_size;
подвійний tmp;
n_size = $SIZE(n);
tmp = 0;
for(i=0; i
tmp += $a(n=>i);
}
$b() = tmp;
},
);

який робить те ж саме, що і раніше, але більш довготривалий. Ви можете побачити, щоб отримати елемент "i".
a() ми говоримо "$a(n=>i)" - ми вказуємо розмір за назвою "n". У 2D ми можемо сказати:

Pars=>'a(m,n);',
...
tmp += $a(m=>i,n=>j);
...

Синтаксис "m=>i" запозичується з хешів Perl, які фактично використовуються в реалізації
пп. Можна також сказати "$a(n=>j,m=>i)", оскільки порядок не важливий.

Ви також можете побачити у наведеному вище прикладі використання іншої конструкції PP - $SIZE(n) для отримання
довжина розміру «n».

Однак слід зазначити, що ви не повинні писати явний цикл C, коли це можливо
використовували конструкцію "цикл" PP, оскільки PDL::PP автоматично перевіряє обмеження циклу для
Ви, використання "циклу" робить код більш лаконічним тощо. Але, безперечно, бувають ситуації
де вам потрібен явний контроль циклу, і тепер ви знаєте, як це зробити ;).

Щоб повернутися до розділу "Чому PP?" - наведений вище код для sumit() буде створено для кожного типу даних. Це
працюватиме з фрагментами масивів «на місці». Це буде автоматично - наприклад, якщо 2D
надано масив, він буде викликатися повторно для кожного 1D рядка (знову перевірте PDL::Indexing для
деталі різьблення). І тоді b() буде одновимірним масивом сум кожного рядка. Ми могли б
замість цього викличте його за допомогою $a->xchg(0,1), щоб підсумувати стовпці. І трасування потоку даних тощо буде
в наявності.

Ви можете бачити, що PP рятує програміста від написання великої кількості непотрібно повторюваного C-коду --
на нашу думку, це одна з найкращих можливостей PDL, що дозволяє писати нові підпрограми C
для PDL дивовижно лаконічна вправа. Друга причина - це можливість змусити PP розширитися
ваші стислі визначення коду в інший код C на основі потреб комп’ютера
архітектура під питанням. Уявіть, наприклад, вам пощастило мати суперкомп’ютер
твої руки; у цьому випадку ви хочете, щоб PDL::PP безумовно генерував код, який використовує переваги
функцій векторизації/паралельних обчислень вашої машини (це проект для
майбутнє). У будь-якому випадку, суть полягає в тому, що ваш незмінний код все одно має розширюватися до
робочий код XS, навіть якщо змінилися внутрішні елементи PDL.

Крім того, оскільки ви генеруєте код у справжньому Perl-скрипті, є багато цікавого
речі, які ви можете зробити. Скажімо, вам потрібно написати як sumit (як вище), так і multit.
Додавши трохи творчості, ми можемо це зробити

for({Name => 'sumit', Init => '0', Op => '+='},
{Name => 'multit', Init => '1', Op => '*='}) {
pp_def($_->{Name},
Pars => 'a(n); [o]b();',
Код => '
подвійний tmp;
tmp = '.$_->{Init}.';
цикл(n) %{
tmp '.$_->{Op}.' $a();
%}
$b() = tmp;
');
}

який легко визначає обидві функції. Тепер, якщо згодом вам знадобиться змінити підпис або
розмірність чи що завгодно, вам потрібно змінити лише одне місце у вашому коді. Так, звичайно,
у вашому редакторі є «вирізати та вставити» та «шукати та замінити», але це все ще менше
нудно і, безумовно, складніше забути лише одне місце і мати дивні помилки
Крім того, додавання 'orit' (порозрядне або) пізніше є однорядковим.

І пам’ятайте, що ви дійсно володієте всіма можливостями Perl – ви можете дуже легко читати
будь-який вхідний файл і створювати підпрограми з інформації в цьому файлі. Для простих випадків, як
вище, автор (Tjl) наразі віддає перевагу синтаксису хешування, як наведено вище - це не надто
набагато більше символів, ніж відповідний синтаксис масиву, але набагато легше зрозуміти і
змінити.

Тут також слід згадати про можливість отримати вказівник на початок даних
пам'ять - обов'язкова умова для взаємодії PDL з деякими бібліотеками. Це обробляється за допомогою
Директива "$P(var)", див. нижче.

Починаючи роботу над новою функцією pp_def'ined, якщо ви припуститеся помилки, то зазвичай
знайдіть купу помилок компілятора, що вказують номери рядків у згенерованому файлі XS. Якщо ти
знати, як читати файли XS (або якщо ви хочете навчитися скрутним способом), ви можете відкрити файл
згенерував файл XS і знайдіть номер рядка з помилкою. Проте нещодавнє
додаток до PDL::PP допомагає повідомити правильний номер рядка ваших помилок:
"pp_line_numbers". Робота з оригінальним прикладом саміту, якщо у вас було неправильне написання
tmp у вашому коді, ви можете змінити код (erroneos) на щось на зразок цього та
компілятор дасть вам набагато більше корисної інформації:

pp_def('sumit',
Pars => 'a(n); [o]b();',
Код => pp_line_numbers(__LINE__, q{
подвійний tmp;
tmp = 0;
цикл(n) %{
tmp += $a();
%}
$b() = rmp;
})
);

Для наведеної вище ситуації мій компілятор каже мені:

...
test.pd:15: помилка: 'rmp' неоголошено (перше використання в цій функції)
...

У моєму прикладі сценарію (називається test.pd) рядок 15 — це саме той рядок, у якому я зробив свій
друкарська помилка: "rmp" замість "tmp".

Отже, після цього короткого огляду загального характеру використання підпрограм PDL
PDL::PP давайте резюмуємо, за яких обставин вам дійсно варто це використовувати
препроцесор/прекомпілятор. Ви повинні використовувати PDL::PP, якщо хочете

· інтерфейс PDL до якоїсь зовнішньої бібліотеки

· напишіть алгоритм, який буде повільним, якщо його кодувати на Perl (це не так часто, як ви
думати; спершу подивіться на потоки та потік даних).

· бути розробником PDL (і навіть тоді це не обов'язково)

УВАГА


Завдяки своїй архітектурі PDL::PP може бути як гнучким, так і простим у використанні, з одного боку,
але водночас надзвичайно складний. Наразі частина проблеми полягає в цій помилці
повідомлення не дуже інформативні, і якщо щось піде не так, вам краще знати, що ви
ви робите і можете зламати свій шлях через внутрішні органи (або зможете з’ясувати,
методом проб і помилок, що не так з вашими аргументами до "pp_def"). Хоча ведеться робота над
створювати кращі попередження, не бійтеся надсилати свої запитання до списку розсилки, якщо
ти натрапляєш на неприємності.

ОПИС


Тепер, коли у вас є уявлення про те, як використовувати "pp_def" для визначення нових функцій PDL, настав час
пояснити загальний синтаксис "pp_def". "pp_def" спочатку приймає в якості аргументів ім'я
функцію, яку ви визначаєте, а потім хеш-список, який може містити різні ключі.

На основі цих ключів PP генерує код XS і файл .pm. Функція "pp_done" (див
приклад у SYNOPSIS) використовується, щоб повідомити PDL::PP, що в ньому більше немає визначень
цей файл, і настав час створити файл .xs і
файл .pm.

Як наслідок, їх може бути кілька pp_def() виклики всередині файлу (за умовними файлами
з кодом PP мають розширення .pd або .pp), але зазвичай лише одне pp_done().

Існує два основних різних типи використання pp_def(), «операція з даними» і «зріз
операційні прототипи.

«Операція з даними» використовується, щоб взяти деякі дані, змінити їх і вивести деякі інші дані; це
включає, наприклад, операцію '+', обернену матрицю, sumover тощо та всі приклади
ми вже говорили про це в цьому документі. Неявна та явна потокова обробка та
створення результату здійснюється автоматично в цих операціях. Ви навіть можете
виконувати потік даних за допомогою "sumit", "sumover" тощо (не лякайтеся, якщо ви не розумієте
концепція потоку даних у PDL ще дуже вдала; це все ще дуже експериментально).

«Операція зрізу» — це інший вид операції: в операції зрізу ви не є
змінюючи будь-які дані, ви визначаєте відповідності між різними елементами двох
piddles (приклади включають визначення функцій маніпулювання індексом/зрізів у файлі
шматочки.pd що є частиною розподілу PDL; але будьте обережні, це не вступний рівень
речі).

Якщо PDL був скомпільований з підтримкою поганих значень (тобто "WITH_BADVAL => 1"), то додаткові
ключі необхідні для "pp_def", як пояснюється нижче.

Якщо ви просто зацікавлені у спілкуванні з якоюсь зовнішньою бібліотекою (наприклад, деякими
Лінійна алгебра/матрична бібліотека), зазвичай вам потрібна «операція з даними», тому ми збираємося
щоб спочатку це обговорити.

дані операція


A простий приклад
Під час роботи з даними ви повинні знати, які розміри даних вам потрібні. Спочатку приклад
зі скалярами:

pp_def('додати',
Pars => 'a(); b(); [o]c();',
Код => '$c() = $a() + $b();'
);

Це виглядає трохи дивно, але давайте розберемо це. Перший рядок простий: ми визначаємо a
підпрограма з назвою "додати". Другий рядок просто оголошує наші параметри та
дужки означають, що вони є скалярами. Ми називаємо рядок, який визначає наші параметри і
їх розмірність підпис цієї функції. За його актуальність щодо
маніпуляції з потоками та індексами перевіряють сторінку керівництва PDL::Indexing.

Третій рядок – це власне операція. Ви повинні використовувати знаки долара та дужки
щоб посилатися на ваші параметри (це, ймовірно, зміниться в якийсь момент у майбутньому, як тільки a
знайдено хороший синтаксис).

Ці рядки – це все, що необхідно для визначення функції для PDL (ну,
насправді це не так; вам додатково потрібно написати Makefile.PL (див. нижче) і зібрати файл
модуль (щось на кшталт 'perl Makefile.PL; make'); але давайте поки що проігноруємо це).
Тож тепер можна

використовувати MyModule;
$a = pdl 2,3,4;
$b = pdl 5;

$c = додати ($a,$b);
# або
додати ($a,$b,($c=null)); # Альтернативна форма, корисна, якщо $c було
# попередньо налаштований на щось велике, тут не корисне.

і правильно працювати з потоками (результат $c == [7 8 9]).

Команда Pars розділ: підпис of a PP функція
Побачивши наведений вище приклад коду, ви, швидше за все, запитаєте: що це за дивне "$c=null"
синтаксису у другому виклику нашої нової функції "додати"? Якщо ще раз поглянути на
визначення "додати" ви помітите, що третій аргумент "c" позначено прапорцем
кваліфікатор "[o]", який повідомляє PDL::PP, що це вихідний аргумент. Тож вищезазначений заклик до
add означає "створити новий $c з нуля з правильними розмірами" - "null" є спеціальним
маркер для "пустого piddle" (ви можете запитати, чому ми не використали значення "undef", щоб позначити це
замість конкретного PDL «null»; ми зараз думаємо про це ;).

[Це також має бути пояснено в іншому розділі посібника!!] Причина
використання цього синтаксису як альтернативи полягає в тому, що якщо у вас є дійсно величезні piddles, ви можете це зробити

$c = PDL->null;
for(якийсь довгий цикл) {
# мунге a,b
додати ($a,$b,$c);
# munge c, повернути щось назад до a,b
}

і уникайте виділення та звільнення кожного разу $c. Спочатку виділяється один раз
add () і після цього пам'ять залишається, доки $c не буде знищено.

Якщо ти просто скажеш

$c = додати ($a,$b);

код, згенерований PP, автоматично заповнить "$c=null" і поверне результат. Якщо
ви хочете дізнатися більше про причини, чому PDL::PP підтримує цей стиль виведення
аргументи наводяться як останні аргументи, перевірте сторінку керівництва PDL::Indexing.

"[o]" - не єдиний кваліфікатор, який аргумент pdl може мати в сигнатурі. Інший
Важливим кваліфікатором є параметр "[t]", який позначає файл pdl як тимчасовий. Що це означає
означає? Ви повідомляєте PDL::PP, що цей pdl використовується лише для тимчасових результатів у ході
обчислення, і вас не цікавить його значення після обчислення
завершено. Але чому PDL::PP повинен знати про це в першу чергу? Причина
тісно пов'язаний з концепціями автоматичного створення pdl (ви чули про це вище) і
неявна різьблення. Якщо ви використовуєте неявну нитковість, розмірність автоматично
створений файл pdl насправді більше, ніж зазначено в сигнатурі. З позначкою "[o]".
pdl буде створено так, щоб вони мали додаткові розміри відповідно до числа
неявних розмірів нитки. Однак під час створення тимчасового файлу Pdl це завжди буде лише
бути достатньо великим, щоб він міг утримувати результат протягом однієї ітерації в циклі потоку, тобто
такого розміру, як того вимагає підпис. Таким чином, витрачається менше пам’яті, коли ви позначаєте файл pdl як
тимчасовий. По-друге, ви можете використовувати автоматичне створення виводу з тимчасовими файлами Pdl, навіть якщо ви
використовують явну потокову передачу, яка заборонена для звичайних вихідних pdl, позначених "[o]"
(див. PDL::Індексування).

Ось приклад, де ми використовуємо кваліфікатор [t]. Ми визначаємо функцію "викликати" це
викликає підпрограму C "f", якій потрібен тимчасовий масив того ж розміру та типу, що й масив
"a" (вибачте за пряме посилання для $P; це доступ за вказівником, див. нижче):

pp_def('дзвінок',
Pars => 'a(n); [t] tmp(n); [o] b()',
Код => 'int ns = $SIZE(n);
f($P(a),$P(b),$P(tmp),ns);
'
);

аргументація розміри та підпис
Тепер ми щойно поговорили про розміри pdl і підпис. Як вони пов’язані?
Скажімо, ми хочемо додати скаляр + номер індексу до вектора:

pp_def('add2',
Pars => 'a(n); b(); [o]c(n);',
Код => 'loop(n) %{
$c() = $a() + $b() + n;
%}'
);

Тут варто звернути увагу на кілька моментів: по-перше, аргумент "Pars" тепер містить n
аргументи, щоб показати, що ми маємо єдиний вимір в a та c. Важливо відзначити
що розміри є фактичними об’єктами, доступ до яких здійснюється за іменем, тому це оголошує a та c до
є то ж перші виміри. У більшості визначень PP розмір названих розмірів буде
встановлюватися з відповідних розмірів невивідних pdl (тих, у яких немає прапора "[o]"), але
іноді може знадобитися встановити розмір іменованого виміру явно через файл
цілочисельний параметр. Як це працює, дивіться нижче в описі розділу "OtherPars".

Постійне аргумент розміри in підпис
Припустимо, ви хочете, щоб вихідний вікно створювалося автоматично, і ви знаєте, що для кожного
його вимір буде мати однаковий розмір (скажімо 9) незалежно від розмірів
введення piddles. У цьому випадку ви використовуєте наступний синтаксис у розділі Pars для вказівки
розмір розміру:

' [o] y(n=9); '

Як і очікувалося, за потреби будуть створені додаткові розміри, необхідні для нарізання різьблення. Якщо ти
потрібно призначити іменований вимір відповідно до складнішої формули (ніж константа)
Ви повинні використовувати ключ "RedoDimsCode", описаний нижче.

тип конверсій та підпис
Сигнатура також визначає перетворення типів, які будуть виконуватися під час PP
викликається функція. Отже, що відбувається, коли ми викликаємо один із наших раніше визначених
функції з pdl різного типу, напр

add2($a,$b,($ret=null));

де $a має тип "PDL_Float", а $b - тип "PDL_Short"? З підписом, як показано в
визначення "add2" над типом даних операції (як визначено під час виконання).
файл pdl з "найвищим" типом (послідовність байт < короткий < ushort < довгий < float
< подвійний). У прикладі add2 типом даних операції є float ($a має це
тип даних). Після цього всі аргументи pdl перетворюються на цей тип даних (вони не є
перетворюється на місце, але копія з правильним типом створюється, якщо аргумент pdl не містить
тип операції). Нульові файли pdl не сприяють визначенню типу
тип операції. Однак вони будуть створені з типом даних операції;
тут, наприклад, $ret матиме тип float. Ви повинні знати ці правила, коли
виклик функцій PP з pdl різних типів, щоб зайняти додаткове сховище та
врахування вимог часу виконання.

Ці перетворення типів є правильними для більшості функцій, які ви зазвичай визначаєте за допомогою "pp_def".
Однак існують певні випадки, коли поведінка перетворення типу дещо змінена
бажаний. Для цих випадків можна використовувати додаткові кваліфікатори в підписі для вказівки
бажані властивості щодо перетворення типу. Ці кваліфікаційні показники можна поєднувати з
з якими ми вже стикалися ( створення qualifiers "[o]" і "[t]"). Ходімо
через список кваліфікаторів, які змінюють поведінку перетворення типу.

Найважливішим є кваліфікатор "int", який стане в нагоді, коли аргумент pdl
представляє індекси в інший файл Pdl. Давайте подивимося на приклад із "PDL::Ufunc":

pp_def('maximum_ind',
Pars => 'a(n); int [o] b()',
Код => '$GENERIC() cur;
int curind;
цикл(n) %{
якщо (!n || $a() > cur) {cur = $a(); curind = n;}
%}
$b() = curind;',
);

Функція «maximum_ind» знаходить індекс найбільшого елемента вектора. Якщо подивитися
у сигнатурі ви помічаєте, що вихідний аргумент "b" оголошено з
додатковий кваліфікатор "int". Це має такі наслідки для перетворення типів:
незалежно від типу вхідного pdl "a", вихідний pdl "b" матиме тип "PDL_Long"
що має сенс, оскільки "b" представлятиме індекс у "a". Крім того, якщо ви зателефонуєте
функція з наявним вихідним файлом pdl "b", її тип не вплине на тип даних
операція (див. вище). Отже, навіть якщо "a" менший за тип, ніж "b", це не буде
перетворюється на відповідність типу "b", але залишається недоторканим, що економить пам'ять і цикли ЦП
і це правильно, коли "b" представляє індекси. Також зверніть увагу, що ви можете використовувати
Кваліфікатор 'int' разом з іншими кваліфікаторами (кваліфікатори "[o]" і "[t]"). Порядок є
значний -- кваліфікатори типу передують кваліфікаторам створення ("[o]" і "[t]").

Наведений вище приклад також демонструє типове використання макросу "$GENERIC()". Воно розширюється
до поточного типу у так званому родовому циклі. Що таке загальний цикл? Як ти вже
чув, що функція PP має тип даних часу виконання, що визначається типом аргументів pdl
його було викликано з. Таким чином, код XS, згенерований PP для цієї функції, містить a
перемикач типу "перемикач (тип) {case PDL_Byte: ... case PDL_Double: ...}", який вибирає регістр
на основі типу даних функції під час виконання (вона називається типом "цикл", тому що там
це цикл у коді PP, який генерує випадки). У будь-якому випадку ваш код вставляється один раз
для кожного типу PDL в цей оператор switch. Макрос "$GENERIC()" просто розгортається до
відповідний тип у кожній копії вашого проаналізованого коду в цьому операторі "перемикання", наприклад, у
"case PDL_Byte" розділ "cur" розгорнеться до "PDL_Byte" і так далі для іншого випадку
заяви. Я думаю, ви розумієте, що це корисний макрос для зберігання значень pdl в деяких
Код.

Є пара інших кваліфікаторів із подібними ефектами, як "int". Для вашого
для зручності є кваліфікатори "float" і "double" з аналогічними наслідками на
введіть перетворення як "int". Припустимо, у вас є a дуже великий масив, для якого ви хочете
обчислити суми рядків і стовпців за допомогою еквіваленту функції "sumover". Однак з
звичайне визначення "sumover" ви можете зіткнутися з проблемами, коли ваші дані, наприклад
тип коротко. Подібний дзвінок

sumover($large_pdl,($sums = null));

призведе до того, що $sums буде короткого типу і, отже, схильний до помилок переповнення if
$large_pdl – це дуже великий масив. З іншого боку дзвонить

@dims = $large_pdl->dims; shift @dims;
sumover($large_pdl,($суми = нулі(подвійні,@dims)));

також не є хорошою альтернативою. Тепер у нас немає проблем із переповненням з $sums, але at
витрати на перетворення типу $large_pdl у подвійний, щось погано, якщо це дійсно так
великий pdl. Ось де "подвійний" стане в нагоді:

pp_def('sumoverd',
Pars => 'a(n); подвійний [o] b()',
Код => 'подвійний tmp=0;
цикл(n) %{ tmp += a(); %}
$b() = tmp;',
);

Це дозволяє нам обійти проблеми перетворення типів і переповнення. Знову ж таки аналогічно
Кваліфікатор "int" "double" призводить до того, що "b" завжди має тип double, незалежно від типу
"a", не призводячи до перетворення типу "a" як побічного ефекту.

Нарешті, є кваліфікатори "type+", де тип є одним із "int" або "float". Що
чи це означає. Давайте проілюструємо кваліфікатор "int+" фактичним визначенням
сумовер:

pp_def('sumover',
Pars => 'a(n); int+ [o] b()',
Код => '$GENERIC(b) tmp=0;
цикл(n) %{ tmp += a(); %}
$b() = tmp;',
);

Як ми вже бачили для кваліфікаторів "int", "float" і "double", файл pdl позначений символом
Кваліфікатор "type+" не впливає на тип даних операції pdl. Його значення в тому
"зробіть цей pdl принаймні типу "type" або вище, відповідно до типу файлу
операція". У прикладі sumover це означає, що коли ви викликаєте функцію з "a"
типу PDL_Short вихідний pdl буде типу PDL_Long (так само, як
регістр з кваліфікатором "int"). Це знову намагається уникнути проблем переповнення під час використання
малі типи даних (наприклад, байтові зображення). Однак, коли тип даних операції вищий
ніж тип, зазначений у специфікаторі "тип+" "b", буде створено з типом даних
операція, наприклад, коли "a" має тип double, тоді "b" також буде подвійним. Ми сподіваємося
Ви погоджуєтеся, що це розумна поведінка для "сумовера". Має бути очевидно, як
Кваліфікатор "float+" працює за аналогією. Може виникнути потреба в можливості вказати набір
альтернативних типів для параметрів. Однак, ймовірно, це не буде реалізовано
поки хтось не придумає розумного застосування для цього.

Зауважте, що тепер нам потрібно було вказати макрос $GENERIC з ім’ям файлу pdl, щоб отримати
введіть із цього аргументу. Чому так? Якщо ви уважно дотримуєтеся наших пояснень, ви це зробите
зрозуміли, що в деяких випадках "b" матиме інший тип, ніж тип
операція. Виклик макросу "$GENERIC" з "b" як аргумент гарантує, що тип
завжди буде таким же, як у "b" у цій частині загального циклу.

Це приблизно все, що можна сказати про розділ «Pars» у виклику «pp_def». Ти повинен
пам’ятайте, що цей розділ визначає підпис функції, визначеної PP, ви можете використовувати
кілька варіантів кваліфікації певних аргументів як вихідних і тимчасових аргументів і все
розміри, на які пізніше можна буде посилатися в розділі «Код», визначаються за назвою.

Важливо, щоб ви розуміли значення підпису, оскільки в останньому PDL
версії ви можете використовувати його для визначення потокових функцій з Perl, тобто те, що ми називаємо
Perl рівень різьблення. Будь ласка, перевірте PDL::Indexing для деталей.

Команда код розділ
Розділ «Код» містить фактичний код XS, який буде у внутрішній частині a
цикл потоку (якщо ви не знаєте, що таке цикл потоку, ви все ще не читали
PDL::Індексування; зробіть це зараз ;) після виконання будь-яких макросів PP (наприклад, $GENERIC) і функцій PP
розгорнутий (наприклад, функція «цикл», яку ми пояснимо далі).

Давайте швидко повторимо приклад "sumover":

pp_def('sumover',
Pars => 'a(n); int+ [o] b()',
Код => '$GENERIC(b) tmp=0;
цикл(n) %{ tmp += a(); %}
$b() = tmp;',
);

Конструкція «цикл» у розділі «Код» також посилається на назву виміру, тому ви цього не робите
потрібно вказати будь-які обмеження: цикл правильно розміром і все зроблено за вас,
знову.

Далі, є дивовижний факт, що "$a()" і "$b()" так НЕ містять індекс. Це
це не потрібно, тому що ми перебираємо n і обидві змінні знають, які розміри
так вони автоматично знають, що їх зациклюють.

Ця функція дуже зручна в багатьох місцях і робить код набагато коротшим. оф
звичайно, бувають випадки, коли хочеться обійти це; ось функція, яка створює a
матриця симетрична і служить прикладом того, як кодувати явний цикл:

pp_def('symm',
Pars => 'a(n,n); [o]c(n,n);',
Код => 'loop(n) %{
int n2;
for(n2=n; n2<$SIZE(n); n2++) {
$c(n0 => n, n1 => n2) =
$c(n0 => n2, n1 => n) =
$a(n0 => n, n1 => n2);
}
%}
'
);

Давайте розберемо те, що відбувається. По-перше, що має робити ця функція? Від його
підпису, ви бачите, що потрібна двовимірна матриця з рівною кількістю стовпців і рядків і
виводить матрицю такого ж розміру. З заданої вхідної матриці $a вона обчислює симетричну
вихідна матриця $c (симетрична в матричному сенсі, що A^T = A, де ^T означає матрицю
транспонувати, або мовою PDL $c == $c->xchg(0,1)). Це робиться, використовуючи лише значення
на і нижче діагоналі $a. У вихідній матриці $c усі значення на та нижче
діагональ такі ж, як і в $a, а ті, що над діагоналлю, є дзеркальним відображенням
ті, що знаходяться під діагоналлю (зверху і знизу, тут інтерпретуються так, як друкує PDL
2D Pdls). Якщо це пояснення все ще звучить трохи дивно, просто продовжуйте, створіть невеликий файл
в яке ви пишете це визначення, створіть нове розширення PDL (див. розділ про
Makefiles для коду PP) і спробуйте це на кількох прикладах.

Після пояснення того, що функція має робити, варто зазначити кілька моментів
зауваження з синтаксичної точки зору. Спочатку ми отримуємо розмір названого виміру
"n" знову за допомогою макросу $SIZE. По-друге, раптом з'являються ці кумедні "n0" і "n1"
імена індексів у коді, хоча підпис визначає лише вимір "n". Чому це? The
Причина стає зрозумілою, коли ви помітите, що і перший, і другий вимір $a і $b
мають назву "n" у підписі "symm". Це повідомляє PDL::PP, що перший і другий
розмірність цих аргументів повинна мати однаковий розмір. Інакше створена функція
викличе помилку під час виконання. Однак тепер у доступі до $a і $c PDL::PP не можна розрахувати
виходячи з назви індексу, до якого індексу "n" відноситься більше. Тому,
індекси з однаковими назвами вимірів нумеруються зліва направо, починаючи з 0, наприклад, in
наведений вище приклад "n0" відноситься до першого виміру $a і $c, "n1" до другого і
так далі.

У всіх прикладах до цього часу ми використовували лише члени "Pars" і "Code" хешу that
було передано до "pp_def". Звичайно, є й інші ключі, які розпізнаються PDL::PP і
про деякі з них ми почуємо в ході цього документа. Знайти (неповний)
список ключів у Додатку A. Список макросів і функцій PP (ми зустрічалися лише
деякі з наведених вище прикладів ще), які розширені у значеннях хеш-аргументу
до "pp_def" узагальнено в Додатку B.

На цьому етапі може бути доречним згадати, що PDL::PP не є повністю статичним,
добре розроблений набір процедур (за словами Туомаса: «припиніть думати про ПП як про набір
рутини, висічені в камені"), а радше набір речей, які автор PDL::PP
(Туомас Дж. Лукка) вважав, що йому доведеться часто писати в свої підпрограми розширення PDL.
PP намагається бути розширеним, щоб у майбутньому, коли виникнуть нові потреби, новий загальний код міг
абстрагуватися назад у нього. Якщо ви хочете дізнатися більше про те, чому ви можете змінити
PDL::PP та як це зробити, перевірте розділ про внутрішні елементи PDL::PP.

Обробка поганий величини
Якщо у вас немає підтримки поганого значення, скомпільованої в PDL, ви можете проігнорувати цей розділ і файл
пов'язані ключі: "BadCode", "HandleBad", ... (спробуйте роздрукувати значення
$PDL::Bad::Status - якщо він дорівнює 0, то рухайтеся далі).

Існує кілька ключів і макросів, які використовуються під час написання коду для обробки поганих значень. Перший
один із них - клавіша HandleBad:

HandleBad => 0
Це позначає pp-програму як $NOT поводження з поганими цінностями. Якщо ця рутина надсилається piddles
із встановленим "badflag", тоді на STDOUT і додатки друкується попереджувальне повідомлення
обробляються так, ніби значення, яке використовується для подання поганих значень, є дійсним числом. The
Значення "badflag" не поширюється на вихідні piddles.

Прикладом використання цього є підпрограми ШПФ, які зазвичай не мають способу
ігнорування частини даних.

HandleBad => 1
Це змушує PDL::PP писати додатковий код, який забезпечує використання розділу BadCode, і
що макрос "$ISBAD()" (і його побратими) працюють.

HandleBad не дається
Якщо будь-який з вхідних пікселів має свій «поганий флаг», то вихідні піктограми будуть
мають встановлений "поганий флаг", але будь-який наданий BadCode ігнорується.

Значення "HandleBad" використовується для визначення вмісту ключа "BadDoc", якщо він не
дано.

Щоб обробляти погані значення, код повинен бути написаний дещо інакше; наприклад,

$c() = $a() + $b();

стає чимось на зразок

якщо ( $a() != НЕДОСТАТОЧНИЙ && $b() != НЕВАЛЬНИЙ ) {
$c() = $a() + $b();
} Ще {
$c() = БАДВАЛЬ;
}

Однак нам потрібна друга версія лише в тому випадку, якщо у вхідних модулях є погані значення
(і ця підтримка поганої вартості потрібна!) - інакше ми насправді хочемо оригінальний код.
Тут на допомогу приходить ключ «BadCode»; ви використовуєте його, щоб вказати код для виконання, якщо він поганий
значення можуть бути присутніми, і PP використовує і його, і розділ «Код», щоб щось створити
подібно до:

якщо ( bad_values_are_present ) {
fancy_threadloop_stuff {
BadCode
}
} Ще {
fancy_threadloop_stuff {
код
}
}

Цей підхід означає, що накладних витрат практично немає, коли погані значення відсутні
(тобто підпрограма badflag повертає 0).

У розділі BadCode можна використовувати ті ж макроси та циклічні конструкції, що й у розділі Code.
Однак без наступних додаткових макросів це було б мало користі:

$ISBAD(вар)
Щоб перевірити, чи є значення piddle поганим, скористайтеся макросом $ISBAD:

if ( $ISBAD(a()) ) { printf("a() поганий\n"); }

Ви також можете отримати доступ до певних елементів piddle:

if ( $ISBAD(a(n=>l)) ) { printf("елемент %d із a() поганий\n", l); }

$ISGOOD(вар.)
Це протилежність макросу $ISBAD.

$SETBAD(вар.)
Якщо ви хочете встановити елемент piddle погано.

$ISBADVAR(c_var,pdl)
Якщо ви кешували значення piddle "$a()" у змінну c (скажімо, "foo"), то
перевірте, чи він поганий, використовуйте "$ISBADVAR(foo,a)".

$ISGOODVAR(c_var,pdl)
Як і вище, але цього разу перевіряємо, чи не погане значення кешування.

$SETBADVAR(c_var,pdl)
Щоб скопіювати неправильне значення для piddle у змінну ac, використовуйте "$SETBADVAR(foo,a)".

РОБИТИ: згадати макроси "$PPISBAD()" тощо.

Використовуючи ці макроси, наведений вище код можна вказати як:

Код => '$c() = $a() + $b();',
Поганий код => '
якщо ( $ISBAD(a()) || $ISBAD(b()) ) {
$SETBAD(c());
} Ще {
$c() = $a() + $b();
}',

Оскільки це Perl, TMTOWTDI, ви також можете написати:

Поганий код => '
if ( $ISGOOD(a()) && $ISGOOD(b())) {
$c() = $a() + $b();
} Ще {
$SETBAD(c());
}',

Якщо ви хочете отримати доступ до значення badflag для даного piddle, ви можете використовувати
Макроси "$PDLSTATExxxx()":

$PDLSTATEISBAD(pdl)
$PDLSTATEISGOOD(pdl)
$PDLSTATESETBAD(pdl)
$PDLSTATESETGOOD(pdl)

РОБИТИ: також згадати параметри «FindBadStatusCode» та «CopyBadStatusCode» до «pp_def»
як ключ "BadDoc".

Взаємодія ваш власна/бібліотека Функції використання PP
Тепер розглянемо наступне: у вас є власна функція C (вона насправді може бути частиною
деяка бібліотека, яку ви хочете підключити до PDL), яка приймає в якості аргументів два вказівники
вектори подвійного:

void myfunc(int n,double *v1,double *v2);

Правильним способом визначення функції PDL є

pp_def('myfunc',
Pars => 'a(n); [o]b(n);',
GenericTypes => ['D'],
Код => 'myfunc($SIZE(n),$P(a),$P(b));'
);

"$P("номінальнийСинтаксис ")" повертає покажчик на перший елемент, а інші елементи є
гарантовано брехатиме після цього.

Зауважте, що тут можна зробити багато помилок. По-перше, потрібно використовувати $SIZE(n).
замість "n". По-друге, ви не повинні вставляти жодних циклів у цей код. По-третє, тут ми зустрічаємося
новий хеш-ключ, розпізнаний PDL::PP : декларація "GenericTypes" повідомляє PDL::PP, щоб
ГЕРУЙТЕ ЛИШЕ TYPELOOP FOP СПИСКУ ВКАЗАНИХ ТИПІВ. В даному випадку «подвійний». Це
має дві переваги. По-перше, розмір скомпільованого коду значно зменшується, по-друге, якщо
неподвійні аргументи передаються до "myfunc()". PDL автоматично конвертує їх
подвоїти перед переходом до зовнішньої процедури C і перетворити їх назад.

Можна також використовувати «Pars» для визначення типів окремих аргументів. Так можна було б
напишіть це як:

pp_def('myfunc',
Pars => 'подвійний a(n); подвійний [o]b(n);',
Код => 'myfunc($SIZE(n),$P(a),$P(b));'
);

Специфікація типу в "Pars" звільняє аргумент від змін у циклі типу -
скоріше він також автоматично конвертується з вказаного типу. Це очевидно
корисно в більш загальному прикладі, наприклад:

void myfunc(int n,float *v1,long *v2);

pp_def('myfunc',
Pars => 'float a(n); довгий [o]b(n);',
GenericTypes => ['F'],
Код => 'myfunc($SIZE(n),$P(a),$P(b));'
);

Зверніть увагу, що ми все ще використовуємо "GenericTypes", щоб зменшити розмір циклу типу, очевидно, PP може
в принципі помітьте це і зробіть це автоматично, хоча код ще не досяг цього
рівень витонченості!

Нарешті, зауважте, що коли типи перетворюються автоматично, ОБОВ’ЯЗКОВО використовувати кваліфікатор "[o]".
вихідні змінні або ви жорсткі зміни будуть оптимізовані PP!

Якщо ви використовуєте велику бібліотеку, ви можете ще більше автоматизувати інтерфейс. Perl може
допоможіть вам знову (!) зробити це. У багатьох бібліотеках у вас є певні умови виклику.
Цим можна скористатися. Коротше кажучи, ви можете написати невеликий парсер (який насправді не є
важко в Perl), який потім генерує виклики "pp_def" з проаналізованих описів
функції в цій бібліотеці. Для прикладу, будь ласка, перевірте Slatec інтерфейс у
Дерево «Lib» розподілу PDL. Якщо ви хочете перевірити (під час налагодження), який викликає
Функції PP Ваш код Perl, згенерований невеликим допоміжним пакетом, стане в нагоді, який
замінює функції PP з ідентичними назвами, які передають свої аргументи до стандартного виведення.

Просто сказати

perl -MPDL::PP::Дамп myfile.pd

щоб побачити виклики до "pp_def" і друзів. Спробуйте з ops.pd та slatec.pd. Якщо ви
зацікавлені (або хочете покращити його), джерело знаходиться в Basic/Gen/PP/Dump.pm

Інше макроси та Функції in код розділ
Макроси: наразі ми зустрічали макроси $SIZE, $GENERIC та $P. Зараз ми збираємося
швидко пояснити інші макроси, які розгорнуті в розділі «Код» PDL::PP разом
з прикладами їх використання.

$T Макрос $T використовується для перемикачів типу. Це дуже корисно, коли потрібно використовувати
різні зовнішні (наприклад, бібліотеки) функції залежно від типу введення аргументів.
Загальний синтаксис такий

$Ttypeletters(type_alternatives)

де "букви" - це перестановка підмножини букв "BSULFD", які стоять
для Byte, Short, Ushort тощо та "type_alternatives" є розширеннями, коли тип
операції ПП дорівнює зазначеній відповідною буквою. Давайте
проілюструйте цей незрозумілий опис на прикладі. Припустимо, що у вас є два C
функції з прототипами

void float_func(float *in, float *out);
void double_func(подвійний *вхід, подвійний *вихід);

які в основному роблять те ж саме, але один приймає float, а інший подвійні вказівники.
Ви можете зв’язати їх із PDL, визначивши загальну функцію «foofunc» (яка буде
викликати правильну функцію залежно від типу перетворення):

pp_def('foofunc',
Pars => ' a(n); [o] b();',
Код => ' $TFD(float_func,double_func) ($P(a),$P(b));'
GenericTypes => [qw(FD)],
);

Зверніть увагу, що ви не можете сказати

Код => ' $TFD(float,double)_func ($P(a),$P(b));'

оскільки макрос $T розширюється за допомогою кінцевих пробілів, аналогічно макросам препроцесора C.
Трохи довша форма, проілюстрована вище, є правильною. Якщо ви дійсно хочете стислості, ви
звісно можна

'$TBSULFD('.(join ',',map {"long_identifier_name_$_"}
qw/byt короткий несеігрований lounge flotte dubble/).');'

$ PP
Макрос $PP використовується для т.зв фізичний покажчик доступ, фізичний відноситься до
деякі внутрішні оптимізації PDL (для тих, хто знайомий з ядром PDL, ми
говоримо про оптимізацію vaffine). Цей макрос в основному призначений для внутрішнього використання та для вас
не потрібно використовувати його в будь-якому звичайному коді.

$COMP (і розділ "OtherPars")
Макрос $COMP використовується для доступу до значень, які не є pdl, у розділі коду. Його назва
отримані від реалізації перетворень у PDL. Змінні, на які можна посилатися
для використання $COMP є членами ``компільованої'' структури, яка представляє PDL
перетворення під питанням, але ще не містить жодної інформації про розміри
(для отримання додаткової інформації перевірте PDL::Internals). Однак ви можете розглядати $COMP просто як
чорний ящик, нічого не знаючи про реалізацію перетворень у PDL.
Тож коли б ви використали цей макрос? Його основне використання — доступ до значень аргументів, які
оголошуються в розділі "OtherPars" визначення "pp_def". Але тоді у вас немає
вже чули про ключ "OtherPars"?! Наведемо ще один приклад, який ілюструє
типове використання обох нових функцій:

pp_def('pnmout',
Pars => 'a(м)',
OtherPars => "char* fd",
GenericTypes => [qw(BUSL)],
Код => 'PerlIO *fp;
IO *io;

io = GvIO(gv_fetchpv($COMP(fd),FALSE,SVt_PVIO));
якщо (!io || !(fp = IoIFP(io)))
croak("Не можу зрозуміти FP");

якщо (PerlIO_write(fp,$P(a),len) != len)
croak("Помилка запису файлу pnm");
');

Ця функція використовується для запису даних з pdl у файл. Дескриптор файлу передано
як рядок у цю функцію. Цей параметр не входить до розділу «Pars».
оскільки його не можна з користю розглядати як файл pdl, а скоріше як влучно названий
Розділ «OtherPars». Параметри в розділі "OtherPars" слідують за параметрами в розділі "Pars"
розділ під час виклику функції, тобто

open FILE,">out.dat" або die "не вдалося відкрити out.dat";
pnmout($pdl,'FILE');

Якщо ви хочете отримати доступ до цього параметра всередині кодової секції, ви повинні вказати PP by
використовуючи макрос $COMP, тобто ви пишете "$COMP(fd)", як у прикладі. Інакше ПП
не знав би, що "fd", на який ви посилаєтеся, такий самий, як зазначено в
Розділ «OtherPars».

Іншим використанням розділу "OtherPars" є встановлення іменованого виміру в сигнатурі.
Наведемо приклад, як це робиться:

pp_def('setdim',
Pars => '[o] a(n)',
OtherPars => 'int ns => n',
Код => 'loop(n) %{ $a() = n; %}',
);

Це говорить про те, що названий вимір "n" буде ініціалізовано зі значення інший
параметр "ns", який має цілочисельний тип (я думаю, ви зрозуміли, що ми використовуємо
Синтаксис "CType From => named_dim"). Тепер ви можете викликати цю функцію звичайним способом:

setdim(($a=null),5);
надрукувати $a;
[ 0 1 2 3 4 ]

Правда, ця функція не дуже корисна, але вона демонструє, як вона працює. Якщо ти
Викличте функцію з наявним файлом Pdl, і вам не потрібно явно вказувати
розмір "n", оскільки PDL::PP може визначити його з розмірів ненульового pdl. в
у цьому випадку ви просто вказуєте параметр розміру як "-1":

$a = історія ($b);
setdim($a,-1);

Це має зробити це.

Єдина функція PP, яку ми використовували в прикладах, це "цикл". Крім того,
наразі є дві інші функції, які розпізнаються в розділі «Код»:

ниткапетля
Як ми чули вище, сигнатура визначеної PP функції визначає розміри всіх
аргументи pdl, залучені в a примітивний операція. Однак ви часто дзвоните
функції, які ви визначили за допомогою PP з файлами pdl, які мають більше розмірів, ніж ці
зазначено в підписі. У цьому випадку примітивна операція виконується для всіх
підзрізи відповідної розмірності в тому, що називається a нитка петля (Дивись також
огляд вище та PDL::Indexing). Припускаючи, що ви маєте якесь уявлення про цю концепцію
напевно зрозуміє, що операція, зазначена в розділі коду, має бути
оптимізовано, оскільки це найтугіша петля всередині нитки. Однак, якщо ви переглянете
У прикладі, де ми визначаємо функцію "pnmout", ви швидко зрозумієте, що дивлячись
дескриптор файлу "IO" у циклі внутрішнього потоку не дуже ефективний під час запису
pdl з багатьма рядками. Кращим підходом було б пошукати дескриптор "IO" один раз
за межами петлі нитки та використовуйте його значення, а потім всередині найтугішої петлі нитки. Це
саме там, де функція "threadloop" стане в нагоді. Ось удосконалене визначення
"pnmout", який використовує цю функцію:

pp_def('pnmout',
Pars => 'a(м)',
OtherPars => "char* fd",
GenericTypes => [qw(BUSL)],
Код => 'PerlIO *fp;
IO *io;
int len;

io = GvIO(gv_fetchpv($COMP(fd),FALSE,SVt_PVIO));
якщо (!io || !(fp = IoIFP(io)))
croak("Не можу зрозуміти FP");

len = $SIZE(m) * sizeof($GENERIC());

петля нитки %{
якщо (PerlIO_write(fp,$P(a),len) != len)
croak("Помилка запису файлу pnm");
%}
');

Це працює наступним чином. Зазвичай код C, який ви пишете, розміщується в розділі «Код».
всередині циклу потоку (тобто PP генерує відповідний обертаючий код XS навколо нього).
Однак, коли ви явно використовуєте функцію "threadloop", PDL::PP розпізнає це і
не обгортає ваш код додатковим циклом потоку. Це має ефект, який ви кодуєте
запис за межами циклу потоку виконується лише один раз за перетворення і лише код
з навколишнім "%{ ... %}" пара розміщується в найтугішому циклі нитки. Це
також стане в нагоді, коли ви хочете виконати рішення (або будь-який інший код, особливо
код із інтенсивним процесором) лише один раз на потік, тобто

pp_addhdr('
#define RAW 0
#define ASCII 1
');
pp_def('do_raworascii',
Pars => 'a(); b(); [o]c()',
OtherPars => 'int mode',
Код => перемикач ' ($COMP(режим)) {
корпус RAW:
петля нитки %{
/* робити необроблені речі */
%}
break;
регістр ASCII:
петля нитки %{
/* робити ASCII речі */
%}
break;
за замовчуванням:
croak("невідомий режим");
}'
);

Типи
Функція типів працює подібно до макросу $T. Однак з функцією "типи".
код у наступному блоці (розмежований "%{" і "%}", як зазвичай) виконується для всіх
ті випадки, в яких є тип даних операції будь-який of типи, представлені
літери в аргументі "тип", напр

Код =>...

типи (BSUL) %{
/* виконати операцію цілого типу */
%}
типи (FD) %{
/* виконати операцію з плаваючою комою */
%}
... '

Команда RedoDimsCode розділ
Клавіша "RedoDimsCode" - це додатковий ключ, який використовується для обчислення розмірів piddles на
час виконання у випадку, якщо стандартні правила обчислення розмірів із сигнатури відсутні
достатній. Вміст запису "RedoDimsCode" інтерпретується так само
розділ Кодексу інтерпретується-- тобто, макроси PP розгортаються, і результат є
інтерпретується як код C. Метою коду є встановлення розміру деяких вимірів
з'являються в підписі. Розподіл пам’яті, цикли потоків тощо будуть налаштовані як
якщо обчислений вимір з'явився в підписі. У вашому коді ви спочатку обчислюєте
бажаний розмір іменованого розміру в підписі відповідно до ваших потреб, а потім
призначте йому це значення через $РОЗМІР() макрос.

Як приклад розглянемо наступну ситуацію. Ви використовуєте зовнішню бібліотеку
підпрограма, яка вимагає, щоб тимчасовий масив для робочої області передавався як аргумент. два
масиви вхідних даних, які передаються p(m) і x(n). Вихідним масивом даних є y(n). The
для підпрограми потрібен масив робочої області довжиною n+m*m, і вам потрібно сховище
створюється автоматично так само, як і для будь-якого piddle, позначеного позначками [t] або [o]. Що
ви хотіли б сказати щось на кшталт

pp_def( "myexternalfunc",
Парс => " p(м); x(n); [о] у; [t] робота(n+m*m); ", ...

але це не спрацює, оскільки PP не може інтерпретувати вирази з арифметикою в
підпис. Натомість ти пиши

pp_def( "myexternalfunc",
Парс => " p(м); x(n); [о] у; [t] робота (wn); ",
RedoDimsCode => "
int im = $PDL(p)->dims[0];
int in = $PDL(x)->dims[0];
int min = in + im * im;
int inw = $PDL(work)->dims[0];
$SIZE(wn) = inw >= min ? inw : хв; ",
Код => "
externalfunc($P(p),$P(x),$SIZE(m),$SIZE(n),$P(робота));
";)

Цей код працює наступним чином: Макрос $PDL(p) розгортається до покажчика на структуру pdl для
piddle с. У цьому випадку вам не потрібен покажчик на дані (тобто $P), тому що ви
хочете отримати доступ до методів для piddle на рівні C. Ви отримуєте перший вимір
кожну зі слів і зберігайте їх у цілих числах. Потім обчислюється мінімальна довжина
робочий масив може бути. Якщо користувач надіслав piddle «роботу» з достатньою пам’яттю, то залиште його
на самоті. Якщо користувач надіслав, скаже нульовий pdl або взагалі не надіслав pdl, тоді розмір wn буде
нуль і ви скидаєте його до мінімального значення. Перед кодом у розділі Code є
виконаний PP створить належне сховище для «роботи», якщо воно не існує. Зверніть увагу, що ви
прийняв лише перший вимір "p" і "x", оскільки користувач, можливо, надіслав piddles з
додаткові розміри різьблення. Звичайно, тимчасовий piddle «працює» (зверніть увагу на прапорець [t])
в будь-якому випадку не слід надавати жодних розмірів різьблення.

Ви також можете використовувати "RedoDimsCode", щоб встановити розмір піктограми, позначеної позначкою [o]. У цьому
якщо ви встановлюєте розміри для названого виміру в сигнатурі за допомогою $РОЗМІР() як в
попередній приклад. Однак, оскільки у piddle позначено [o] замість [t],
розміри різьблення будуть додані за потреби так само, як якщо б був розмір розміру
обчислюється з підпису за звичайними правилами. Ось приклад із
PDL::Математика

pp_def("полікорені",
Pars => 'cr(n); ci(n); [о]rr(м); [о]ri(м);',
RedoDimsCode => 'int sn = $PDL(cr)->dims[0]; $SIZE(m) = sn-1;',

Вхідні пікселі є дійсними та уявними частинами комплексних коефіцієнтів a
поліном. Вихідними є дійсні та уявні частини коренів. є "n"
корені до полінома "n"-го порядку, і такий поліном має коефіцієнти "n+1" (
повертається через "n"-й). У цьому прикладі потоки працюватимуть правильно. Тобто
перший вимір вихідного елемента з настроєним розміром, але інше різьблення
розміри будуть призначені так, як якщо б не було "RedoDimsCode".

Типова карта обробка in "OtherPars" розділ
Розділ "OtherPars", розглянутий вище, дуже часто є абсолютно вирішальним, коли ви
інтерфейс зовнішніх бібліотек з PDL. Однак у багатьох випадках і зовнішні бібліотеки
використовувати похідні типи або вказівники різних типів.

Стандартним способом вирішення цього питання в Perl є використання файлу "typemap". Це обговорюється в
деякі деталі в perlxs у стандартній документації Perl. У PP функціональність дуже
подібне, тому ви можете створити файл "typemap" у каталозі, де знаходиться ваш файл PP
і коли він побудований, він автоматично зчитується, щоб визначити відповідний переклад
між типом C і вбудованим типом Perl.

Тим не менш, є кілька важливих відмінностей від загальної обробки типів
в XS. Перше, і, мабуть, найважливіше, це те, що на даний момент є вказівники на типи
не допускається в розділі "OtherPars". Щоб обійти це обмеження, ви повинні використовувати
Тип "IV" (дякую Джадду Тейлору за вказівку на те, що це необхідно для портативності).

Напевно, найкраще проілюструвати це парою фрагментів коду:

Наприклад, функція "gsl_spline_init" має таке оголошення C:

int gsl_spline_init(gsl_spline * сплайн,
const double xa[], const double ya[], size_t size);

Очевидно, що масиви "xa" і "ya" є кандидатами на передачу як piddles і
Аргумент "size" - це лише довжина цих додатків, тому їх можна обробити
Макрос "$SIZE()" у PP. Проблема полягає в покажчику на тип "gsl_spline". Природне
рішенням буде написати декларацію "OtherPars" форми

OtherPars => 'gsl_spline *spl'

і напишіть короткий файл "typemap", який обробляв цей тип. Наразі це не працює
однак! Отже, що вам потрібно зробити, це трохи обійти проблему (і в певному сенсі
це теж простіше!):

Рішення полягає в тому, щоб оголосити «сплайн» у розділі «OtherPars» за допомогою «Целочисельного значення»,
"IV". Це приховує природу змінної від PP, і тоді вам потрібно (уникати
принаймні попередження компілятора!) виконайте переведення типу, коли ви використовуєте змінну у своєму коді.
Таким чином, "OtherPars" має мати вигляд:

OtherPars => 'IV spl'

і коли ви використовуєте його в коді, ви напишете

INT2PTR(gsl_spline *, $COMP(spl))

де макрос Perl API "INT2PTR" використовувався для обробки вказівника, якого слід уникнути
попередження та проблеми компілятора для машин із змішаним 32- і 64-розрядним Perl
конфігурації. З’єднавши це так, як зробив Андрес Джордан (з модифікацією
використовуючи "IV" Джадда Тейлора) у "gsl_interp.pd" у джерелі розповсюдження, ви отримуєте:

pp_def('init_meat',
Pars => 'подвійний x(n); подвійний y(n);',
OtherPars => 'IV spl',
Код =>'
gsl_spline_init,( INT2PTR(gsl_spline *, $COMP(spl)), $P(x),$P(y),$SIZE(n)));'
);

де я видалив виклик обгортки макросу, але це затьмарить обговорення.

Інша незначна відмінність у порівнянні зі стандартною обробкою карти типів у Perl полягає в тому
користувач не може вказати нестандартні розташування карт типів або імена файлів типів за допомогою
Параметр "TYPEMAPS" у MakeMaker... Таким чином, ви можете використовувати лише файл під назвою "typemap" та/або
«IV» трюк вище.

Інше корисний PP ключі in дані операція Визначення
Ви вже чули про ключ «OtherPars». Наразі інших ключів не так багато
для операції з даними, яка буде корисною в звичайному (незалежно від цього) програмуванні PP. в
насправді, було б цікаво почути про випадок, коли ви думаєте, що вам потрібно більше, ніж що
надається на даний момент. Будь ласка, пишіть в одному зі списків розсилки PDL. Більшість інших
ключі, розпізнані "pp_def", дійсно корисні лише для того, що ми називаємо частина операції (Див.
також вище).

Одна річ, яка сильно планується, - це змінна кількість аргументів, яка буде a
трохи складно.

Неповний список доступних ключів:

На місці
Встановлення цієї клавіші позначає підпрограму як робочу на місці, тобто вхід і вихід
підлі однакові. Прикладом є "$a->inplace->sqrt()" (або "sqrt(inplace($a))").

На місці => 1
Використовується, коли підпрограма є унарною функцією, наприклад "sqrt".

Inplace => ['a']
Якщо є більше одного вхідних панелей, вкажіть назву того, який може бути
змінено на місці за допомогою посилання на масив.

Inplace => ['a','b']
Якщо є більше одного вихідного елемента, вкажіть ім’я вхідного елемента та
вивести piddle у посиланні 2-елементного масиву. Це, мабуть, не потрібно, але залишилося
для повноти.

Якщо використовуються погані значення, необхідно подбати про те, щоб забезпечити поширення
badflag, коли використовується inplace; розглянемо цей уривок з Basic/Bad/bad.pd:

pp_def('replacebad',HandleBad => 1,
Pars => 'a(); [o]b();',
OtherPars => 'подвійний newval',
На місці => 1,
CopyBadStatusCode =>
'/* пропагувати badflag, якщо на місці І він змінився */
якщо ( a == b && $ISPDLSTATEBAD(a) )
PDL->propogate_badflag( b, 0 );

/* завжди переконуйтеся, що результат «гарний» */
$SETPDLSTATEGOOD(b);
',
...

Оскільки ця підпрограма видаляє всі погані значення, то вихідний piddle мав поганий прапор
очищено. Якщо запустити на місці (тому "a == b"), то ми повинні сказати всім дітям "a"
що поганий прапор знято (щоб заощадити час, ми дзвонимо
"PDL->propogate_badgflag" лише якщо для вхідного piddle встановлено поганий прапор).

ПРИМІТКА: одна з ідей полягає в тому, що документація для процедури може бути автоматично
позначені, щоб вказати, що його можна виконати на місці, тобто щось подібне до того, як
«HandleBad» встановлює «BadDoc», якщо він не надається (це не ідеальне рішення).

Інше PDL::PP Функції до підтримка лаконічний пакет визначення
Поки що ми описали функції "pp_def" і "pp_done". PDL::PP експортує декілька
інші функції, які допоможуть вам у написанні стислих визначень пакетів розширення PDL.

pp_addhdr

Часто, коли ви використовуєте функції бібліотеки інтерфейсу, як у наведеному вище прикладі, вам доводиться включати
додаткові файли C. Оскільки файл XS генерується PP, нам потрібні деякі засоби для цього
змусьте PP вставити відповідні директиви включення в потрібне місце в згенерований XS
файл. Для цього існує функція «pp_addhdr». Цю функцію також потрібно використовувати
коли ви хочете визначити деякі функції C для внутрішнього використання деякими функціями XS
(які в основному є функціями, визначеними "pp_def"). Включивши ці функції сюди, ви
переконайтеся, що PDL::PP вставляє ваш код перед точкою, де знаходиться фактичний модуль XS
Розділ починається і, отже, буде залишено без впливу xsubpp (пор. perlxs та perlxstut
сторінки керівництва).

Типовий дзвінок буде

pp_addhdr('
#включати /* нам потрібні defs XXXX */
#include "libprotos.h" /* прототипи бібліотечних функцій */
#include "mylocaldecs.h" /* Локальні програми */

static void do_the real_work(PDL_Byte * in, PDL_Byte * out, int n)
{
/* виконати деякі обчислення з даними */
}
');

Це гарантує, що всі необхідні константи та прототипи будуть належним чином включені та
що ви можете використовувати внутрішні функції, визначені тут у "pp_def", наприклад:

pp_def('barfoo',
Pars => ' a(n); [o] b(n)',
GenericTypes => ['B'],
Код => ' int ns = $SIZE(n);
робити_справжню_роботу($P(a),$P(b),ns);
',
);

pp_addpm

У багатьох випадках фактичний код PP (маючи на увазі аргументи викликів "pp_def") є лише частиною
пакет, який ви зараз реалізуєте. Часто є додатковий код Perl і XS
код, який ви зазвичай записали у файли pm і XS, які тепер автоматично
створений ПП. Тож як помістити цей матеріал у ці динамічно згенеровані файли?
На щастя, є кілька функцій, які зазвичай називаються "pp_addXXX", які допоможуть вам
при цьому.

Припустимо, у вас є додатковий код Perl, який повинен входити до згенерованого pm- файл. Це
легко досягається за допомогою команди "pp_addpm":

pp_addpm(<<'EOD');

=head1 НАЗВА

PDL::Lib::Mylib -- інтерфейс PDL до бібліотеки Mylib

=head1 ОПИС

Цей пакет реалізує інтерфейс до пакета Mylib з повним
підтримка потоків та індексування (див. L ).

= вирізати

використовувати PGPLOT;

=head2 use_myfunc
ця функція застосовує операцію myfunc до всіх
елементів вхідного pdl незалежно від розмірів
і повертає суму результату
= вирізати

sub use_myfunc {
мій $pdl = зсув;

myfunc($pdl->clump(-1),($res=null));

повернути $res->sum;
}

EOD

pp_add_exported

Ви, мабуть, зрозуміли ідею. У деяких випадках ви також хочете експортувати свої додаткові
функції. Щоб уникнути проблем із PP, який також заважає @EXPORT
масиву ви просто скажете PP додати ваші функції до списку експортованих функцій:

pp_add_exported('use_myfunc gethynx');

pp_add_isa

Команда "pp_add_isa" працює як функція "pp_add_exported". Аргументи до
"pp_add_isa" додається до списку @ISA, напр

pp_add_isa(' Деякі::Інші::Клас ');

pp_bless

Якщо ваші підпрограми pp_def мають використовуватися як об’єктні методи, використовуйте "pp_bless", щоб вказати
пакет (тобто клас), до якого ваш pp_defed методи будуть додані. Наприклад,
"pp_bless('PDL::MyClass')". За замовчуванням це "PDL", якщо це опущено.

pp_addxs

Іноді ви хочете додати додатковий власний код XS (який зазвичай не пов’язаний з
будь-які проблеми з потоками/індексуванням, але надає деякі інші функції, до яких ви хочете отримати доступ
зі сторони Perl) у згенерований файл XS, наприклад

pp_addxs('','

# Визначити порядковий байт машини

Int
ibigendian()
КОД:
короткий i без знака;
PDL_Byte *b;

i = 42; b = (PDL_Byte*) (void*) &i;

якщо (*b == 42)
RETVAL = 0;
інакше якщо (*(b+1) == 42)
RETVAL = 1;
ще
croak("Неможливо - машина не є ні великим, ні малим байтом!!\n");
ВИХІД:
РЕТВАЛЬ
');

Особливо слід обережно використовувати "pp_add_exported" і "pp_addxs". ПП використовує
PDL::Exporter, отже дозволити PP експортувати вашу функцію означає, що вони будуть додані до
стандартний список функцій, експортованих за замовчуванням (список, визначений тегом експорту
``:Func''). Якщо ви використовуєте «pp_addxs», вам не слід намагатися робити щось, що пов’язано з потоками
або безпосередньо індексація. PP набагато краще генерує відповідний код з вашого
визначення.

pp_add_boot

Нарешті, ви можете додати деякий код до розділу BOOT файлу XS (якщо ви цього не зробили
знаю, що це таке чек perlxs). Це легко зробити за допомогою команди "pp_add_boot":

pp_add_boot(<
descrip = mylib_initialize(KEEP_OPEN);

якщо (опис == NULL)
croak("Не вдається ініціалізувати бібліотеку");

GlobalStruc->опис = опис;
GlobalStruc->maxfiles = 200;
EOB

pp_export_nothing

За замовчуванням PP.pm розміщує всі підпорядковані елементи, визначені за допомогою функції pp_def, у вихідний файл .pm
список EXPORT файлу. Це може створити проблеми, якщо ви створюєте об’єкт підкласу where
ви не хочете експортувати жодні методи. (тобто методи будуть викликатися лише за допомогою
синтаксис $object->method).

Для цих випадків можна дзвонити pp_export_nothing() щоб очистити список експорту. Приклад (ст
кінець файлу .pd):

pp_export_nothing();
pp_done();

pp_core_importList

За замовчуванням PP.pm ставить 'use Core;' рядок у вихідний файл .pm. Це імпортує Core
експортовані імена в поточний простір імен, що може створити проблеми, якщо ви занадто
використання одного з методів Core у поточному файлі. Ви отримуєте повідомлення типу
"Попередження: sub sumover перевизначено у файлі subclass.pm" під час запуску програми.

У цих випадках pp_core_importList можна використовувати, щоб змінити те, з чого імпортується
Core.pm. Наприклад:

pp_core_importList('()')

Це призведе до

використовувати Core();

створюється у вихідному файлі .pm. Це призведе до того, що імена не будуть імпортовані
Core.pm. Так само дзвонить

pp_core_importList(' qw/ barf /')

призведе до

використовувати Core qw/barf/;

створюється у вихідному файлі .pm. Це призведе до імпорту просто "barf".
з Core.pm.

pp_setversion

Я впевнений, що це дозволяє одночасно встановлювати файли .pm і .xs
версії, уникаючи непотрібного перекосу між ними. Щоб скористатися цим, просто мати
наступний рядок у певному місці вашого файлу .pd:

pp_setversion('0.0.3');

Однак не використовуйте це, якщо ви використовуєте Module::Build::PDL. Перегляньте документацію до цього модуля
подробиці

pp_deprecate_module

Якщо певний модуль вважається застарілим, цю функцію можна використовувати, щоб позначити його як
застарів. Це має ефект випромінювання попередження, коли користувач намагається «використовувати».
модуль. Згенерований POD для цього модуля також містить повідомлення про припинення підтримки. The
модуль заміни можна передати як аргумент, наприклад:

pp_deprecate_module( infavor => "PDL::NewNonDeprecatedModule");

Зверніть увагу, що функція впливає тільки попередження про час виконання та POD.

Робить ваш PP функція "приватний"


Скажімо, у вашому модулі є функція під назвою PDL::foo, яка використовує PP
функція "bar_pp" для виконання важкої роботи. Але ви не хочете рекламувати, що "bar_pp"
існує. Для цього ви повинні перемістити свою функцію PP у верхню частину файлу модуля
call

pp_export_nothing()

щоб очистити список "ЕКСПОРТ". Щоб переконатися, що жодної документації (навіть документів PP за замовчуванням).
створений, встановлений

Doc => undef

і щоб запобігти додаванню функції до таблиці символів, встановіть

PMFunc => ''

у вашій декларації pp_def (див. приклад Image2D.pd). Це ефективно зробить
ваша функція PP "приватна". Однак це так завжди доступний через PDL::bar_pp завдяки Perl
модульний дизайн. Але якщо зробити його приватним, користувач буде дуже далеко від нього
як це використати, тож він чи вона несе наслідки!

Скибочка операція


Розділ роботи з фрагментами цього посібника надається з використанням потоку даних та відкладеного оцінювання:
коли вам це потрібно, попросіть Tjl написати це. доставка через тиждень з моменту отримання листа
ймовірність 95%, а двотижнева доставка — 99%.

І в будь-якому випадку, операції з фрагментами вимагають набагато більш глибоких знань про внутрішні елементи PDL
ніж операції з даними. Крім того, складність проблем, пов’язаних з цим
значно вище, ніж при середній операції з даними. Якщо хочеш переконати
самі на цей факт подивіться на Basic/Slices/slices.pd файл у PDL
розповсюдження :-). Тим не менш, функції, створені за допомогою операцій з зрізом, знаходяться на
серцевина маніпулювання індексом і можливостей потоку даних PDL.

Крім того, є багато брудних проблем з віртуальними piddles та vaffines, які ми і розглянемо
повністю пропустити тут.

Скибочки та поганий величини
Операції з фрагментами повинні мати можливість обробляти погані значення (якщо підтримку скомпільовано в PDL).
Найпростіше - подивитися Basic/Slices/slices.pd щоб побачити, як це працює.

Поряд із «BadCode» є також ключі «BadBackCode» та «BadRedoDimsCode» для
"pp_def". Однак будь-який "EquivCPOffsCode" повинен НЕ потрібно змінити, оскільки будь-які зміни є
поглинається визначенням макросу "$EQUIVCPOFFS()" (тобто він обробляється
автоматично за допомогою PDL::PP>.

A кілька ноти on лист a нарізка рутина ...
Наступні кілька параграфів описують написання нової процедури нарізки («діапазон»); будь-який
помилки є CED. (--CED 26 серпня 2002 р.)

Обробка of "попередити" та "барф" in PP код


Для друку попереджувальних повідомлень або переривання/вмирання ви можете викликати "warn" або "barf" з PP
код. Однак ви повинні знати, що ці виклики були перевизначені за допомогою C
макроси препроцесора до "PDL->barf" і "PDL->warn". Ці перевизначення мають місце для того, щоб
запобігти випадковому виклику perl "warn" або "barf" безпосередньо, що може призвести до
segfaults під час pthreading (тобто багатопоточність процесора).

Власні версії PDL "barf" і "warn" будуть ставити в чергу попередження або повідомлення barf до
pthreading завершено, а потім викличте версії perl цих підпрограм.

Дивіться PDL::ParallelCPU для отримання додаткової інформації про pthreading.

КОРИСНІ ПРОЦЕДУРИ


Структура PDL "Core", визначена в Basic/Core/pdlcore.h.PL, містить покажчики на a
кількість процедур, які можуть бути вам корисними. Більшість із цих рутин мають справу з
маніпулювання piddles, але деякі є більш загальними:

PDL->qsort_B( PDL_Byte *xx, int a, int b)
Відсортуйте масив "xx" між індексами "a" і "b". Є також версії для
інші типи даних PDL з постфіксом "_S", "_U", "_L", "_F" і "_D". Використання будь-якого модуля
це має гарантувати, що "PDL::Ufunc" завантажено.

PDL->qsort_ind_B( PDL_Byte *xx, int *ix, int a, int b)
Що стосується "PDL->qsort_B", але цього разу сортування індексів, а не даних.

Процедура "med2d" в Lib/Image2D/image2d.pd показує, як використовуються такі процедури.

МАЙКФАЙЛИ ДЛЯ PP ФАЙЛИ


Якщо ви збираєтеся згенерувати пакет із вашого файлу PP (типові розширення файлів
".pd" або ".pp" для файлів, що містять код PP) найпростіше та найбезпечніше залишити
створення відповідних команд для Makefile. Далі ми окреслимо
типовий формат Makefile Perl для автоматичного створення та встановлення вашого пакета
опис у файлі PP. Більшість правил для створення xs, pm та інших необхідних файлів
з файлу PP вже попередньо визначені в пакеті PDL::Core::Dev. Ми просто повинні
скажіть MakeMaker використовувати його.

У більшості випадків ви можете визначити свій Makefile як

# Makefile.PL для пакета, визначеного кодом PP.

використовувати PDL::Core::Dev; # Підберіть утиліти для розробки
використовуйте ExtUtils::MakeMaker;

$package = ["mylib.pd",Mylib,PDL::Lib::Mylib];
%hash = pdlpp_stdargs($package);
$hash{OBJECT} .= ' додатковий_Ccode$(OBJ_EXT) ';
$hash{clean}->{FILES} .= ' todelete_Ccode$(OBJ_EXT) ';
$hash{'VERSION_FROM'} = 'mylib.pd';
WriteMakefile(%hash);

sub MY::postamble { pdlpp_postamble($package); }

Ось список у $package: спочатку: ім’я вихідного файлу PP, потім префікс для
створених файлів і, нарешті, повної назви пакета. Ви можете змінити хеш у будь-якому
як вам подобається, але було б розумно залишатися в деяких межах, щоб ваш пакет
продовжить працювати з пізнішими версіями PDL.

Якщо ви не хочете використовувати попередньо запаковані аргументи, ось загальний варіант Makefile.PL що ви можете
адаптувати під власні потреби:

# Makefile.PL для пакета, визначеного кодом PP.

використовувати PDL::Core::Dev; # Підберіть утиліти для розробки
використовуйте ExtUtils::MakeMaker;

WriteMakefile(
'NAME' => 'PDL::Lib::Mylib',
'VERSION_FROM' => 'mylib.pd',
'TYPEMAPS' => [&PDL_TYPEMAP()],
'OBJECT' => 'mylib$(OBJ_EXT) Additional_Ccode$(OBJ_EXT)',
'PM' => { 'Mylib.pm' => '$(INST_LIBDIR)/Mylib.pm'},
'INC' => &PDL_INCLUDE(), # додати включати каталоги відповідно до вимог вашої бібліотеки
'LIBS' => [''], # додайте директиви посилань, якщо необхідно
'clean' => {'FILES' =>
'Mylib.pm Mylib.xs Mylib$(OBJ_EXT)
додатковий_Ccode$(OBJ_EXT)'},
);

# Додати правило genpp; це викличе PDL::PP у нашому файлі PP
# аргумент є посиланням на масив, де масив містить три рядкових елементи:
# arg1: ім'я вихідного файлу, який містить код PP
# arg2: базова назва файлів xs і pm, які будуть згенеровані
# arg3: ім'я пакунка, який має бути згенерований
sub MY::postamble { pdlpp_postamble(["mylib.pd",Mylib,PDL::Lib::Mylib]); }

Щоб зробити життя ще простішим, PDL::Core::Dev визначає функцію "pdlpp_stdargs", яка повертає
хеш зі значеннями за замовчуванням, які можна передати (або безпосередньо, або після відповідного
модифікація) до виклику WriteMakefile. Наразі "pdlpp_stdargs" повертає хеш де
ключі заповнюються таким чином:

(
'NAME' => $mod,
'TYPEMAPS' => [&PDL_TYPEMAP()],
'OBJECT' => "$pref\$(OBJ_EXT)",
PM => {"$pref.pm" => "\$(INST_LIBDIR)/$pref.pm"},
MAN3PODS => {"$src" => "\$(INST_MAN3DIR)/$mod.\$(MAN3EXT)"},
'INC' => &PDL_INCLUDE(),
'LIBS' => [''],
'clean' => {'FILES' => "$pref.xs $pref.pm $pref\$(OBJ_EXT)"},
)

Тут $src - це ім'я вихідного файлу з кодом PP, $pref - префікс для згенерованого
Файли .pm і .xs і $mod ім'я модуля розширення, який потрібно створити.

ВНУТРІШНІСТЬ


Внутрішня частина поточної версії складається з великої таблиці, яка містить правила
згідно з якими речі перекладаються, і субтитри, які реалізують ці правила.

Пізніше було б добре зробити таблицю, яку користувач може змінювати, щоб вона відрізнялася
речі можна спробувати.

[Мета-коментар: у майбутньому, сподіваюся, буде більше; на даний момент найкращим буде
щоб прочитати вихідний код :-( або запитати у списку (спершу спробуйте останнє) ]

Додаток A: Дещо ключі визнані by PDL::PP


Якщо не вказано інше, аргументи є рядками. Лише ключі, позначені (погано).
використовується, якщо підтримка поганого значення скомпільовано в PDL.

Pars
визначте сигнатуру вашої функції

OtherPars
аргументи, які не є pdl. За замовчуванням: нічого. Це список, розділений крапкою з комою
аргументи, наприклад, "OtherPars=>'int k; подвійне значення; char* fd'". Див. $COMP(x), а також
той самий запис у Додатку В.

код
фактичний код, який реалізує функціональність; кілька PP макросів і функцій PP
розпізнаються у значенні рядка

HandleBad (погано)
Якщо встановлено значення 1, передбачається, що програма підтримує погані значення та код у BadCode
ключ використовується, якщо є неправильні значення; він також налаштовує так, щоб "$ISBAD()"
тощо можна використовувати макроси. Якщо встановлено значення 0, змусить підпрограму надрукувати попередження, якщо таке є
вхідні piddles мають свій поганий прапорець.

BadCode (поганий)
Укажіть код, який буде використовуватися, якщо у вхідних панелях можуть бути погані значення. Тільки використаний
якщо "HandleBad => 1".

GenericTypes
Посилання на масив. Масив може містити будь-яку підмножину односимвольних рядків `B',
`S', `U', `L', `Q', `F' та `D', які вказують, які типи ваша операція буде приймати.
Значення кожного типу таке:

B - байт зі знаком (тобто знаковий символ)
S - короткий (двобайтове ціле число)
U - короткий без знака
L - довге зі знаком (чотирибайтове ціле число, int у 32-розрядних системах)
Q - довге довге зі знаком (ціле вісім байт)
F - поплавок
D - подвійний

Це дуже корисно (і важливо!) під час взаємодії із зовнішньою бібліотекою. За замовчуванням:
[qw/BSULQFD/]

На місці
Позначте функцію як можливість працювати на місці.

Inplace => 1, якщо Pars => 'a(); [o]b();'
Inplace => ['a'] if Pars => 'a(); b(); [o]c();'
Inplace => ['a','b'], якщо Pars => 'a(); b(); [o]c(); [o]d();'

Якщо використовуються погані значення, необхідно подбати про те, щоб забезпечити поширення
badflag, коли використовується inplace; наприклад, дивіться код для "replacebad" в
Basic/Bad/bad.pd.

Doc Використовується для визначення рядка документації у форматі Pod. Дивіться PDL::Doc для отримання інформації про
Конвенції щодо документації PDL. Примітка: в особливому випадку, коли є рядок PP 'Doc'
один рядок неявно використовується для швидкої довідки І документації!

Якщо поле Doc опущено, PP створить документацію за замовчуванням (зрештою, що йому відомо
про підпис).

Якщо ви дійсно хочете, щоб функція НЕ була жодним чином документована на цьому етапі (наприклад
для внутрішньої процедури або тому, що ви робите це в іншому місці коду) явно
вкажіть "Doc=>undef".

BadDoc (поганий)
Містить текст, повернутий командою "badinfo" (у "perldl") або перемикачем "-b"
до сценарію оболонки "pdldoc". У багатьох випадках вам не потрібно буде вказувати це, оскільки
інформація може бути автоматично створена PDL::PP. Однак, як і належить комп'ютеру-
згенерований текст, він досить ходульний; можливо набагато краще зробити це самому!

NoPthread
Необов’язковий прапорець для вказівки функції PDL НЕ використовувати потоки процесора (тобто
pthreads або POSIX потоки), щоб розділити роботу між кількома ядрами ЦП. Цей варіант є
зазвичай встановлюється на 1, якщо базова функція PDL не є потокобезпечною. Якщо цей варіант
немає, то передбачається, що функція є потокобезпечною. Ця опція діє лише
якщо PDL було скомпільовано з увімкненими потоками POSIX.

PMCode
Функції PDL дозволяють вам передати piddle, в який ви хочете зберегти вихідні дані. Це
це зручно, оскільки ви можете виділити вихідний piddle один раз і повторно використовувати його багато разів; в
Альтернативою може бути, щоб PDL щоразу створював новий piddle, що може втратити обчислення
циклів або, швидше, ОЗП. Ця додаткова гнучкість коштує більшого
складність: PDL::PP має писати функції, які достатньо розумні, щоб підрахувати
аргументи, передані йому, і створюють нові piddles на льоту, але тільки якщо ви цього бажаєте.

PDL::PP достатньо розумний, щоб зробити це, але існують обмеження щодо порядку аргументів і
подібне. Якщо вам потрібна більш гнучка функція, ви можете написати власну частину Perl
wrapper і вкажіть його в ключі PMCode. Рядок, який ви надаєте, повинен (повинен)
визначте функцію Perl з іменем, яке відповідає тому, що ви дали pp_def у першому
місце. Якщо ви захочете в кінцевому підсумку викликати функцію, створену PP, вам знадобиться
поставте всі piddles в точному порядку, зазначеному в підписі: вихідні piddles є
не є необов'язковим, і функція, створена PP, нічого не поверне. Заплутаний
ім'я, яке ти будеш називати _ _int.

Я вважаю, що ця документація потребує додаткового уточнення, але це доведеться зробити.
:-(

PMFunc
Коли pp_def генерує функції, він зазвичай визначає їх у пакеті PDL. Тоді,
у файлі .pm, який він генерує для вашого модуля, він зазвичай додає рядок
по суті копіює цю функцію в таблицю символів вашого поточного пакета з кодом
це виглядає так:

*func_name = \&PDL::func_name;

Це трохи розумніше (він знає, коли загорнути подібні речі в
наприклад, блок BEGIN, і якщо ви вказали щось інше для pp_bless), але
ось в чому суть. Якщо вам байдуже імпортувати функцію у ваш поточний
таблицю символів пакета, ви можете вказати

PMFunc => '',

PMFunc не має інших побічних ефектів, тому ви можете використовувати його для вставки довільного коду Perl
у свій модуль, якщо хочете. Однак вам слід використовувати pp_addpm, якщо ви хочете додати Perl
код до вашого модуля.

Додаток B: PP макроси та Функції


Макрос
Макроси, позначені як (погано), використовуються лише в тому випадку, якщо підтримка поганих значень скомпільована в PDL.

$змінна_з_сиг()
отримати доступ до файлу pdl (за його іменем), який було зазначено в сигнатурі

$COMP(x)
отримати доступ до значення в приватній структурі даних цього перетворення (в основному використовується для
використовувати аргумент, зазначений у розділі "OtherPars")

$SIZE(n)
замінюється під час виконання на фактичний розмір a названий розмір (як зазначено в
підпис)

$GENERIC()
замінено на тип C, що дорівнює типу виконання операції

$P(a) вказівник доступу до PDL з назвою "a" в сигнатурі. Корисно для взаємодії з C
Функції

$PP(a) доступ фізичного покажчика до pdl "a"; в основному для внутрішнього використання

$TXXX(альтернатива, альтернатива)
альтернативи розширення відповідно до типу виконання операції, де XXX є деяким
рядок, якому відповідає "/[BSULFD+]/".

$PDL(a)
повернути покажчик на структуру даних pdl (pdl *) piddle "a"

$ISBAD(a()) (погано)
повертає значення true, якщо значення, збережене в "a()", дорівнює поганому значенню для цього елемента.
Потрібно встановити для параметра HandleBad значення 1.

$ISGOOD(a()) (погано)
повертає true, якщо значення, збережене в "a()", не дорівнює поганому значенню для цього
piddle. Потрібно встановити для параметра HandleBad значення 1.

$SETBAD(a()) (погано)
Встановлює "a()" як рівне поганому значенню для цього piddle. Потрібно встановити "HandleBad".
в 1.

Функції
"цикл (DIMS) %{ ... %}"
цикл над названими розмірами; ліміти генеруються автоматично ПП

"threadloop %{ ... %}"
укладіть наступний код у цикл потоку

"types(TYPES) ​​%{ ... %}"
виконати наступний код, якщо тип операції є одним із "ТИПів"

Додаток C: Функції Імпортований by PDL::PP


Під час «використання PDL::PP» імпортується ряд функцій. До них належать функції, які
керувати згенерованим кодом C або XS, функціями, які керують згенерованим кодом Perl, і
функції, які маніпулюють пакетами та таблицями символів, у які створюється код.

Генерація C та XS код
Основна мета PDL::PP полягає в тому, щоб полегшити вам обгортання потокового механізму навколо вашого
власний код C, але ви також можете робити деякі інші речі.

pp_def
Використовується для обгортання потокового механізму навколо вашого коду C. Практично весь цей документ
обговорюється використання pp_def.

pp_done
Вказує, що ви закінчили з PDL::PP і що він повинен створити свої файли .xs і .pm
на основі інших функцій pp_*, які ви викликали. Ця функція не займає
аргументів.

pp_addxs
Це дає змогу додати код XS до файлу .xs. Це корисно, якщо ви хочете створити Perl-
доступні функції, які викликають код C, але не можуть або не повинні викликати потокову роботу
двигун. XS — це стандартний засіб, за допомогою якого ви обертаєте доступний для Perl код C. Ти можеш
дізнайтеся більше на perlx.

pp_add_boot
Ця функція додає будь-який рядок, який ви передаєте, до розділу XS BOOT. Розділ BOOT
це код C, який викликається Perl, коли ваш модуль завантажується, і корисний для
автоматична ініціалізація. Ви можете дізнатися більше про XS і розділ BOOT на perlxs.

pp_addhdr
Додає код чистого C до вашого файлу XS. Файли XS структуровані таким чином, що повинен використовувати чистий код C
передує специфікаціям XS. Це дозволяє вказати такий код C.

pp_boundscheck
PDL зазвичай перевіряє межі ваших доступів, перш ніж робити їх. Ви можете повернути це
увімкнути або вимкнути під час виконання, встановивши MyPackage::set_boundscheck. Ця функція дозволяє
щоб видалити цю гнучкість під час виконання та ніколи зробити перевірку меж. Це також повертає
поточний статус перевірки меж, якщо викликано без аргументів.

ПРИМІТКА. Я не знайшов нічого про перевірку меж в іншій документації. Це
потребує вирішення.

Генерація Perl код
Багато функцій, імпортованих під час використання PDL::PP, дозволяють змінювати вміст файлу
згенерований файл .pm. Крім pp_def і pp_done, роль цих функцій
в першу чергу для додавання коду до різних частин згенерованого файлу .pm.

pp_addpm
Додає код Perl до згенерованого файлу .pm. PDL::PP фактично відстежує три
різні розділи згенерованого коду: верхній, середній і нижній. Ви можете додати
Perl-код до середнього розділу, використовуючи форму з одним аргументом, де аргументом є
Perl-код, який ви хочете надати. У формі з двома аргументами першим аргументом є an
анонімний хеш лише з одним ключем, який визначає, куди помістити другий аргумент,
це рядок, який потрібно додати до файлу .pm. Хеш є одним із них
три:

{На => 'Вгорі'}
{На => 'Посередині'}
{На => 'Бот'}

Наприклад:

pp_addpm({At => 'Бот'}, <

=head1 Деяка документація

Я знаю, що друкую це в середині свого файлу, але це буде
знизу.

= вирізати

POD

Попередження: якщо в середині вашого файлу .pd ви помістили документацію, призначену для
у нижній частині капсули ви повністю переплутаєте CPAN. З іншого боку, якщо в
у середині вашого файлу .pd ви додаєте деякий код Perl, призначений для нижньої або верхньої частини вашого
.pm файлу, ви можете заплутати себе. :-)

pp_beginwrap
Додає обгортання блоків BEGIN. Однак деякі оголошення можуть бути загорнуті в блоки BEGIN
поведінка за замовчуванням — відсутність такої обгортки.

pp_addbegin
Встановлює код, який буде додано до початку вашого файлу .pm, навіть вище коду, який ви вказали
з "pp_addpm({At => 'Top'}, ...)". На відміну від pp_addpm, виклик this перезаписує все
був там раніше. Загалом, вам, мабуть, не варто його використовувати.

Відстеження Лінія Номери
Коли ви отримуєте помилки компіляції, чи то з вашого C-подібного коду, чи з вашого коду Perl, це може допомогти
щоб повернути ці помилки до номерів рядків у вихідному файлі, у якому виникла помилка
сталося.

pp_line_numbers
Бере номер рядка та (зазвичай довгий) рядок коду. Номер рядка має
вкажіть рядок, з якого починається цитата. Зазвичай це "__LINE__" Perl
literal, якщо ви не використовуєте heredocs, у цьому випадку це "__LINE__ + 1". The
повернутий рядок містить директиви #line, що допомагають компілятору повідомляти про помилки
на належній лінії.

Модифікація Symbol таблиця та Експорт Поведінка
PDL::PP зазвичай експортує всі функції, створені за допомогою pp_def, і зазвичай їх встановлює
в таблицю символів PDL. Однак ви можете змінити цю поведінку за допомогою цих функцій.

pp_bless
Встановлює пакет (таблицю символів), до якого додається код XS. За замовчуванням – PDL,
що загалом те, чого ви хочете. Якщо ви використовуєте благословення за замовчуванням і створюєте a
функцію myfunc, то ви можете зробити наступне:

$piddle->myfunc( );
PDL::myfunc($piddle, );

З іншого боку, якщо ви благословляєте свої функції в інший пакет, ви не можете викликати
їх як методи PDL і повинні викликати їх як:

MyPackage::myfunc($piddle, );

Звичайно, ви завжди можете використовувати клавішу PMFunc, щоб додати свою функцію до символу PDL
стіл, але навіщо це робити?

pp_add_isa
Додає до списку модулів, з яких ваш Модулі успадковує. Список за замовчуванням

qw(PDL::Exporter DynaLoader)

pp_core_importlist
У верхній частині згенерованого файлу .pm є рядок, який виглядає так:

використовувати PDL::Core;

Ви можете змінити це, вказавши рядок у pp_core_importlist. Наприклад,

pp_core_importlist('::Blarg');

призведе до

використовувати PDL::Core::Blarg;

Ви можете використовувати це, наприклад, щоб додати список символів для імпорту з PDL::Core. Для
приклад:

pp_core_importlist(" ':Внутрішній'");

призведе до наступного оператора використання:

використовувати PDL::Core ':Internal';

pp_setversion
Встановлює версію вашого модуля. Версія має бути узгодженою між .xs та .pm
файл, і використовується для того, щоб ваші бібліотеки Perl не страждали від версії
перекіс.

pp_add_exported
Додає до списку експорту будь-які імена, які ви йому дасте. Функції, створені за допомогою pp_def
автоматично додаються до списку. Ця функція корисна, якщо ви визначаєте будь-який Perl
функції за допомогою pp_addpm або pp_addxs, які також потрібно експортувати.

pp_export_nothing
Це обнуляє список експортованих символів. Це, мабуть, краще назвати
"pp_export_clear", оскільки ви можете додати експортовані символи після виклику
"pp_export_nothing". При виклику безпосередньо перед викликом pp_done це гарантує, що ваш
модуль нічого не експортує, наприклад, якщо ви хочете, щоб програмісти використовували лише ваш
функціонує як методи.

Використовуйте PDL::PPp онлайн за допомогою служб onworks.net


Безкоштовні сервери та робочі станції

Завантажте програми для Windows і Linux

Команди Linux

Ad