#!/usr/bin/perl
# [c] piotao, 20070112
# Program wykonujacy mnozenie macierzy
# Uruchomienie:
# pomnoz.pl plik-danych.txt
# - plik danych musi zawierac dwa bloki liczb, w N kolumnach i M wierszach
# pomiedzy macierzami musi byc przynajmmiej jeden pusty wiersz
use strict;
use warnings;
# deklarujemy dane
my $N; # wymiar poziomy macierzy
my $M; # wymiar pionowy macierzy
# macierze:
my @A = my @B = my @C = (); # wszystkie na razie sa pustymi tablicami
# okreslenie argumentow programu
# i zdecydowanie, czy dane beda podane z klawiatury interaktywnie, wczytane
# z pliku danych lub wczytane nieinteraktywnie ze standardowego wejscia
if( defined $ARGV[0] and -f $ARGV[0] ){ # mamy jeden argument i jest to plik
#otwieramy i wczytujemy plik
#zakladamy ze macierze sa wpisane w kolumnach i wierszach tak jak wyglada ich normalna
#reprezentacja, a nastepna macierz jest oddzielona od poprzedniej jedna pusta linia, np:
# 1 2 3 4 5
# 2 3 4 5 6 <---n--->
# 3 4 5 6 7
#
# 1 2 3 ^
# 2 3 4 |
# 4 3 4 n
# 4 3 2 |
# 2 3 4 v
#
my $macierz = 'a'; # pierwsza macierz, do ktorej czytamy, to A
my $file; # deklarujemy lokalna zmienna plikowa
if( open $file,'<',$ARGV[0] ){ # jezeli udalo sie otworzyc plik, to mamy go w uchwycie $file
while( <$file> ){ # wczytujemy w petli pojedyncze wiersze (do zmiennej $_)
chomp; # usuwamy z kazdego kolejnego wiersza znak nowej linii
if($_ eq ''){ # jezeli wczytalismy pusta linie
$macierz = 'b'; # zmieniamy macierz, do ktorej wczytujemy dane
next; # i od razu zaczynamy nastepna iteracje petli
}
my @wiersz = split; # dzielimy wczytany wiersz na osobne liczby i wpisujemy je do tablicy
if($macierz eq 'a'){ # jezeli wczytywanie odbywa sie do macierzy A, to
push @A,[@wiersz]; # zapamietujemy wiersz w macierzy A
}
else{ # a w przeciwnym wypadku
push @B,[@wiersz]; # zapamietujemy wczytany wiersz w macierzy B
}
}
}
}
else{ # nie podano zadnego pliku lub plik nie istnieje.
die "Nie podano pliku z danymi macierzy do przemnozenia. Uruchom program z nazwa pliku jako argumentem.\n";
}
# po wczytaniu macierzy, okreslamy ich rozmiar na podstawie macierzy A
$N = scalar @{$A[0]}; # dlugosc pierwszego wiersza macierzy to wymiar poziomy
$M = scalar @A; # dlugosc tabeli @A to wymiar pionowy macierzy
# sprawdzamy rozmiary macierzy B
my $Mb = scalar @{$B[0]}; # wymiar poziomy macierzy B (musi byc rowny pionowemu A)
my $Nb = scalar @B; # wymiar pionowy macierzy B (musi byc rowny poziomemu B)
unless($Mb == $M and $Nb == $N){
# gdy rozmiary macierzy nie pasuja do siebie, napisz to i zdechnij
die "Nie pasuja do siebie wymiary macierzy A i B! A=[$M,$N], B=[$Mb,$Nb]\n";
}
# teraz, gdy dane mamy wczytane, mozemy smialo mnozyc macierze!
for(my $n=0; $n<$N; $n++){ # po wierszach @A i kolumnach @B
for(my $m=0; $m<$M; $m++){ # po kolumnach @A i wierszach @B
for(my $k=0; $k<$N; $k++){ # petla sumujaca wyniki mnozen
$C[$n][$m] += $A[$n][$k] * $B[$k][$m];
}
}
}
# piszemy teraz macierz A:
map{ map{ printf "%4i",$_ } @{$_};print "\n" } @A;
print "* \n";
#piszemy macierz B:
map{ map{ printf "%4i",$_ } @{$_};print "\n" } @B;
print "= \n";
# a teraz wypisywanie macierzy wynikowej:
# ale najpierw sprawdzamy, jaka jest najwieksza dlugosc liczby
# w calej macierzy, aby ladnie dobrac szerokosc kolumn
my $len = 0;
my $max = $len;
for(my $m=0;$m<$N;$m++){
for(my $n=0;$n<$M;$n++){
$len = length($C[$m][$n]);
if($max < $len){ $max = $len }
}
}
## sposob bardziej perlowy moze byc np. taki (sprawdz, to dziala)
#for(map{map{ $_ } @{$_} } @C){
# $max = ( ($len = length $_) > $max) ? $len : $max;
#}
# majac dlugosc najdluzszego elementu macierzy C, ustalamy format
# wypisywanych liczb:
$max++;
my $format = "\%${max}i";
# (ten sposob pisania wykorzystuje dwie zaglebione petle for
# i moze niektorym z Was wydawac sie prostszy, ale raczej
# nie jest prostszy... :) prostsze jest wykorzystanie MAP)
for(my $m=0;$m<$N;$m++){
for(my $n=0;$n<$M;$n++){
printf $format,$C[$m][$n];
}
print "\n";
}