C/C++ come condividere dati fra diversi processi con una DLL

compilatori C/C++

Le DLL sono librerie di funzioni, che necessitano di altri programmi per essere eseguite. Chiunque è in grado di costruirsi la propria libreria di funzioni, semplicemente con la conoscenza del linguaggio C o C++ e un compilatore. Fra questi ce ne sono in particolare due molto usati e gratuiti: Bloodshed DevC++ con licenza GNU e Microsoft Visual C++ Express che pure è un ambiente di sviluppo gratuito con limitazioni più che altro nei wizard e negli strumenti di aiuto.

Normalmente, se costruiamo una DLL e questa viene utilizzata da diverse applicazioni ogni processo che carica quella DLL avrà la sua copia delle variabili, anche fossero dichiarate come globali. Questo è un comportamento del tutto logico e consente di dare stabilità e sicurezza al sistema.
Potrebbe tuttavia esserci la necessità di condividere dati fra processi diversi, in questo caso esiste un modo di dichiarare una variabile in una sezione di memoria condivisa.
Vediamo come condividere una variabile che chiameremo banalmente sharedint, appunto di tipo int, utilizzando la sintassi adatta in modo specifico al compilatore Visual C++, vedremo in seguito come ottenere lo stesso risultato utilizzando il compilatore Bloodshed DevC++ (che si appoggia a gcc).
Per prima cosa, dobbiamo fornire al pre-processore le seguenti direttive pragma:

1
2
3
4
5
#pragma data_seg("SHARED")  // Begin the shared data segment.
// Define variable
int sharedint = 0;
#pragma data_seg()          // End the shared data segment 
#pragma comment(linker, "/section:SHARED,RWS")

Quindi definiamo la funzione DllMain che deve essere sempre presente in qualsiasi DLL. In realtà, quando scegliamo un progetto di tipo DLL sarà l’IDE stesso a scrivere questo per noi!
Notate che la funzione è preceduta da due modificatori: il primo: BOOL non è altro che un typedef ad un int ed è definito nell’header windows.h il secondo: APIENTRY, più interessante, è un typedef a WINAPI che è a sua volta un typedef a _stdcall. Si tratta di una calling convention che impone alla funzione chiamata di ripulire lo stack, compito che di default ( _cdecl ) viene invece eseguito dal chiamante. Se utilizzeremo la nostra DLL in Visual Basic, è richiesto l’utilizzo di _stdcall.
Infine definiamo due funzioni, che saranno quelle esportate dalla DLL:

  • SetNumber(int num1) che prende come input un intero e lo memorizza nella variabile definita nell’area di memoria condivisa
  • GetNumber() che restituisce l’intero impostato nella variabile definita nell’area di memoria condivisa

Ecco il codice completo per Visual C++:

// dllmain.cpp: definisce il punto di ingresso per l'applicazione DLL.
#include "stdafx.h"
 
#pragma data_seg("SHARED")  // Begin the shared data segment.
// Define variable
int sharedint = 0;
#pragma data_seg()          // End the shared data segment 
#pragma comment(linker, "/section:SHARED,RWS")
 
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}
 
 
int __stdcall SetNumber(int num1)
{
	sharedint = num1;
	return TRUE;
}
int __stdcall GetNumber ()
{
	return sharedint;
}

Ci siamo quasi, ma prima di compilare dovremo informare il linker quali sono le funzioni che vogliamo esportare. Questo avviene preparando un file di definizioni (.def) in questo modo:

LIBRARY shareIntVc.dll
EXPORTS
SetNumber
GetNumber

Impostiamo il riferimento al file DEF nella finestra delle proprietà del progetto come nella figura qui sotto:

proprieta' del linker

Compiliamo ed il gioco è fatto! Se non abbiamo avuto errori dal compilatore, troveremo la DLL nella sottocartella del progetto: debug.

