anonymous@RULINUX.NET~# | Last login: 2024-12-25 11:00:40 |
Регистрация Вход | Новости | Разметка | Пользователи | Галерея | Форум | Статьи | Неподтвержденное | Трекер | Правила форума | F.A.Q. | Ссылки | Поиск |
Статьи - Development | [RSS] |
Допустим, нужно что-то быстро вычислить, а скорости perl не хватает. Для этого в perl существует специальный интерфейс, позволяющий проводить вычисления на языке C. Соответствующие описания находятся в мауналах например perldoc perlxstut или perldoc perlguts. Ниже по тексту несколько примеров как передавать в модули числа и последовательности байтов и получать на выходе различные структуры данных.
Скаляр
Задача: Нужно вычислить сумму чисел до числа передаваемомго функции, т.е. если функции передать число 5, то она должна вернуть число 1+2+3+4+5=15.
Командой h2xs -A -n Perebor создается пакет Perebor с одноименной директорией, в которой находится файл Perebor.xs, в котороый пишется нижеследующий код (этот пример взят с немецкого сайта):
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Perebor PACKAGE = Perebor int sum_numbers(int x) CODE: { int i, sum; for(i=1,sum=0;i<=x;i++) { sum+=i; } RETVAL=sum; } OUTPUT: RETVALДалее нужно сказать perl Makefile.PL, потом make. Вызов модуля при помощи программы
#/usr/bin/perl -w BEGIN { push @INC, "./blib/arch", "./blib/lib"; } use Perebor; $x=Perebor::sum_numbers(5); print "$x\n";
Массив
Для того, чтобы функция возыращала массив или ссылку на массив, надо сделать так:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Perebor PACKAGE = Perebor SV* getnum(int l) INIT: AV* array; CODE: int n = 1; array = newAV(); while (n < l) { av_push(array, newSVnv(n)); n++; } RETVAL = newRV_noinc((SV*)array); OUTPUT: RETVALФункция massiv вызывается как-то так:
#/usr/bin/perl -w BEGIN { push @INC, "./blib/arch", "./blib/lib"; } use Perebor; $x=Perebor::massiv(11); print join " " => @{$x},"\n";В случае двух функций:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Perebor PACKAGE = Perebor SV* massiv(int l) INIT: AV* array; CODE: int n = 1; array = newAV(); while (n < l) { av_push(array, newSVnv(n)); n++; } RETVAL = newRV_noinc((SV*)array); OUTPUT: RETVAL SV* massiv_square(int l) INIT: AV* array; CODE: int n = 1; array = newAV(); while (n < l) { av_push(array, newSVnv(n*n)); n++; } RETVAL = newRV_noinc((SV*)array); OUTPUT: RETVALPerl код такой:
#/usr/bin/perl -w BEGIN { push @INC, "./blib/arch", "./blib/lib"; } use Perebor; $x=Perebor::massiv(11); print join " " => @{$x},"\n"; $x=Perebor::massiv_square(11); print join " " => @{$x},"\n";
Если есть необходимость передать в программу строку чисел, упакованных функцией pack C*, 123 (т.е. числа в 16-тиричном виде), то возникает проблема следующего толка: при обработке символа '\x00' (ноль в 16-тиричном виде), сишнная функция завершит работу, т.к. ноль это конец записи. Для того, чтобы избежать этой проблемы, нужно помимо строки unsigned char передать её длинну, вычисленную функцией perl length:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Perebor PACKAGE = Perebor SV* getnum(unsigned char *number, int l) INIT: AV* array; int sum; double y; CODE: int n = 0; array = newAV(); while (n < l) { sum=number[n++]; av_push(array, newSVnv(sum*sum)); } RETVAL = newRV_noinc((SV*)array); OUTPUT: RETVALВ случае, если не написать unsigned char а просто char, то значения передаваемой переменной будут изменяться в диапазоне от -128 до 128, что испортит картину, написанную маслом.
Perl код, вызывающий функцию getnum, выглядит так:
#/usr/bin/perl -w BEGIN { push @INC, "./blib/arch", "./blib/lib"; } use Perebor; $txt="\x00\x01\x02\x03\x04\xdd\xcd\xfe"; $x=Perebor::getnum($txt,length $txt); print join " " => @{$x},"\n";Хэши
Предположим, теперь нужно построить и вернуть в си-программу хеш
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Hval PACKAGE = Hval SV * getnum(int k) INIT: CODE: HV* hash = newHV(); hv_store(hash, "aaa",3, newSViv(k), 1); hv_store(hash, "bbb",3, newSVpv("xxx",0), 0); RETVAL = newRV( (SV *) hash ); /* bepmsr| qq{kjs m u}x */ OUTPUT: RETVALвозвращает ссылку на хеш, функцию Hval::getnum можно вызвать так:
#!/usr/bin/perl -w use strict; use warnings; use ExtUtils::testlib; use Hval; use Data::Dumper; my $hash_ref = Hval::getnum(10); foreach my $key (keys %$hash_ref)вывод:{ print "$key => $hash_ref->{$key}\n"; } $hash_ref = Hval::getnum(2); foreach my $key (keys %$hash_ref) { print "$key => $hash_ref->{$key}\n"; } $hash_ref = Hval::getnum(7); foreach my $key (keys %$hash_ref) { print "$key => $hash_ref->{$key}\n"; }
File rr.pl not changed so no update needed. [vilfred@mobile100 Hval]$ perl rr.pl bbb => xxx aaa => 10 bbb => xxx aaa => 2 bbb => xxx aaa => 7 [vilfred@mobile100 Hval]$Передача и возврат данных в виде "\x00\ff..." etc... через хэш:
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Hval PACKAGE = Hval SV * getnum(char *data,int l,int k) INIT: CODE: HV* hash = newHV(); hv_store(hash, "unsigned_char",3, newSVpv ((char*) data, l*sizeof(data[0])), 0); RETVAL = newRV( (SV *) hash ); OUTPUT: RETVALПерл-код соответственно такой:
#!/usr/bin/perl -w use strict; use warnings; use ExtUtils::testlib; use Hval; use Data::Dumper; my $txt=pack "C",255; my $hash_ref = Hval::getnum($txt,length $txt,10); foreach my $key (keys %$hash_ref) { # print "$key => $hash_ref->{$key}\n\n"; print "$key => ".unpack "C",$hash_ref->{$key}; print "\n"; }и работает вот так:
[vilfred@mobile100 Hval]$ perl rr.pl uns => 255 [vilfred@mobile100 Hval]$Создание хеша с именоваными ключами и значениями в виде массива и строки в виде \x00\xff...
#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" MODULE = Hval PACKAGE = Hval SV * getnum(char *data,int l,int k) INIT: CODE: HV* hash = newHV(); AV* array = newAV(); av_push(array, newSVnv(34)); av_pushВозвращается хеш с двумя элементами: массивом и строкой unsigned char в виде '\x00\xff...'(array, newSVnv(77)); hv_store(hash, "unsigned_char",3, newSVpv ((char*) data, l*sizeof(data[0])), 0); hv_store(hash, "array",3,(SV*)array, 0); RETVAL = newRV( (SV *) hash ); OUTPUT: RETVAL
#!/usr/bin/perl -w use strict; use warnings; use ExtUtils::testlib; use Hval; use Data::Dumper; my $txt=pack "C",255; my $hash_ref = Hval::getnum($txt,length $txt,10); foreach my $key (keys %$hash_ref) { print "$key => "; print join " | "=> @{$hash_ref->{$key}} if $key ne 'uns'; print "\n\n"; print "$key => ".unpack "C",$hash_ref->{$key} if $key eq 'uns'; print "\n"; }Работа с параллельным портом при помощи xs
Обработка изображений
В gtk2-perl есть функция, позволяющая получать яркость пикселей в виде строки '\xff\x00...'. Т.к. зачастую при обработке изображений важна скорость работы программы, то по скорости get_pixels(), в принципе, может использоваться для обработки одиночных рисунков:
#!/usr/bin/perl use strict; use warnings; use Glib qw(FALSE TRUE); use Gtk2 -init; use ExtUtils::testlib; use HalfWork; my(@criteria,@a,@res,$pix,$button,@m,$img,$pixbuf,$pixels,$w,$h,$c); sub render_image{ my ($txt1,$min, $max); $min=10; $max=440; $img=$ARGV[0] if $ARGV[0]; $img=$_[0] if $_[0]; $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file ($img); $pixels = $pixbuf->get_pixels(); $h = $pixbuf->get_height; $w = $pixbuf->get_width; } while (<$ARGV[0]/*.*>){ print $c++," "; &render_image($_) if m!\.jpg$!; }Скорость работы соответственно:
[vilfred@mobile100 HalfWork]$ time perl fast.pl ../datchik/files/ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 real 0m2.230s user 0m1.893s sys 0m0.320s [vilfred@mobile100 HalfWork]$ perl -e 'print 85/2.2,"\n"' 38.6363636363636 [vilfred@mobile100 HalfWork]$38 кадров 640x480 в секудну.
Чтобы работать не с пикселями а с картинкой, т.е. сразу видеть результаты работы алгоритма, пишется, например, такой код:
#!/usr/bin/perl use strict; use warnings; use Glib qw(FALSE TRUE); use Gtk2 -init; use ExtUtils::testlib; use HalfWork; my(@criteria,@a,@res,$pix,$button,@m,$img,$pixbuf,$pixels,$w,$h,$c); my $window = Gtk2::Window->new ( "toplevel" ); my $hbox = Gtk2::HBox->new (1,1); my $vbox = Gtk2::VBox->new (0,1); my $image = Gtk2::Image->new; my $e; &images(); sub render_image{ my ($txt1,$min, $max); $min=10; $max=440; # die "Usage: $0 imagefile\n" unless @ARGV; $img=$ARGV[0] if $ARGV[0]; $img=$_[0] if $_[0]; $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file ($img); $pixels = $pixbuf->get_pixels(); $h = $pixbuf->get_height; $w = $pixbuf->get_width; my $criteria; $pixbuf = Gtk2::Gdk::Pixbuf->new_from_data ($pixels, # packed image data in a scalar 'rgb', # only 24- or 32-bit RGB are supported 0, # no alpha, data is 24-bit 8, # only 8 bits per sample are supported $w, # in pixels $h, # in pixels $w*3); # number of *bytes* in each row $image->set_from_pixbuf ($pixbuf); $window->set_title("imaging $img"); $img=''; $#res=-1; } $window->add($hbox); $hbox->add($vbox); $vbox->add($image); $button = Gtk2::Button->new("Repaint..."); $button->signal_connect("clicked", \&signal); $vbox->pack_start($button, 1, 1, 5); $window->signal_connect( "destroy" , sub { Gtk2->main_quit ; } ) ; $window->signal_connect (delete_event => sub {Gtk2->main_quit;}); $window->show_all(); eval { Gtk2->main; }; my $cry; open F,">$ARGV[0]/criteria.txt" or die "cant open: $!"; foreach (@criteria){ $cry+=$_ } print F $cry/$#criteria," | ", print join " " => @criteria,"\n"; close F; print "\nуТЕДОЕЕ ЪОБЮЕОЙЕ ЮЙУМБ НБЛУЙНХНПЧ: ",$cry/$#criteria,"\n"; exit 0; sub signal { &render_image($m[$#m]) if $m[$#m] and -f $m[$#m]; pop @m; Glib::Timeout->add (100, sub { $button->clicked; 0}); } sub images{ while (<$ARGV[0]/*.*>){ $m[$c++]=$_ if m!\.jpg$!; } reverse @m; }и результат его работы:
[vilfred@mobile100 HalfWork]$ time perl wheather.pl ../datchik/files/ real 0m12.635s user 0m2.565s sys 0m0.453s [vilfred@mobile100 HalfWork]$ perl -e 'print 85/11,"\n"' 7.72727272727273 [vilfred@mobile100 HalfWork]$т.е. около 8 кадров в секунду. Скриншот программы:
vilfred(*) (2010-03-04 20:39:00)
Подтверждено: vilfred(*) (2010-03-04 20:39:00)
|
|
|
Этот тред читают 1 пользователь: |
Анонимных: 1 Зарегистрированных: 0 |