STL - count e count_if

Dal quinto capitolo di Designing Components with the C++ STL, di Ulrich Breymann. Algoritmi standard.

Sezione dedicata agli algoritmi che non modificano le sequenze su cui operano.

5.3.6: count e count_if

Algoritmi per il conteggio degli elementi uguali a uno specificato valore o che soddisfano un dato predicato.

Vediamo count() e count_if() in azione in questo esempio:

#include<iostream>
#include<algorithm>
#include <vector>

using namespace std;

class ComparModule {
public:

ComparModule(int i) : module(i) {
}

bool operator()(int x) {
return (x % module == 0);
}
private:
int module;
};

bool isOdd(int i) { return (i%2) != 0; }

int main() {
std::vector<int> v;

for (int i = 1; i < 101; ++i)
v.push_back(i);

cout << "Elements with value 99: " << count(v.begin(), v.end(), 99)
<< endl;

cout << "Elements module 40: " << count_if(v.begin(), v.end(), ComparModule(40))
<< endl;

cout << "Odd elements: " << count_if(v.begin(), v.end(), isOdd)
<< endl;
}

MEC++: Lo standard del linguaggio C++

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Sesto blocco: Miscellanea.

(35) Lo standard del linguaggio C++

Un sito da tenere d'occhio per tener traccia degli sviluppi del linguaggio é questo: http://www.open-std.org/jtc1/sc22/wg21/

MEC++: C e C++ nello stesso programma

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Sesto blocco: Miscellanea.

(34) Come combinare C e C++ nello stesso programma.