Il progetto per Visual C++ 2008 lo potete prelevare qui.
Per testare la DLL potete prelevare questo progetto in Visual Basic 6, avendo cura di modificare il file module1.bas (che contiene le dichiarazioni delle funzioni importate), indicando il percorso corretto della DLL sul vostro PC e ricompilando il sorgente. Lanciando più istanze dell’eseguibile Visual Basic sarà possibile leggere da ogni istanza la variabile impostata.

Vediamo infine come cambia il codice della nostra DLL utilizzando il compilatore Bloodshed DevC++: l’unica differenza sta nella sostituzione delle direttive pragma con: l’utilizzo dello specificatore di attributi delle variabili : __attribute__((section ("shared"), shared))
Ecco il codice completo per Bloodshed DevC++:

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
#include 
#include 
 
#include 
 
int sharedint __attribute__((section ("shared"), shared)) = 0;
 
BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,
                       DWORD reason        /* Reason this function is being called. */ ,
                       LPVOID reserved     /* Not used. */ )
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        break;
 
      case DLL_PROCESS_DETACH:
        break;
 
      case DLL_THREAD_ATTACH:
        break;
 
      case DLL_THREAD_DETACH:
        break;
    }
 
    /* Returns TRUE on success, FALSE on failure */
    return TRUE;
}
 
int _stdcall SetNumber(int num1) {
    sharedint = num1;
    return TRUE;
}
 
int _stdcall GetNumber() {
    return sharedint;
}

Anche qui è necessario definire le funzioni esportate in un file di definizioni che sarà identico a quello utilizzato con Visual C++, e impostarlo nella sezione “Opzioni del progetto” -> “Parametri” -> “Linker”, come mostrato nella figura qui sotto:

proprieta' del linker

Il progetto per Bloodshed DevC++ lo potete prelevare qui.

Conclusioni:

L’utilizzo della memoria condivisa con le DLL si rivela utile per scambiare dati fra diverse applicazioni, ma bisogna fare attenzione alle potenziali violazioni della memoria. Per ottenere un funzionamento sicuro, sarebbe necessario implementare un sistema di sicronizzazione dell’accesso ai dati condivisi attraverso un mutex o una critical section.

Riferimenti ed approfondimenti:


luciano ha scritto:

si ok l’ho provata e funziona perfettamente ..
mi chiedo se anziche un int volessi una una variabile stringa
come diventerebbela dll ?
ciao Luciano
02.11.10 18:50
Mario Spada ha scritto:

@Luciano: Con le stringhe il discorso si complica un poco perché il C tratta le stringhe come byte array terminate da un NULL char, mentre VB usa BSTR che sono stringhe unicode. Ad ogni modo la cosa è fattibile, e nemmeno troppo complicata. Purtroppo in questo momento non ho tempo di preparare un esempio, ma ti posso suggerire questi due link molto esaurienti:
1) http://sandsprite.com/CodeStuff/Writing_A_C_Dll_for_VB.html
2) http://support.microsoft.com/kb/187912
08.11.10 19:20

PHP: connettere un database MS Access

Connessione PHP-MS AccessIn questo post propongo una mini web-application in linguaggio PHP che consente di interagire con un database Microsoft Access. La limitazione principale è che il server deve girare in ambiente Windows. La connessione al database sfrutta la classe COM che fornisce un ambiente idoneo all’utilizzo di diversi tipi di componenti COM come per esempio applicazioni Word, Excel e Powerpoint, ma anche ADO. Questa classe, nel pacchetto PHP per Windows, è integrata nella distribuzione, e non è necessaria nessuna installazione. Per quanto riguarda la versione, sembra che quella minima sia la 4.1.x, ma non ho avuto modo di fare prove. Suggerirei comunque di utilizzare le versioni 5.2.x per stare tranquilli!

Una generica connessione ad un database MS Access, ed il riempimento di un recordset può essere avviata con queste istruzioni:

1
2
3
4
5
6
7
8
$cn = new COM("ADODB.Connection");
$cnStr = "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=".
            realpath("./nomeDatabase.mdb").";";
$cn->open($cnStr);
$rs = $cn->execute("SELECT * FROM nomeTabella");
...
$rs->Close();
$cn->Close();

