-+  Documenti
 |-  Bibliografia
 |-  Articoli
 |-  Perlfunc
 |-  F.A.Q.
 |-  F.A.Q. iclp
-+  Eventi
-+  Contatti
-+  Blog
-+  Link
R: Tags
hop (3)



 

Versione stampabile.


Luca Dante Ortolani
Responsabile commerciale di una PMI nel campo ambientale e sviluppatore per passione. Da anni alla ricerca di soluzioni Open Source per le aziende è il creatore di IGSuite, Integrated Groupware Suite http://www.igsuite.org

Vi siete mai chiesti da piccoli perché imparare a memoria le "tabelline" ? Beh, pensate a come sarebbe stata più difficile la vostra vita se ogni volta che dovevate fare ad esempio 6 x 5 aveste dovuto sommare 6 volte 5!

Allora perché dobbiamo rendere difficile la vita alle nostre applicazioni? In questo articolo vedremo come far loro imparare a memoria le "tabelline".

Il concetto di "Memoization" espresso in HOP da Dominus è proprio questo! Creare un qualsiasi sistema per "memorizzare" i risultati ottenuti da una elaborazione e poi riutilizzarli in un secondo tempo, senza dover rielaborare ogni volta il risultato.


È chiaro che questa tecnica non è applicabile ad ogni situazione, ma senza problemi la possiamo utilizzare là dove l'elaborazione è generata da valori che si potranno ripresentare uguali nel tempo. Stessi valori, stessi risultati.

Fare attenzione a quest'ultimo enunciato! Sono moltissimi i casi in cui con stessi valori in tempi diversi, otteniamo diversi risultati! Allo sviluppatore quindi l'analisi dell'applicabilità di tale sistema.

In questo articolo andremo ad ottimizzare una piccola applicazione che apre un file di log di qualsiasi tipo e sostituisce al suo interno tutti gli indirizzi IP con i relativi host name risolti. L'idea è nata da una domanda fatta su it.comp.lang.perl qualche tempo fa.

Prima...


Prima di addentrarci nell'uso della tecnica del "memoization" esaminiamo i passi che intraprenderemo per risolvere il problema.

Step 1. Apriamo il file e ne effettuiamo il parsing riga per riga attraverso una semplice RE.
Step 2. Se troviamo un indirizzo IP, che nel nostro semplice caso dovrà solo corrispondere ad una RE del tipo \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} (non vogliamo addentrarci in ulteriori problemi di individuazione degli IP), lanciamo una nostra sub che risolve l'IP e restituisce l'hostname, sostituendolo all'IP.
Step 3. Scriviamo la riga modificata in un file nuovo, creato appositamente.

Ecco la nostra applicazione...


#! /usr/bin/perl

use strict;
use Socket;

## Apriamo il file di log da cui leggere
open (IN, $ARGV[0]) or die("Can't read log file");
## Apriamo un nuovo file in cui scrivere
open (OUT, ">$ARGV[0].resolved") or die("Can't write new log file");

while (<IN>)
 {
  ## Sostituiamo gli indirizzi IP con gli Host name
  s/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ip2host($1)/eg;
  print OUT $_; 
 }

close (IN);
close (OUT);

sub ip2host
 {
  my $ip = shift;
  my $addr = inet_aton( $ip );
  return gethostbyaddr( $addr, AF_INET );
 }

... e dopo


La nostra applicazione funzionerà alla perfezione risolvendo ogni indirizzo IP all'interno del nostro file di log, ma ora è arrivato il momento di insegnarle le "tabelline"!

Analizzando un file di log di Apache noteremo che la nostra applicazione si troverà sicuramente nella situazione di dover risolvere più volte lo stesso indirizzo IP, interrogando ogni volta il nameserver del sistema in cui opera. Perché allora non farle ricordare il nome relativo all'IP facendole effetuare una ricerca solo nel caso in cui non ne sia a conoscenza?

In questo modo eviteremo numerose interrogazioni al nameserver che sicuramente ottimizzeranno i tempi di risposta della nostra applicazione.

Dominus nel suo libro elenca numerose soluzioni adatte a memorizzare i risultati (il modulo Storable o lo stesso modulo di Dominus: Memoize), ma tra queste vogliamo utilizzare la più semplice ed applicabile senza troppi sforzi mentali, ma soprattutto adatta ai fini di comprendere la tecnica del "memoization".

Salveremo il nostro valore all'interno di un hash la cui chiave è rappresentata dai valori passati alla procedura. Nel nostro caso fortunatamente e volutamente i "valori" sono rappresentati soltanto dall'indirizzo IP che utilizzeremo direttamente come chiave dell'hash.

La cosa interessante nell'illustrazione in HOP dell'uso di un hash come "memoria" dei nostri risultati, è il metodo utilizzato per limitare lo scope che avrà l'hash. Infatti per non definire un hash con scope globale che potrebbe "inquinare" altre parti dell'applicazione, Dominus utilizza un "blocco" di codice vale a dire racchiude tra parentesi graffe i confini dello scope dell'hash.

Ecco un esempio:

{
 my %hash;
 sub miasub
  {
   my stuff...
  }
}

Nell'esempio riportato %hash avrà uno scope limitato al blocco che lo racchiude, sarà visibile quindi esclusivamente all'interno della sub "miasub()".

Fatto questo ora non ci rimane che ridefinire la nostra procedura ip2host() in modo che adotti la nostra tecnica di memoization. La nostra procedura quando verrà richiamata per convertire un indirizzo IP, non dovrà far altro che controllare che non ne conosca già il valore. Nel caso in cui non "ricorderà" nessun valore relativo all'IP richiesto lo elaborerà salvandolo in un hash che avrà come chiave l'IP e come valore il nome dell'host. Nel caso contrario restituirà semplicemente il valore dell'hash indicizzato dall'indirizzo IP, evitando una nuova chiamata al DNS del sistema.

Questa che segue è la versione "memoized" di ip2host():

{
 my %hosts;
 sub ip2host
  {
   my $ip = shift;
   if ( $hosts{$ip} )
    {
     return $hosts{$ip}
    }
   else
    {
     my $addr = inet_aton( $ip );
     return $hosts{$ip} = gethostbyaddr( $addr, AF_INET );
    }
  }
}



Ecco quindi la nostra soluzione finale nella sua completezza:

#! /usr/bin/perl
use strict;
use Socket;

## Apriamo il file di log da cui leggere
open (IN, $ARGV[0]) or die("Can't read log file");
## Apriamo un nuovo file in cui scrivere
open (OUT, ">$ARGV[0].resolved") or die("Can't write new log file");

while (<IN>)
 {
  ## Sostituiamo gli indirizzi IP con gli Host name
  s/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ip2host($1)/eg;
  print OUT $_; 
 }

close (IN);
close (OUT);

{
 my %hosts;
 sub ip2host
  {
   my $ip = shift;
   if ( $hosts{$ip} )
    {
     return $hosts{$ip}
    }
   else
    {
     my $addr = inet_aton( $ip );
     return $hosts{$ip} = gethostbyaddr( $addr, AF_INET );
    }
  }
}


Il libro di Dominus merita veramente di essere "acquistato", abbiamo soltanto dato un accenno della tecnica del memoization trascurando tanti aspetti e considerazioni affrontate dall'autore nel suo testo, e non volevamo, e certo nemmeno avremmo potuto, essere ridondanti rispetto alla splendida trattazione su HOP.

Detto questo, una raccomandazione... non dimenticate di ripassare le tabelline!


Ti è piaciuto questo articolo? Iscriviti al feed!











Devo ricordare i dati personali?






D:
Annunci Google