Choisissez votre style :
colorisé,
impression
Correction 16
Révisions, exceptions, espaces de nommage, arguments de main
Les solutions suivantes ne sont évidemment pas uniques. Si vous en trouvez de meilleures (ou si vous trouvez des erreurs), faites-le savoir ! Merci.
Par ailleurs, les solutions proposées correspondent à l'état de vos connaissances au moment où la série est abordée. D'autres solutions pourront être envisagées une fois de nouveaux concepts acquis.
Exercice 1
L'ensemble des instructions tentant d'accéder à un un élément d'indice donné peut lancer une exception. Cela donne cette première solution possible :
#include <vector>
#include <iostream>
#include <stdexcept>
using namespace std;
int acces(const vector<int>& vect, size_t i);
int main()
{
vector <int> v({1, 2, 3, 4, 5, 6, 7, 8 });
try {
cout << acces(v, 2) << endl;
cout << acces(v, 200) << endl;
cout << acces(v, 0) << endl;
}
catch (const out_of_range& e) {
cout << "Vous dépassez les bornes!" << endl;
}
return 0;
}
int acces(const vector<int>& vect, size_t i)
{
return vect.at(i);
}
Si l'on veut que seul l'accès fautif provoque un message d'erreur, il faut que chaque accès essaie d'intercepter l'exception. Pour éviter la répétition de code, on introduit une nouvelle fonction :
#include <vector>
#include <iostream>
#include <stdexcept>
using namespace std;
int acces(const vector<int>& vect, size_t i);
void afficher(const vector<int>& vect, size_t i);
int main()
{
vector <int> v({1, 2, 3, 4, 5, 6, 7, 8 });
afficher(v, 2);
afficher(v, 200);
afficher(v, 0);
}
int acces(const vector<int>& vect, size_t i)
{
return vect.at(i);
}
void afficher(const vector<int>& vect, size_t i)
{
try {
cout << acces(vect, i) << endl;
}
catch (const out_of_range& e) {
cout << "Vous dépassez les bornes!" << endl;
}
}
Exercice 2
#include <iostream>
#include <cassert>
#include <sstream>
#include <stdexcept> //pour l'utilisation des exceptions standards
// retourne vrai si chaine commence par sous_chaine
bool starts_with(const std::string& chaine,
const std::string& sous_chaine);
// lit un entier à partir d'un chaine de caracteres
int read_integer(std::string arg);
// traite les arguments de main
void process(int argc, char* argv[]);
// Par defaut calcul d'une somme, sinon calcul d'une moyenne
void compute(int argc, char* argv[], bool sum = false);
// PROGRAMME PRINCIPAL
int main(int argc, char* argv[])
{
try {
process(argc, argv);
}
catch (std::exception const& e) {
std::cerr << "Erreur: " << e.what() << std::endl;
return 1;
}
return 0;
}
// Definition des fonctions
void process(int argc, char* argv[])
{
if (argc < 3) {
throw std::runtime_error("Il n'y a pas suffisamment d'arguments");
}
if (!starts_with(argv[1], "-"))
{
throw std::invalid_argument("option manquante (-s ou -m)");
}
const unsigned int arg_length(std::string(argv[1]).size());
if (arg_length < 2) {
throw std::invalid_argument("option mal ecrite");
} else if (arg_length > 2) {
throw std::invalid_argument("les options possibles sont -m ou -s et non pas " + std::string(argv[1]));
}
switch(argv[1][1]) {
case 'm':
compute(argc, argv);
break;
case 's':
compute(argc, argv, true);
break;
default:
throw std::invalid_argument("option inconnue: " + std::string(argv[1]));
break;
}
}
void compute(int argc, char* argv[], bool sum)
{
assert(argc > 2);
double total(0);
size_t size(0);
for (int i(2) ; i < argc ; ++i) {
// chaque argv[i] est une string
int current(read_integer(std::string(argv[i])));
total += current;
++size;
}
if (sum){
std::cout << "La somme de vos nombres est : " << total;
}
else {
std::cout << "La moyenne de vos nombres est : " << total/size;
}
std::cout << std::endl;
}
bool
starts_with(const std::string& chaine, const std::string& sous_chaine) {
return (chaine.find(sous_chaine) == 0);
}
int read_integer(std::string arg) {
int result(0);
try {
result = std::stoi(arg);
}
catch(const std::exception& e){
throw std::runtime_error("conversion en entier impossible pour " +
arg);
}
return result;
}
Exercice 3
#include <sstream>
#include <stdexcept>
#include <vector>
#include <array>
#include <iostream>
#include "rand_range.h"
#include <cmath>
using namespace std;
// un espace de nommage pour des types et des fonctions utiles
namespace impl {
typedef short bit_t;
typedef std::array<impl::bit_t, 20> signal_t;
typedef std::array<double, 20> weights_t;
bool bernoulli(double threshold)
{
// Retourne vrai si le générateur produit un nombre inférieur
// à threshold et faux sinon
return rand_range(0.0, 1.0) < threshold;
}
// inverse la valeur d'un bit
bit_t
inverse(bit_t t){
return (t ? 0 : 1);
}
// retourne vrai si chaine commence avec sous_chaine
bool
starts_with(const std::string& chaine, const std::string& sous_chaine) {
return (chaine.find(sous_chaine) == 0);
}
} // impl::
// un type énuméré pour indiquer quelle type d'exécution on veut:
// MakeDist: afficher les scores de tous les neurones
// FindBest : afficher les caractéristiques du meilleur neurone
// Pour les types énuméré
// voir : https://www.coursera.org/learn/initiation-programmation-cpp/lecture/aqXqY/puissance-4-introduction
// (les types énumérés sont présentés plus précisément depuis ~06:29 dans la vidéo)
enum ExecTarget {
MakeDist, FindBest, Undefined
};
// un type pour stocker tous les paramètres d'exécution:
// le type d'exécution souhaitée
// le nombre de signaux que doit traiter chaque neurone
// le nombre de neurones
struct Parameters {
ExecTarget target;
unsigned int signals;
unsigned int neurons;
};
struct Neuron {
// les poids synaptiques
impl::weights_t w_;
// vrai si le neurone "détecte"
// la présence d'un objet
bool activated_;
};
struct Signal {
// l'ensemble de 20 bits
impl::signal_t s_;
// indique si l'objet est présent
bool object_present_;
};
bool process(Neuron& neuron, const Signal& sig);
void display_neuron(ostream& o, const Neuron& neuron);
Neuron init_neuron();
Signal init_signal(bool object_present);
void display_signal(ostream& o, const Signal& signal);
Parameters init_parameters(int argc, char* argv[]);
int read_numeric_parameter(std::string arg);
void execute(const Parameters&);
void make_distribution_data(const unsigned int neurons,
const unsigned int signals,
bool find_best = false);
Neuron init_neuron();
// Programme principal
int main(int argc, char* argv[])
{
try {
Parameters params(init_parameters(argc, argv));
execute(params);
return 0;
} catch (exception const& e) {
cerr << "Error: " << e.what() << endl;
return 1;
}
}
Neuron init_neuron()
{
Neuron neuron;
neuron.activated_ = false;
for (auto& w_ : neuron.w_) {
w_ = rand_range(-15., 15.);
}
return neuron;
}
bool process(Neuron& neuron, const Signal& sig)
{
// voir: http://www.cplusplus.com/reference/std/numeric/inner_product/
double prod(std::inner_product(sig.s_.begin(), sig.s_.end(), neuron.w_.begin(), 0.0));
neuron.activated_ = (prod >= 0 ? true : false);
return neuron.activated_;
}
void display_neuron(ostream& o, const Neuron& neuron)
{
// affiche les poids synaptiques
for (size_t i(0); i < neuron.w_.size(); ++i)
o << neuron.w_[i] << " ";
}
Signal init_signal(bool object_present)
{
Signal s;
s.object_present_ = object_present;
for (int i(0) ; i < 5 ; i++) {
s.s_[i] = 1;
}
for (int i(5) ; i < 10 ; i++) {
s.s_[i] = 0;
}
for (int i(10) ; i < 15 ; i++) {
s.s_[i] = object_present ? 1 : 0;
}
for (int i(15) ; i < 20 ; i++) {
s.s_[i] = object_present ? 0 : 1;
}
// bruitage du signal
// chaque bit a une probabilité de 0.1 d'etre altéré
for (int i(0) ; i < 20 ; i++) {
if (impl::bernoulli(0.1))
{
if (s.s_[i] == 0)
s.s_[i] = 1;
else
s.s_[i] = 0;
}
}
return s;
}
void display_signal(ostream& o, const Signal& signal)
{
// affiche les poids synaptiques
for (auto s : signal.s_)
o << s << " ";
}
Parameters init_parameters(int argc, char* argv[])
{
Parameters parameters;
if (argc < 2) {
throw runtime_error("Too few arguments");
}
parameters.signals = 1000;
parameters.neurons = 10000;
parameters.target = FindBest;
for (int i(1) ; i < argc ; ++i) {
if (impl::starts_with(argv[i], "-")) {
const unsigned int arg_length(std::string(argv[1]).size());
if (arg_length < 2) {
throw invalid_argument("empty parameter");
} else if (arg_length > 2) {
throw invalid_argument("multicharacter option: " + string(argv[i]));
}
switch(argv[i][1]) {
case 'd':
parameters.target = MakeDist;
break;
case 'b':
parameters.target = FindBest;
break;
case 's':
if (++i < argc) {
parameters.signals = read_numeric_parameter(std::string(argv[i]));
}
break;
case 'n':;
if (++i < argc) {
parameters.neurons = read_numeric_parameter(std::string(argv[i]));
}
break;
default:
throw invalid_argument("unknown option: " + string(argv[i]));
break;
}
} else {
throw invalid_argument("misplaced parameter: " + string(argv[i]));
}
}
if (parameters.target == Undefined) {
throw logic_error("missing target option -b or -d");
}
return parameters;
}
int read_numeric_parameter(std::string arg) {
int result(0);
try {
result = std::stoi(arg);
}
catch(const std::exception& e){
throw std::runtime_error("conversion en entier impossible pour " +
arg);
}
return result;
}
void execute(const Parameters& parameters)
{
if (parameters.target == MakeDist) {
make_distribution_data(parameters.neurons, parameters.signals);
} else {
make_distribution_data(parameters.neurons, parameters.signals, true);
}
}
void
make_distribution_data(const unsigned int nb_neurons,
const unsigned int nb_signals,
bool find_best)
{
// crée aléatoirement nb_neuron neurones
cerr << "Creation de " << nb_neurons << " neurones" << endl;
vector<Neuron> neurons;
// reserve permet de reserver le nombre de "cases" souhaitées
// dans le vecteur : a utiliser si le vecteur peut contenir potentiellement
// beaucoup de valeurs (pour eviter d'evenuelles reallocation de memoire en
// interne). On aurait aussi pu ici utiliser un array C++11
neurons.reserve(nb_neurons);
for (size_t i(0) ; i < nb_neurons ; ++i) {
neurons.push_back(init_neuron());
}
// crée nb_signals signaux aléatoires
cerr << "Creation de " << nb_signals << " signaux" << endl;
vector<Signal> signals;
signals.reserve(nb_signals);
for (size_t i(0) ; i < nb_signals ; ++i) {
signals.push_back(init_signal(impl::bernoulli(0.5)));
}
unsigned int highscore(0);
Neuron best;
cerr << "Execution ..." << endl;
for (auto neuron : neurons){
unsigned int score(0);
for (auto signal : signals) {
if (process(neuron, signal) == signal.object_present_) {
score++;
}
}
// mise à jour du meilleur score
// et mémorisation du neurone correspondant
if (find_best) {
if (score > highscore) {
highscore = score;
best = neuron;
}
}
if (!find_best) {
// affiche le score d'un neurone sur la sortie standard
cout << score << endl;
}
}
if (find_best) {
// affiche le meilleur neurone et son score
cout << "Best score: " << highscore << "/" << nb_signals << " (" << 1.*highscore/nb_signals << ")" << endl;
cout << "Best neuron: ";
display_neuron(cout, best);
cout << endl;
}
}
Dernière mise à jour : 2025/02/27 12:19 $