Tornando alla mini-applicazione che ha uno scopo prettamente dimostrativo, le sue funzioni sono:

  • Selezionare una tabella di un file MS Access e mostrare l’elenco dei records
  • Selezionare attraverso la chiave primaria un record e modificare uno o più campi
  • Selezionare attraverso la chiave primaria un record e cancellarlo
  • Inserire un nuovo record

Per poter utilizzare l’applicazione, è necessario innanzitutto modificare le impostazioni definite nel file config.php definendo il nome e il percorso del database, il nome della tabella, il nome e la posizione della chiave primaria e la possibilità di attivare o meno la modifica e cancellazione dei record attraverso i link alla chiave primaria.

Il codice del file principale che mostra la lista dei record e i form per le modfiche, le cancellazioni e gli inserimenti è disponibile leggendo il file test.php.
Un ultimo file: modify.php si occupa di gestire le POST ed effettuare le query di inserimento, aggiornamento e cancellazione.

Alcune annotazioni: per semplificarsi la vita con Access è bene non dare mai il nome “note” ad un campo, perché è una parola chiave. Se proprio fosse necessario, bisognerà riferirsi a quel campo con la forma nometabella.note.
Altra considerazione: se in PHP è attiva la direttiva magic_quotes_gpc le variabili POST verranno trattate con i caratteri di escape per l’apostrofo.
Per memorizzare questi dati nel database, sarà quindi necessario fare prima uno stripslahes() e poi rimpiazzare gli apostrofi raddoppiandoli (che è il modo di “escapare” in Access!).
Infine, per motivi di tempo non ho gestito gli errori della classe COM, però è possibile farlo controllando le eccezione tramite la classe com_exception.

Molte curiosità sull’argomento trovano risposta in questa FAQ

Download:

Il pacchetto comprendente tutti i file PHP, il foglio stile e il database MS Access fatture.mdb è scaricabile qui.

Uno screenshot:

Screenshot dell'applicazione
Screenshot dell’applicazione

Conclusioni:

Sono convinto che le cose che sono nate per stare in coppia diano i risultati migliori, quindi PHP con MySQL. Però avere a disposizione la possibilità di interagire con un database così diffuso e così fortemente legato alle applicazioni Microsoft (Access si sposa bene con Visual Basic!) è un’ottima cosa. Non fosse altro per poter gestire delle emergenze, uno scambio di dati, la migrazione dei dati e via dicendo. O magari semplicemente per non perdere tempo e riutilizzare vecchie applicazioni con il vantaggio delle web-application.

Riferimenti ed approfondimenti:


andrea ha scritto:

l’esempio riportato è perfetto!

Alex ha scritto:

Molto utile, sono del parere che gli esempi pratici siano molto più efficaci e comprensibili di una una tonnellata di teoria.
Grazie

PHP: calcolare la deviazione standard, la varianza, la covarianza e la correlazione

Karl PearsonPer rimanere nel tema del precedente post sulla regressione lineare, cioè la statistica, vi propongo una funzione PHP davvero semplicissima che calcola altri quattro fra i più utilizzati indici della cosiddetta “statistica descrittiva”:

1. deviazione standard
2. varianza
3. covarianza
4. correlazione di Pearson

Vediamo brevemente cosa rappresentano e come si calcolano.

Le prime due (deviazione standard e varianza) sono strettamente legate, e servono a fornire la dimensione della dispersione dei dati. Per capire meglio, immaginiamo due serie di dati: a = { 2, 4, 3, 2.5, 3.5, 3 } e b = { 1, 5, 9, -3, -12, 18 }.
In entrambi i casi la media è 3, mentre la deviazione standard di a è = 0.6455 e quella di b è = 9.3986. La deviazione standard ci dice che i dati di b sono molto più dispersi di quelli di a ( …per chi non lo avesse già capito ad occhio!). Bisogna però pensare che la serie potrebbe essere molto lunga e a quel punto il metodo “occhio-metrico”, ovviamente non ci sarebbe di aiuto!