Occorre accertarsi che i file oggetto prodotti dai compilatori C e C++ coinvolti siano compatibili.
Le funzioni usate da entrambi i linguaggi vanno dichiarate extern "C".
Se possibile, é opportuno che il main sia in C++.
Attenzione all'allocazione di memoria, va mantenuto l'accoppiamento tra new e delete da una parte e tra malloc e free dall'altra.
Le strutture che possono essere scambiate tra i linguaggi sono quelle che, in C++ non contengo definizioni di funzioni virtuali (ma conviene fare comunque attenzione.

MEC++: classi astratte

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Sesto blocco: Miscellanea.

(33) Le classi che non sono foglie nell'albero gerarchico dovrebbero essere astratte.

Non sono é più naturale, ma ci risparmia anche una serie di grattacapi.

Le vocali in una stringa

Scrivere un metodo statico in Java che ritorni il numero di vocali della stringa che gli viene passata come parametro.

Assumiamo che per vocali si intendano solo i caratteri a, e, i, o, u.

A voler fare i precisini bisognerebbe considerare anche i caratteri accentati, per non parlare dei caratteri che rappresentano suoni vocalici specifici di alcune lingue (tedesco, svedese, per dirne un paio). Ma limitiamoci alla soluzione di base.

In una classe creiamo un paio di metodi statici. Il primo, isVowel(), accetta in ingresso un carattere e ritorna true solo se questo é una vocale, secondo la nostra definizione:

public static final boolean isVowel(char c) {
switch(c) {
case 'A':
case 'a':
case 'E':
case 'e':
case 'I':
case 'i':
case 'O':
case 'o':
case 'U':
case 'u':
return true;
default:
return false;
}
}

Il secondo metodo é quello richiesto, accetta in input una stringa e fa un loop su di essa al fine di contare tutte le vocali presenti:

public static int vowels(String input) {
int value = 0;
if(input == null)
return 0;

for(int i = 0; i < input.length(); ++i)
if(isVowel(input.charAt(i)))
++value;
return value;
}

Per testare la funzionalità ho usato questo main:

public static void main(String[] argv) {
int value = vowels("ambaradan");
System.out.println(value);

value = vowels(null);
System.out.println(value);

value = vowels("");
System.out.println(value);

value = vowels("aeiou");
System.out.println(value);

value = vowels("sfrt");
System.out.println(value);
}

Ordinare una matrice

Stiamo lavorando in C++. Ho un array bidimensionale di interi, tipo questo:

4 9 8 5 7
5 6 2 6 1
9 7 8 3 2
3 9 4 5 8
1 6 8 7 1

Voglio crearne una copia ordinata per colonne, in modo che l'output sia:

1 3 5 7 8
1 3 5 7 8
1 4 6 7 9
2 4 6 8 9
2 5 6 8 9

come fare?

La mia idea é quella di usare come appoggio un multiset. Approfittando del fatto che l'inserimento in questo container é automaticamente ordinato basta caricare i valori dall'array in input nel multiset e quindi leggerli nell'array di output.

Questo il codice risultante:

#include <iostream>
#include <set>

using namespace std;

namespace {
const int DIM = 5;

void mapper(int input[][DIM], int output[][DIM]) {
multiset<int> temp;

for (int i = 0; i < DIM; ++i) {
for (int j = 0; j < DIM; ++j) {
temp.insert(input[i][j]);
}
}

multiset<int>::const_iterator it = temp.begin();
for (int i = 0; i < DIM; ++i) {
for (int j = 0; j < DIM; ++j) {
// !!! [j][i] !!! ordered by columns
output[j][i] = *it++;
}
}
}
}

int main() {
int input[DIM][DIM] = {
4, 9, 8, 5, 7,
5, 6, 2, 6, 1,
9, 7, 8, 3, 2,
3, 9, 4, 5, 8,
1, 6, 8, 7, 1
};

for (int i = 0; i < DIM; ++i) {
for (int j = 0; j < DIM; ++j) {
cout << input[i][j] << ' ';
}
cout << endl;
}
cout << endl;

int output[DIM][DIM];
mapper(input, output);

for (int i = 0; i < DIM; ++i) {
for (int j = 0; j < DIM; ++j) {
cout << output[i][j] << ' ';
}
cout << endl;
}
cout << endl;
}

MEC++: programmare per il futuro

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Sesto blocco: Miscellanea.

(32) Programmare per il futuro

Data le difficoltà che si incontrano nella manutenzione del codice, conviene fare il possibile per pensare in anticipo al momento in cui ci toccherà mettere mano al codice che scriviamo oggi.

Il codice scritto bene é facile da modificare. Scrivere codice flessibile, robusto e affidabile non é semplice. Deve essere pensato e realizzato pensando a quello che può accadere in futuro.

MEC++: Funzioni virtuali rispetto a più oggetti

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Quinto blocco: Tecniche.

(31) Funzioni virtuali rispetto a più oggetti

É una idea interessante: permettere che una funzione abbia un comportamento polimorfo rispetto a due (o più) oggetti. In pratica penso (o forse spero) che difficilmente mi capiterà di progettare una applicazione che mi porti ad usare questa tecnica.

MEC++: Proxy

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Quinto blocco: Tecniche.

(30) Proxy

A dire il vero, non ho capito bene il senso di questa tecnica. Me la rileggerò un'altra volta.

MEC++: Reference counting

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Quinto blocco: Tecniche.

(29) Reference counting

Lo scopo del reference counting é permettere che differenti oggetti condividano la stessa rappresentazione interna di uno stesso valore. Ci sono due motivazioni fondamentali per usare questa tecnica: aiutare a gestire correttamente la costruzione e distruzione dell'oggetto sull'heap (in particolare, la distruzione va effettuata solo quando l'ultimo riferimento al valore viene a cadere), e l'esigenza di trattare come un unico oggetto diverse istanze che si riferiscano allo stesso valore, in modo da non allocare spazio inutile sullo heap.

MEC++: Puntatori vs riferimenti

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Primo blocco: Fondamentali.

(1) Puntatori e riferimenti

Puntatori e riferimenti (reference) appaiono diversi ma fanno cose molto molto simili. Le differenze fondamentali sono:

Un puntatore può assumere valore NULL ma non si può fare niente di simile con un riferimento.

Un riferimento deve riferirsi a un oggetto. Non é possibile creare un riferimento senza inizializzarlo.

Dato che un riferimento é per definizione associato ad un oggetto, non c'é la necessità di verificare che sia valido - per i puntatori é generalmente opportuno controllare che non sia NULL prima di utilizzarlo.

Un puntatore può puntare a differenti oggetti nel corso della sua vita, un riferimento é vincolato all'oggetto con cui viene inizializzato.

MEC++: Smart pointer

Appunti tratti dalla rilettura di More Effective C++ di Scott Meyers. Quinto blocco: Tecniche.

(28) Smart pointer

Gli smart pointer sono oggetti che sembrano puntatori ma offrono funzionalità aggiuntive.

Usando smart pointer invece di puntatori sciocchi/grezzi (dumb/raw) si ottiene il controllo su questi aspetti del loro comportamento:
  • costruzione e distruzione: normalmente uno smart pointer viene inizializzato a zero; é possibili implementare tecniche per ridurre il rischio di memory e resource leak;
  • copia e assegnamento: a seconda dell'implementazione, si possono ottenere diversi effetti;
  • dereferenziamento: l'accesso all'oggetto sotteso può essere regolamentato a piacimento.
auto_ptr

Il problema di come comportarsi in caso di costruzione per copia e assegnamento per uno smart pointer é risolto da auto_ptr trasferendo la proprietà del puntatore dal vecchio al nuovo smart pointer.

Data questa impostazione, é una pessima idea passare un auto_ptr per valore. Vanno passati invece per const reference.