La deviazione standard viene calcolata facendo la radice quadrata della media dei quadrati degli scarti. In effetti elevando al quadrato gli scarti, ci si mette al riparo dalla compensazione che potrebbe essere data dagli scarti positivi e quelli negativi che altrimenti, si annullerebbero

La formula per il calcolo della deviazione standard è:

Deviazione standard

La varianza è il quadrato della deviazione standard, e non ci dice nulla di più o di meno…

Formula della varianza:

Varianza.png

La covarianza ci indica invece quanto sia contemporanea la variazione di due variabili. E’ interessante notare che, se le due variabili coincidono, ovvero x=y la covarianza diventa la varianza di x.

La formula è:

Covarianza.png

I valori che assume la covarianza, non sono troppo leggibili… nel senso che non danno immediatamente il senso della variazione, se non agli addetti ai lavori. In nostro soccorso però, esiste un’altra misura che già dal nome, ci pare di più immediata leggibilità: la correlazione.

Indice di correlazione di Pearson:

Correlazione

La correlazione di Pearson (nella foto in alto) è il risultato della covarianza diviso il prodotto delle deviazioni standard delle due variabili che si stanno confrontando. Questa misura serve a capire quanto due serie di variabili casuali siano legate fra loro, si dice, appunto: correlate.

Questo coefficiente assume sempre valori compresi fra -1 e +1 e rileggendo le definizioni dei parametri precedenti, è facile capire il perché!

Analizzando il risultato possiamo stabilire che:

  • se ρxy > 0 esiste una correlazione positiva, tanto maggiore quanto ci si avvicina a ρxy = 1
  • se ρxy = 0 non c’è correlazione fra le due variabili, ovvero sono indipendenti
  • se ρxy < 0 esiste una correlazione negativa, tanto maggiore quanto ci si avvicina a ρxy = -1

Sembra piuttosto semplice, ma attenzione alle trappole della statistica: se due variabili danno come risultato del coefficiente di correlazione: 0.87, non è necessariamente detto che abbiano un rapporto di causalità diretto (x dipende da y o viceversa). E’ altrettanto probabile che entrambe dipendano da una terza variabile z che rappresenta una causa comune.

Ed ecco dunque il codice PHP della funzione che restituisce queste misure statistiche. (Ho liberamente tradotto il codice di un programma GW-BASIC scritto da Roberto Vacca e pubblicato sul libro “Anche tu matematico” che è possibile acquistare qui e che consiglio vivamente a tutti di leggere!)

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
function correl($X,$Y)
{
  if (!is_array($X) && !is_array($Y)) return false;
  if (count($X) <> count($Y)) return false;
  if (empty($X) || empty($Y)) return false;
 
  $n = count($X);
  $mediaX = array_sum($X)/$n; // media delle x
  $mediaY = array_sum($Y)/$n; // media delle y
 
  $SS = 0;
  $SX = 0;
  $SY = 0;
 
  for($i=0;$i<$n;$i++){
    $SS += ($X[$i] - $mediaX) * ($Y[$i] - $mediaY);
    $SX += pow(($X[$i] - $mediaX),2);
    $SY += pow(($Y[$i] - $mediaY),2);
  }
 
  $M = sqrt($SX)/sqrt($n);
  $M2 = pow($M,2);
  $N = sqrt($SY)/sqrt($n);
  $N2 = pow($N,2);
  $RR = $SS / (sqrt($SX) * sqrt($SY));
 
  $res = array("media x"=>$mediaX, 
               "media y"=>$mediaY,
               "correlazione"=>$RR,
               "varianza x"=>$M2,
               "varianza y"=>$N2,
               "dev. standard x"=>$M,
               "dev. standard y"=>$N
              );
 
  return $res;
}

Conclusioni:

La statistica viene spesso usata per trarre conclusioni tanto categoriche quanto errate. Questo perché a fronte di una certa difficoltà nell’eseguire i calcoli, sembra molto semplice trarre le conclusioni. Ma le cose non stanno proprio così perché, riprendendo appunto una citazione latina dal libro di Roberto Vacca: “Post hoc, ergo propter hoc” (trad.: “dopo di questo, quindi a causa di questo”) è una conclusione semplicistica che non rispecchia quasi mai la realtà, che si presenta spesso decisamente più complessa!

Riferimenti ed approfondimenti:

PHP: calcolare la retta di regressione lineare con i minimi quadrati

Grafico della retta di regressione lineare
Grafico della retta di regressione lineare

Uno strumento statistico molto usato in tutti i campi è la regressione lineare con il metodo dei minimi quadrati. Questa procedura serve a trovare una curva che interpoli al meglio una serie di dati campionari, che siano in una certa relazione fra di loro. Esaminiamo il caso più semplice, quello in cui la relazione sia lineare e quindi la migliore curva interpolante sia una retta.
Il metodo dei minimi quadrati si basa sul principio per il quale la migliore curva interpolante di un dato insieme di punti è la curva che ha la proprietà di avere minima la somma degli scarti quadratici, ovvero le differenze elevate al quadrato delle singole distanze fra i punti dati e i punti corrispondenti della retta interpolante.

Il procedimento per determinare i punti della nostra retta viene calcolato risolvendo:

Equazione della retta dei minimi quadrati

sapendo che il termine noto si ricava da:

termine noto della retta dei minimi quadrati

sostituendo q e sviluppando il quadrato si ottiene:

equazione parabola

… a vederla così sembra bruttina, ma con le opportune sostituzioni rispetto a m:

sostituzioni rispetto a m

otteniamo:

equazione parabola semplice

… adesso è più leggibile, è una equazione di una parabola. Trovare il valore minimo equivale a trovare il vertice. Qui bisognerebbe recuperare qualche reminiscenza sul calcolo delle derivate, comunque la soluzione è:

vertice della parabola

m, guarda caso… è anche il coefficiente angolare della nostra retta: quello che ci mancava!
Ora abbiamo in mano tutti i dati per costruire l’algoritmo.
Ecco quindi il codice della funzione, sviluppato in linguaggio PHP:

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
function regressione($X,$Y)
{
  if (!is_array($X) &amp;&amp; !is_array($Y)) return false;
  if (count($X) &lt;&gt; count($Y)) return false;
  if (empty($X) || empty($Y)) return false;
 
  $regres = array();
  $n = count($X);
  $mx = array_sum($X)/$n; // media delle x
  $my = array_sum($Y)/$n; // media delle y
  $sxy = 0;
  $sxsqr = 0;
 
  for ($i=0;$i&lt;$n;$i++){
    $sxy += ($X[$i] - $mx) * ($Y[$i] - $my);
    $sxsqr += pow(($X[$i] - $mx),2); // somma degli scarti quadratici medi
  }
 
  $m = $sxy / $sxsqr; // coefficiente angolare
  $q = $my - $m * $mx; // termine noto
 
  for ($i=0;$i&lt;$n;$i++){
    $regres[$i] = $m * $X[$i] + $q;
  }
 
  return $regres;
}

La funzione restituisce l’array delle ordinate della retta di regressione. I dati campione si riferiscono al solito rapporto peso / altezza di alcune persone. Per una più chiara rappresentazione dei dati, è necessario realizzare un grafico con il modello “dispersione” per i dati campionari e il modello “retta” per i dati della regressione.

Ho utilizzato il plugin di JQuery: Flot, facendo generare server-side lo script necessario alla rappresentazione del grafico. L’esempio di applicazione di questa funzione e del grafico correlato è visibile in questa demo.

Per completezza occorre dire che bisognerebbe calcolare anche il coefficiente R2 che fornisce indicazioni sulla qualità della correlazione rispetto ai dati. Il coefficiente varia tra 0 e 1 e, tanto più si avvicina a 1, tanto più i dati sono ben correlati.

Conclusioni:

Un breve ripasso di statistica e matematica delle superiori si è reso necessario per sviluppare un algoritmo molto usato, ma non sempre con cognizione di causa!

Riferimenti ed approfondimenti:

PHP: test di algoritmi per generare permutazioni con e senza ricorsione

Benchmarks di algoritmi per le permutazioni
Benchmarks di algoritmi per le permutazioni

Una definizione abbastanza intuitiva delle permutazioni potrebbe essere: “Le permutazioni semplici di n elementi distinti sono tutti i diversi gruppi che si possono formare con gli elementi dati, che rispettino le seguenti clausole:

  1. Ogni gruppo deve essere composto da n elementi.
  2. Un elemento non può essere duplicato in un gruppo.
  3. Due gruppi sono distinti quando differiscono solo per l’ordine con cui sono disposti gli elementi.

La matematica, in particolare il calcolo combinatorio ci insegna che tutte le possibili combinazioni di n elementi sono n!. Per fare un esempio, tutte le possibili permutazioni delle lettere che compongono la parola “mare“, sono 4!, ovvero 4*3*2*1 = 24.
In questo caso, e in tutti i casi in cui tutte le lettere che compongono una certa parola sono distinte, le permutazioni equivalgono agli anagrammi. Nel caso in cui vi siano lettere ripetute, il numero di anagrammi sarà minore. Ad esempio gli anagrammi della parola “olio” sono 4! / 2! = 12

L’implementazione non è banale, ma di algoritmi per la risoluzione del problema “permutazioni” ce ne sono parecchi anche perché l’argomento è quasi sempre presente nei programmi di studio ad indirizzo informatico, quindi è inutile scervellarsi, basta cercarli e cercare di capire la logica con la quale sono stati composti. Cosa più interessante, invece è cercare quello più efficiente.

Per la comparazione ho scelto tre degli algoritmi più noti e comuni, due di essi utilizzano la ricorsione mentre il terzo no. Normalmente il linguaggio con cui vengono presentati questi algoritmi è il C, oppure uno pseudocodice, quindi ho provveduto ad effettuare la traduzione in PHP.

Il primo algoritmo preso in considerazione è quello ricorsivo tradizionale, forse il più conosciuto, tratto da “Appunti di informatica libera” capitolo 595.4 (Algoritmi tradizionali) Ecco il codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Funzione ricorsiva
function Perm($lista, $k, $m) {
  if ($k == $m){
    $parola = "";
    for ($i=0; $i&lt;=$m; $i++){
      $parola .= $lista[$i];
    }
  echo ($parola."
");
  }
  else{
    for ($i=$k; $i&lt;=$m; $i++){
      $tmp = $lista[$k];
      $lista[$k] = $lista[$i];
      $lista[$i] = $tmp;
      Perm($lista, $k+1, $m);          
      $tmp = $lista[$k];
      $lista[$k] = $lista[$i];
      $lista[$i] = $tmp;
    }
  }   
}

Il secondo preso in esame, sempre ricorsivo, è pubblicato negli “user contribs” del manuale PHP. Si tratta di una versione modificata da alejandro dot garza at itesm dot mx di un algoritmo presentato su uno dei libri del noto editore O’Reilly. Sebbene non sia efficientissimo in termini di prestazioni, si tratta comunque di un esempio interessante per chi programma in PHP, in quanto utilizza alcune funzioni native del linguaggio. Ho commentato le istruzioni per la restituzione del risultato in un array, perché sebbene comodo, lo avrebbe penalizzato eccessivamente.
Ecco il codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function array_2D_permute($items, $perms = array( )) {
//static $permuted_array;
    if (empty($items)) {
        //$permuted_array[]=$perms;
        //print_r($new);
      print join('', $perms) . "
";
    }  else {
        for ($i = count($items) - 1; $i &gt;= 0; --$i) {
             $newitems = $items;
             $newperms = $perms;
             list($foo) = array_splice($newitems, $i, 1);
             array_unshift($newperms, $foo);
             array_2D_permute($newitems, $newperms);
         }
         //return $permuted_array;
    }
}

Infine un algoritmo non ricorsivo ideato da Phillip Paul Fuchs, il riferimento è: Scalable Permutations, in particolare ho tradotto in PHP l’esempio descritto in The Counting QuickPerm Algorithm. Phillip Paul Fuchs è un vero esperto di queste tematiche e ha scritto anche un libro sull’argomento. Non a caso, dunque, il suo algoritmo risulta il più efficiente!
Ecco il codice:

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
function Fuchs_perm($buf){
  $res = implode("",$buf);
  echo("$res
");
  $string_length = count($buf);
  for($i=0;$i&lt;$string_length;$i++){
    $p[$i]=0;
  }
  $i=1;
  while($i &lt; $string_length){
   if ($p[$i] &lt; $i) {
      $j = $i % 2 * $p[$i];
      $tmp=$buf[$j];
      $buf[$j]=$buf[$i];
      $buf[$i]=$tmp;
      $res = implode("",$buf);
      echo("$res
");
      $p[$i]++;
      $i=1;
   } else {
      $p[$i] = 0;
      $i++;
   }
  }
}

Per testare la velocità di esecuzione ho utilizzato questa interessante classe PHP realizzata da Ioannis Cherouvim: A time measuring and performance benchmarking class, eliminando tutte le istruzioni di stampa dell’output. I risultati sono visibili nella seguente tabella e nel grafico all’inizio dell’articolo.

Benchmarks di algoritmi per le permutazioni:
Algoritmo2 lett.3 lett.4 lett.5 lett.6 lett.7 lett.8 lett.
Algoritmo non ricorsivo Fuchs0,11010,11790,14210,27781,22007,237158,1750
Algoritmo ricorsivo tradizionale0,11690,15410,31301,19787,174158,0190440,7771
Algoritmo ricorsivo O’Reilly0,19700,28980,53612,301013,8372106,2979854,8127

Se volete fare qualche test con questi 3 algoritmi potete scaricare questo pacchetto
E’ richiesto PHP versione 5.3.x o superiore!

Conclusioni:

I risultati parlano da soli e rispecchiano le mie aspettative. L’algoritmo di Fuchs, è nettamente migliore degli altri, altamente ottimizzato e non ricorsivo. Quelli ricorsivi soffrono degli svantaggi tipici di questa tecnica: ogni chiamata ricorsiva consuma memoria ed utilizza un salto ad una funzione che produce un inevitabile rallentamento.

PHP: una classe per disegnare un orologio analogico

Orologio analogico Questo è un piccolo esercizio per imparare ad utilizzare le funzioni per disegnare con le lbrerieGD di PHP. La classe Analogclock utilizza tali funzioni per generare un’immagine PNG che rappresenta un orologio analogico. Come valori di input vengono passati al costruttore la larghezza dell’immagine, e al metodo display() i valori dell’ora, dei minuti e dei secondi che si desidera rappresentare. Volendo dare un effetto dinamico all’immagine, si può ricorrere ad uno script Javascript che ricarica la pagina, o meglio la sola immagine, ogni secondo, fornendo i parametri di ora, minuti e secondi aggiornati. E’ possibile impostare la classe in modo da escludere dalla visualizzazione la lancetta dei secondi, la visualizzazione digitale e infine, prelevare direttamente l’orario del server. Il confronto fra le due modalità di funzionamento è visibile in questo esempio.

La rappresentazione delle lancette dell’orologio non presenta grosse difficoltà perché le coordinate dei punti di inizio e di fine della linea non sono altro che l’origine del cerchio che rappresenta il quadrante, e, rispettivamente il coseno e il seno dell’angolo della lancetta. Poichè l’origine dell’immagine (il punto x = 0, y = 0) è fissato in alto a sinistra, per prima cosa bisogna traslare la circonferenza di riferimento di Π + Π /2. Il secondo aspetto da tenere presente è che la nostra immagine è un quadrato nel quale è inscritto il cerchio che rappresenta il quadrante dell’orologio. Dunque il raggio della circonferenza è la metà del lato del quadrato.

Vediamo ad esempio il codice per disegnare la lancetta dei secondi:

1
2
3
4
5
6
7
8
9
10
// ....
$delta = M_PI + M_PI / 2 ;  
// 2Π / 60 = Π / 30 !
$delta += (M_PI * 1 / 30) * $seconds ;
// circle centre coordinates: x = radius, y = radius.
$newX0 = $radius + cos($delta) * $radius;
$newY0 = $radius + sin($delta) * $radius ;
// $img is the resourse image and $colorSec is the allocated line color
imageLine($img, $radius, $radius, $newX0, $newY0, $colorSec);
// ....

L’intero codice sorgente della classe è disponibile qui
Il pacchetto completo del file di esempio è scaricabile qui

Vediamo come utilizzare la classe in pratica, questo il codice del file che genera l’immagine PNG con alcune opzioni attivate:

1
<!--?php require_once('analogclock.php'); $myClock = new Analogclock; //client mode: (remove comments): //$actualHour = intval($_GET['hour']); //$actualMin = intval($_GET['min']); //$actualSec = intval($_GET['sec']); //$myClock-&gt;showSeconds(false); //$myClock-&gt;display($actualHour, $actualMin, $actualSec); //server mode: $myClock-&gt;setAutotime(true); $myClock-&gt;display(); ?-->

E questo invece è il codice della pagina html nella quale viene visualizzato l’orologio con un po’ di javascript per ricaricare l’immagine ogni secondo:

<script type="text/javascript">
<!-- Begin function reFresh() { //location.reload(true); var today = new Date(); document.images['myClock'].src = 'test.php?hour=' + today.getHours() + '&amp;min=' + today.getMinutes() + '&amp;sec=' + today.getSeconds(); } // 1 minute = 60000 milliseconds. window.setInterval("reFresh()",1000); // End -->
</script>
<h1>Prova orologio</h1>
<script type="text/javascript">
    var today = new Date();
    document.write("<img src='test.php?hour=" + today.getHours() + "&amp;min=" + today.getMinutes() + "&amp;sec=" + today.getSeconds() + "' name='myClock' alt='Server Clock'/>");      
  </script>

Anche se in modalità server non sarebbe necessario passare al file test.php la querystring con i valori di ora, minuti e secondi (in quanto abbiamo attivato autotime) in realtà questo ci serve a forzare il browser a ricaricare l’immagine invece di prenderla dalla cache.

Conclusioni:

Come ho accennato all’inizio, questo vuole essere soprattutto un esercizio. Alcuni esempi di orologi analogici lato client molto più accattivanti del mio si trovano qui: CoolClock – The Javascript Analog Clock che però utilizzano canvas che non è ancora ben supportato da tutti i browser.
Avendo maggiore dimestichezza con il PHP piuttosto che con Javascript, ho preferito costruire un orologio lato server, che può benissimo funzionare con l’orario del client. Rimangono inoltre tutti gli altri vantaggi di uno script lato server tra i quali quello della piena compatibilità con tutti i browser!

Riferimenti ed approfondimenti:


Mario Spada ha scritto:

Ho leggermente modificato il codice per visualizzare in maniera fluida il movimento della lancetta delle ore. Adesso non si verifica più lo scatto fra un’ora e l’altra, il passaggio è progressivo. Anche il pacchetto per il download è stato aggiornato.
07.07.08 19:28
Peppe ha scritto:

Ciao! Perché nella barra del titolo mi fa il caricamento della pagina? Vorrei eliminarlo…
30.09.08 18:36
Mario Spada ha scritto:

Il caricamento che si nota sulla barra è inevitabile con javascript, perché è necessario ricaricare l’immagine (che è il risultato di uno script php) ogni secondo in modo da rendere efficacemente la lancetta dei secondi. L’unica cosa che mi viene in mente per ridurre l’effetto è quello di utilizzare la tecnologia AJAX.
07.10.08 19:39
Luca ha scritto:

Ciao sto utilizzando l’orologio ma noto che lo sfondo e’ bianco e quadrato c’e’ un modo per farlo diventare un cerchio o in alternativa avere lo sfondo trasparente?
Grazie bella classe ;-)