Partage

[Exercices] Venez vous entraîner !

Ce mois: Parseur de fonctions mathématiques

20 août 2008 à 16:09:17

ah ! ok ! Je sais pas ... :) A toi de voir si le langage permet d'autres erreurs ...

<taille=tpetit> mon petit doigt me dit que la solution va bientôt sortir...</taille>
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
20 août 2008 à 16:14:33

Erreur:
---- Au niveau des paramètres du main.
---- De syntaxe dans le code:
-------- Pas de code (Pseudo-erreur).
-------- '[' ou ']' manquant(s).
-------- Un ']' avant son '['.
---- Au niveau des entrées/sorties.

Voilà j'en vois pas d'autres.

Edit suite au post de Nanoc: Fallait pas que je le dise ?
20 août 2008 à 16:50:30

Chlab_lak -> Je parlais vraiment du code BF en tant que telle. Donc, les erreurs se résument bien à uniquement au '[' et ']'.

Merci ;)

<édit> mon petit doigt me dit que la solution va bientôt sortir...
Et qu'est-ce qui nous dit que ton petit doigt est bien renseigner? :-°
20 août 2008 à 17:18:49

encore une question: dans ton exemple vu que dans (2*3)*(10+1)*10 les parenthèses de 2*3 ne sont pas nécessaires :
Peut-on mettre autant de parenthèses que l'on veut dans la solution? même si non nécessaires? :euh:

à oui et aussi : si la réponse demandée est 100 et que l'on a un 100 dans nos 6 nombres peut on considérer ce 100 lui-même comme une reponse où faut il faire une opération genre 100/1 pour que la réponse soit validable?
20 août 2008 à 19:10:50

Tu peux mettre toutes les parenthèses inutiles que tu veux.

Pour ce qui est du deuxième point, on dira que oui, c'est une réponse. Il faudra cependendant considérer ce tirage comme non-valide pour le niveau 2.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
20 août 2008 à 20:00:26

Solution du deuxième exercice du mois de juillet 2008



Bonjour tout le monde ! Il est temps que je dévoile une solution pour le2ème exercice du mois de juillet . Vous avez été 25 (!) à m'envoyer une solution. Ce qui montre que l'exercice a eu beaucoup de succès.
La plus part des solutions étaient correctes. Certaines avaient un problème avec les parenthèses imbriquées, le point difficile de l'exercice. D'autres étaient tellement complètes qu'elle ne pouvaient pas entrer dans ce post ! (Surtout qu'il existe un compilateur BF qui "pèse" moins de 200 octets, vous êtes loin du compte.)

Lecture du fichier



La première étape du programme consistait à lire le fichier BrainFuck et à stocker son contenu dans une chaîne de caractère.

Pourquoi ne pas laisser tout dans le fichier ?


La raison est simple. Avec les [], le programme peut très bien sauter "en arrière", ce qui n'est pas pratique si l'on navique dans un fichier.

Rien de très compliqué, on copie simplement ligne par ligne dans une std::string.

std::ifstream fichier("source.b") //On ouvre le fichier

std::string ligne;
std::string codeSource;

while(getline(fichier,ligne))  //On recupere la ligne
{
   codeSource+=ligne;    //Et on l'ajoute au code source
}


On ne lit pas tant qu'on a pas atteint EOF. La méthode avec le getline est la seule méthode correcte.


Mise en place de l'interpréteur



A partir de là, il fallait mettre en place les élément importants de l'interpéteur, sa "mémoire vive" et son pointeur. Plusieurs choix sont possibles, personellement, j'ai choisi d'utiliser un std::vector<char> avec comme "pointeur" l'indice de la case. On pouvait tout aussi bien utiliser un tableau standard puisque la taille ne varie pas ou encore une string.
Idem pour le "pointeur", on pouvait aussi utiliser un vrai pointeur ou un itérateur de la STL.

Dans notre cas, nous avons:

std::vector<char> memoire(30000,0);

unsigned int pointeur(0);


Interprétaion du code



Les choses intéressantes commencent ici. Pour interpreter le BF, il fallait parcourir la chaine de caractère d'un bout à l'autre en effectuant l'action associée. Pour ce faire, un switch est sans doute le plus approprié

for(unsigned int i(0); i< codeSource.size();++i)  //On parcourt la chaine
{
    switch(codeSource[i]])      //switch selon le symbole
    {
        case '>':
        {
            //On avance le pointeur
            ++pointeur;
            if (pointeur == 30000)   //Si on est au bout
                pointeur = 0;  //On revient au debut
            break;
        }

        case '<':
        {
            //Si on est au debut on va a la fin
            if (pointeur == 0)   
                pointeur = 30000;

            //Et on recule le pointeur
            --pointeur;
            break;
        }
        case '+':
        {
            //On incremente l'octet pointe
            ++memoire[pointeur];
            break;
        }
        case '-':
        {
            //On decremente l'octet pointe
            --memoire[pointeur];
            break;
        }

        case '.':
        {
            std::cout << memoire[pointeur];  //On affiche le caractere 
            break;
        }
        case ',':
        {
            memoire[pointeur] = std::getchar(); //On recupere la saisie de l'utilisateur
            break;
        }

        //Pour les parentheses, voir plus bas
       
        default: //Si c'est un autre caractere
            break;
    }
}


Les parenthèses



C'est sans doute le point le plus difficile de l'exercice. La première chose à faire était certainement de vérifier que l'expression est bien parenthésée. Compter le nombre de parenthèses ouvrantes et fermantes n'était pas suffisant. En effet, on peut écrire des expressions qui ont le bon nombre de paranthèses mais qui ne sont pas correctes. Par exemple:
<math>\() 3+4 (\)</math>

Le problème ensuite était qu'une parenthèse ne renvoit pas forcément à la parenthèse suivante du code source, des parenthèses peuvent êtres imbriquées, comme dans l'expression mathématique
<math>\((3*(1+4)-2)+1\)</math>

Il fallait donc trouver un moyen de repérer à quelle parenthèse sauter. Il y a deux grandes techniques pour faire cela.
La première consiste à lire une fois tout le code source et de repérer les couples de parenthèses qui vont ensemble puis stocker les positions des couples dans un tableau. Cette solution n'est viable que si le code source n'est pas trop long.
La deuxième est plus complexe mais fonctionnera toujours. Il s'agit d'utiliser une pile. Chaque fois qu'on rencontre une parenthèse ouvrante, on ajoute sa position au sommet de la pile. Lorsqu'on rencontre une parenthèse fermante, on saute à la position se trouvant sur le sommet de la pile et on supprime le sommet de la pile.
Ce qui est bien c'est que la STL propose une structure de type pile dans l'entête "stack".

Un code complet



Pour le code source complet, je vous propose ce mois le code de MatteX qui a réalisé un programme très complet et utilisant beaucoup la STL. Il a également gérer pas mal d'excpetions et il indique le temps d'exécution du programme.

Ce n'est pas forcément très facile quand on débute, mais il y a beaucoup de commentaires et n'hésitez pas à poser des questions si nécessaire.


BrainFuck.h
// BrainFuck.h
// par : MatteX
// 16 juillet 2008

/** Historique
 * 22 juillet 2008 :
 *   - AJOUT : Une constante globale TAILLE contenant la taille du tableau d'exécution.
 *	 - AJOUT : Une méthode valider_code() permettant de vérifier que le code fournis est un code BrainFuck valide.
 *   - MODIFICATION : Déplacement de tableau respecte la norme de warping au début 
 *       et à la fin du tableau d'exécution. Pour éviter les dépassement de tableau.
 *   - MODIFICATION : La méthode Préparer se nomme maintenant Initialisation
 *   - MODIFICATION : Les méthodes Interpréter et Initialisation prennent 
 *       un paramètre supplémentaire : const std::string & code
 *   - MODIFICATION : Intitialisation() ne lance plus d'exception si le code n'est pas valide. 
 *       Les caractère non valides seront simplement ignorés à l'exécution
 *   - RETRAIT : La méthode AddCode a été remplacée par un paramètre de la méthode Interpréter
 *   - RETRAIT : La variable membre m_code n'existe plus.
 *   - RETRAIT : L'opérateur >> n'est plus viable.
 */

#ifndef __EXERCICEMIJUILLET2008_MATTEX_BRAINFUCK_H__
#define __EXERCICEMIJUILLET2008_MATTEX_BRAINFUCK_H__

#include <istream>
#include <ostream>
#include <map>
#include <string>


// Interprète un code BrainFuck
class BrainFuck
{
    typedef std::string::const_iterator iterateur;
    typedef unsigned char octet_t;

    // Contient la position de début et de fin d'une paire d'opérateur de boucle.
    // Sera initialisé au début de la méthode Interpréter()
    std::map<iterateur,iterateur> m_blocs;

    // Valide le code et les boucles.
    void Initialisation( const std::string & code );

    // Interprete les boucles avec leur condition d'arrêt
    void Boucle( iterateur & it, const octet_t * const p ) const;

public:
   
    // Execute le code fournis et gère les entrée/sortie lorsque nécessaire
    void Interpreter( std::ostream & ostr, const std::string & code );
};

#endif // #ifndef __EXERCICEMIJUILLET2008_MATTEX_BRAINFUCK_H__


BrainFuck.cpp
// BrainFuck.cpp
// par : MatteX
// 16 juillet 2008

#include <algorithm>
#include <iostream>
#include <limits>
#include <stack>
#include <string>
#include <stdexcept>

#undef max

#include "BrainFuck.h"

// ----------------------------------------------------------------------------------------------

// Liste des caractères de début et fin de boucles.
const std::string BOUCLES = "[]";
// Taille du tableau d'exécution
const int TAILLE = 30000;

// ----------------------------------------------------------------------------------------------

void BrainFuck::Interpreter( std::ostream & ostr, const std::string & code )
{
    // Préparation de l'interpréteur
    Initialisation( code );

    // Création du buffer de 30 000 octets requis par la norme BrainFuck
    // Le Tableau d'exécution
    octet_t buffer[ TAILLE ] = { 0 };
    octet_t * p = buffer;
    
    // Interprétation des opérateurs
for ( iterateur it_code = code.begin(); it_code != code.end(); ++it_code )
    {
        switch (*it_code)
        {
        case '+':
            ++(*p);
            break;

        case '-':
            --(*p);
            break;

        case '>':
            if ( ++p >= buffer + TAILLE )
                p = buffer;
            break;

        case'<':
            if ( --p <= buffer - 1 )
                p = buffer + (TAILLE - 1);
            break;

        case'.':
            ostr << static_cast<std::ostream::char_type>( *p );
            break;

        case',' :
            *p = static_cast<octet_t>( std::cin.get() );
            std::cin.clear();
            std::cin.ignore( std::numeric_limits<std::streamsize>::max(), std::cin.widen( '\n' ) );
            break;

        case '[':
        case ']':
            Boucle( it_code, p );
            break;

        default:   // Si le caractère n'est pas un opérateur BrainFuck, il est tout simplement ignoré.
            break;
        }
    }
}

// ----------------------------------------------------------------------------------------------

// Valide le code et prépare l'interpréteur.
void BrainFuck::Initialisation( const std::string & code )
{
    std::stack<iterateur> pile;
    iterateur it = std::find_first_of( code.begin(), code.end(), BOUCLES.begin(), BOUCLES.end() );

    m_blocs.clear();

    while( it != code.end() )
    {
        if(pile.empty() && *it != *BOUCLES.begin())
        {
            throw std::runtime_error( "Il manque un ou plusieurs debuts de boucles." );
        }
        else if( (pile.empty()&& *it == *BOUCLES.begin()) || *pile.top() == *it )
        {
            pile.push( it );
        }
        else
        {
            m_blocs[ pile.top() ] = it;
            pile.pop();
        }

        it = std::find_first_of( it + 1, code.end(), BOUCLES.begin(), BOUCLES.end() );
    }

    if( !pile.empty() )
        throw std::runtime_error( "Il manque une ou plusieurs fins de boucles." );

}

// ----------------------------------------------------------------------------------------------

// Effectue les opérations de début et fin de boucles.
void BrainFuck::Boucle( iterateur & it, const octet_t * const p ) const
{
    typedef std::map<iterateur, iterateur>::const_iterator map_iterator;
    
    map_iterator bit;

    bit = m_blocs.find( it );

    // Si l'opérateur en cours est un début de boucle, nous devons tester la condition.
    if( bit != m_blocs.end() )
    {
        // Teste la condition de fin de boucle;
        if( ((*bit->first) == '[' && *p == 0) || (((*bit->first) == ']' && *p != 0)) )
            it = bit->second;
    }
    else
    {
        // L'opérateur est une fin de boucle ! Nous devons trouver le début de boucle associé et y retourner.
        for( map_iterator finder = m_blocs.begin(); finder != m_blocs.end(); ++finder )
        {
            if( finder->second == it )
            {
                it = finder->first - 1;
                break;
            }
        }
    }
}


main.cpp
/** main.cpp
 *
 * PROJET : ExerciceMiJuillet2008
 *	    Dans le cadre du sujet Exerices en C++ sur le Site du Zéro
 *	    -> http://www.siteduzero.com
 *      -> http://www.siteduzero.com/forum-83-257993-2746925-exercices-venez-vous-entrainer.html#r2746925
 *
 *	Merci à Nanoc pour l'idée originale de l'Exercice
 *
 * Par MatteX
 * Fait le 16 juillet 2008 (Une grosse heure)
 */

#include <ctime>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <stdexcept>

#include "BrainFuck.h"

// ----------------------------------------------------------------------------------------------

struct timespan
{
    time_t m_t;

    timespan( time_t t ) : m_t( t ) {}

    friend std::ostream & operator<< ( std::ostream & ostr, const timespan & ts )
    {
        std::tm * temps = std::localtime( &ts.m_t );

        if( ts.m_t >= 3600 )
            ostr << temps->tm_hour <<  "h ";

        if( ts.m_t >= 60 )
            ostr << temps->tm_min <<  "m ";

        if( ts.m_t >= 1 )
            ostr << temps->tm_sec <<  "s.";

        if( ts.m_t == 0 )
            ostr << "Moins d'une seconde.";

        return ostr;
    }
};

// ----------------------------------------------------------------------------------------------

void LireFichier( const std::string & chemin, std::string & cpntenu );

// ----------------------------------------------------------------------------------------------

int main( int argc, char* argv[] )
{
    BrainFuck bf;
    std::string code;

    std::time_t temps = 0;

    std::cout << "=========== Interpréteur BrainFuck ===========\n   Par : MatteX     version : 1.0 16.07.2008\n\n------------- Sortie du programme ------------\n"; 

    try
    {
        if( argc <= 1 )
            throw std::runtime_error( "Vous devez passer un fichier de code source BrainFuck en argument." );

        LireFichier( argv[ 1 ], code );

        temps = std::time( 0 );
        bf.Interpreter( std::cout, code );
    }
    catch( const std::exception & ex )
    {
        std::cerr << "ERREUR : " << ex.what() << std::endl;
    }

    if( temps != 0 )
        temps = std::time( 0 ) - temps;
   
    std::cout << "----------- Interprétation terminée ----------\nTemps d'exécution : " << timespan( temps ) << std::endl;

    return 0;
}

// ----------------------------------------------------------------------------------------------

void LireFichier( const std::string & chemin, std::string & contenu )
{
    std::ostringstream oss;
    std::ifstream ifstr( chemin.c_str() );

    if( !ifstr )
        throw std::runtime_error( ("Impossible d'ouvrir : " + chemin).c_str()  );

    if( !(oss << ifstr.rdbuf()) )
        throw std::runtime_error( "Impossible de lire l'intégralité du fichier." );

    contenu = oss.str();
}


Une solution plus simple



Voici une solution plus simple, dans le sens où elle fait moins appel à la STL. C'est le code de Masthiks.


#include <iostream>
#include <fstream>
#include <stdexcept>

#include "brainfuck.h"

using namespace std;

int main(int argv, char ** argc)
{
	try
	{
		if(argv < 2) throw runtime_error("Fichier d'entrée non precise");
		
		ifstream is;
		is.open(argc[1], ifstream::in);
		
		if(is.fail()) throw runtime_error("Impossible de trouver le fichier precise");
		
		nsBrainfuck::Brainfuck Resolv(is);
		is.close();
		
		Resolv.Exec();		
	}
	catch(exception & e)
	{
		cout << e.what() << endl;
	}
	
    	return 0;
}


Fichier brainfuck.h

#ifndef __BRAINFUCK_H__
#define __BRAINFUCK_H__

#include <iostream>
#include <stack>

namespace nsBrainfuck
{
    /**
	* La classe Brinfuck permet d'analyser un programme en brainfuck, afin de l'interpréter
	*/
    class Brainfuck
    {
      public:
        typedef unsigned char Octet;
	typedef std::string::iterator Iterator_code; 
	typedef Octet * Iterator_memory;

        Brainfuck(std::istream &, unsigned Taille = 30000);
	~Brainfuck();
        void Exec();

      protected:
        void FonctionIncrementer();
        void FonctionDecrementer();
        void FonctionDebut();
        void FonctionFin();
        void FonctionDeplacerGauche();
        void FonctionDeplacerDroite();
        void FonctionEntree();
        void FonctionSortie();

	Iterator_code             m_Car;
        std::string               m_Code;
	std::stack<Iterator_code> m_QWhile;

        Octet *                   m_Memoire;
        Iterator_memory           m_Current;
	unsigned                  m_Zero;
	unsigned                  m_Taille;
    };
}

#endif /* __BRAINFUCK_H__ */


Fichier brainfuck.cxx

#include <sstream>
#include <iostream>
#include <stdexcept>

#include "brainfuck.h"

#define BRAINFUCK nsBrainfuck::Brainfuck

using namespace std;

/**
  * Constructeur.
  * Permet d'initialiser le fichier brainfuck à analyser et la mémoire brainfuck
  * @param Flux représente le fichier à analyser
  */	
BRAINFUCK::Brainfuck(istream & Flux, unsigned Taille /* = 30000 */)
    : m_Zero(0), m_Taille(Taille)
{
	// On récupère le contenu du fichier afin de l'insérer dans un string (il est plus simple
	// de procéder de cette manière, plutôt que d'effectuer une interprétation
	// directe)
	ostringstream Buffer;
	Buffer << Flux.rdbuf();
	m_Code = Buffer.str();
	
	// On créé un tableau de taille m_Taille
	m_Current = m_Memoire = new Octet[m_Taille];	
}

/**
  * Constructeur.
  * Detruit la mémoire brainfuck
  */	
BRAINFUCK::~Brainfuck()
{
	delete [] m_Memoire;
}

/**
  * Analyse le code brainfuck, et affiche le résultat à l'écran
  */
void BRAINFUCK::Exec()
{
    	for(unsigned i(0); i < m_Taille; ++i) m_Memoire[i] = 0;
	for(m_Car = m_Code.begin(); m_Car != m_Code.end(); ++m_Car)
    	{
        	// Cette condition permet de gérer le cas où on aurait rencontré un '[' avec la valeur pointée valant 0:
		// elle passe toutes les instructions contenues dans la boucle, ainsi que les autres boucles qu'elle pourrait contenir.
		if(m_Zero) 
		{
			if(*m_Car == '[') ++m_Zero;
			else if(*m_Car == ']') --m_Zero;
		}
		else
		{	
			// Ii on traite les différentes instructions possibles.
			switch(*m_Car)
			{
		        	case '+': FonctionIncrementer();    break;
		        	case '-': FonctionDecrementer();    break;
		        	case '.': FonctionSortie();         break;
		        	case '>': FonctionDeplacerDroite(); break;
		        	case '<': FonctionDeplacerGauche(); break;
		        	case ',': FonctionEntree();         break;
		        	case '[': FonctionDebut();          break;
		        	case ']': FonctionFin();            break;
		    	}
		}

    	}
	if(m_QWhile.size()) throw runtime_error("Erreur lors de l'execution du programme:\ncaractere ']' manquant");
	cout << flush;
}

/**
  * Incrémente de 1 la valeur pointée
  */
void BRAINFUCK::FonctionIncrementer()
{
    ++(*m_Current);
}

/**
  * Décrémente de 1 la valeur pointée
  */
void BRAINFUCK::FonctionDecrementer()
{
    --(*m_Current);
}

/**
  * Permet de gérer la début d'une boucle, lorsqu'on rencontre le caractère '['
  */
void BRAINFUCK::FonctionDebut()
{
	// On regarde si la valeur pointée vaut 0, si c'est le cas il faut sauter cette boucle: la valeur
	// de m_Zero est incrémentée, ce qui permettra de sauter toutes les instructions que contient la boucle.
    	if(*m_Current == 0) ++m_Zero;
	// Si elle ne vaut pas 0, alors on ajoute la position du début de la boucle à la pile, ce qui permettra de pouvoir y revenir
	// plus tard si nécessaire.
	else m_QWhile.push(m_Car);
}

/**
  * Permet de gérer la fin de la boucle, lorsqu'on rencontre le caractère ']'
  */
void BRAINFUCK::FonctionFin()
 {  
	if(m_QWhile.empty()) throw runtime_error("Erreur lors de l'execution du programme:\ncaractere '[' manquant");
	// On regarde si la valeur courrante vaut 0, si c'est le cas, on enlève la position du début de la boucle afin 
	// de ne pas pouvoir y revenir.
	if(*m_Current == 0) m_QWhile.pop();
	// SI elle ne vaut pas 0, alors on revient au début de la boucle grace à la valeur contenue dans la pile.
	// On prend la dernière position insérée afin de gérer les cas où il y aurait plusieurs boucles dans une seule.
	else m_Car = m_QWhile.top();
}

/**
  * Demande un caractère à l'utilisateur
  */
void BRAINFUCK::FonctionEntree()
{
    	cout << flush;
	*m_Current = cin.get();
}

/**
  * Affiche le caractère courrant à l'écran
  */
void BRAINFUCK::FonctionSortie()
{
    cout << *m_Current;
}

/**
  * Déplace le pointeur mémoire vers la gauche
  */
void BRAINFUCK::FonctionDeplacerGauche()
{
	if(m_Current != m_Memoire) --m_Current;
	else m_Current = m_Memoire + m_Taille - 1;
}

/**
  * Déplace le pointeur mémoire vers la droite
  */
void BRAINFUCK::FonctionDeplacerDroite()
{
	if(m_Current != m_Memoire + m_Taille - 1) ++m_Current;
	else m_Current = m_Memoire;
}

#undef BRAINFUCK


Le code inconnu



Pour ceux qui n'y serait pas parvenu, le code mystère affichait la suite de Fibonacci.

Remarques



Quelques remarques sur les codes reçus :

  • Pas de lecture sur EOF. Utiliser la méthode présentée en haut (ou celle de MatteX)
  • Pas de #define pour les constantes. Utilisez le mot-clé const.
  • Pas de macros, ce n'est pas débugguable, pas profilable et on ne sait jamais ce qui peut se passer à la compilation
  • Il est inutile de rattraper une exception si c'est juste pour la relancer. Laissez la simplement passer


Bonne continuation à tous !

Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
20 août 2008 à 21:26:06

Je trouve le code de Mattex extremement long, 300 lignes c'est beaucoup !! Sinon je préfère utiliser un switch comme tu le montre Nanoc qu'un gros if/else if qui n'ameliore pas la lisibilité. Et le code de Mattex tel quel compile pas chez moi, les lignes du genre " trow std::exeption("chaine"); " le compilo aime pas.
FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
20 août 2008 à 23:01:24

Citation : nanoc

D'autres étaient tellement complètes qu'elle ne pouvaient pas entrer dans ce post ! (Surtout qu'il existe un compilateur BF qui "pèse" moins de 200 octets, vous êtes loin du compte.)

fallait mentionner une taille a ne pas depasser + un temps d'execution max ;)
21 août 2008 à 1:12:36

C'est vrai que le code est long. Mais il est mieux structurer que le mien (je n'ai absolument pas mis d'effort comme la date de remise était dépassé :p )
Sinon, j'ai utilisé exactement le code que Nanoc "suggère" dans son "pas-a-pas", à la différence que j'ai utilisé un tableau de char* et un pointeur.

C'était un exercice très intéressant! (Encore un!).

Sinon, je viens de finir celui des statistiques. Très simple, en effet.

Par contre, pour moi (et je suis, à temps perdu, écrivain), un paragraphe = un caractère quelconque, suivit d'un ou plusieurs \n suivit d'un autre caractère.

Par exemple : "Allo\n\n \n\nGa!" est pour moi, trois paragraphe.
"Allo!\nÇa va?" équivaut à deux paragraphes.
De plus, dans les outils que j'utilise normalement, le nombre de caractère total ne compte pas les retour à la ligne.
Mais bref, ce sont des détails.

Je me lance dans le petit nouveau!
21 août 2008 à 10:49:44

Salut,
Merci je vais voir la réponse pour voir si je comprend.
Ce ne serait pas plutôt Suite de Fibonacci ?? et non "la suite de Ficonacci."
:)
21 août 2008 à 10:56:11

@Gollum: Corrigé. Merci.

@Bachir: C'est de l'humour...

@Freedom: Pour le switch, je suis entièrement d'accord. Pour le reste, oui il fait 300 lignes, mais elles sont toutes utiles. Le code est documenté, commenté, correct et utilise bien la STL, ce que je voulais mettre en avant ici.
Pour ce qui est des exceptions, j'ai corrigé de sorte que ça compile partout.
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
21 août 2008 à 13:18:41

C'est pas tellement un problème mais j'ai remarqué une "anomalie" dans le code de MatteX.

Le code suivant (qui ne doit pas fonctionner) passe très bien avec le programme de MatteX :
++++[>+++++++++<-]
].[


Si on observe le fonctionnement en détail, on voit que le programme se comporte comme si les deuxième ']' et '[' était inversé (il fait la boucle).



Mon problème est que je n'arrive pas à modifier correctement la fonction de vérification pour que le programme renvoie une erreur dans ce cas-là.



EDIT : Autant pour moi, je n'avais pas compris que lorsque on met "][" il doit se passe quelque chose (la condition d'entrée dans la boucle est inversé).
21 août 2008 à 16:45:15

Citation : Ga

EDIT : Autant pour moi, je n'avais pas compris que lorsque on met "][" il doit se passe quelque chose (la condition d'entrée dans la boucle est inversé).


Moi j'ai gérer cela comme une erreur...


<édit>
Pour le dernier exo :

Citation : Règle du jeu (Wikipédia)

les nombres de 1 à 10 présents en double exemplaires et les nombres 25, 50, 75 et 100 présents en un seul exemplaire.


Faut-il respecter cela?
21 août 2008 à 18:44:41

Citation : Darkelfe


++++[>+++++++++<-]
].[



EDIT : Autant pour moi, je n'avais pas compris que lorsque on met "][" il doit se passe quelque chose (la condition d'entrée dans la boucle est inversé).



D'après les régles données par Nanoc (et par Wikipedia) ce code n'est pas viable. Quand l'interpréteur arrive sur le 2° ] il doit pouvoir retourner au [ correspondant, or ici il n'y en as pas, idem pour le 2° [. D'ailleur l'interpréteur de Mattex ne marche pas sur ce code (le programme plante).
FaQ : Fr | En 1 2 | C++11 | Template || Blog : Deloget | C++|Boost--Dev | C++Next | GotW || Installer Boost
22 août 2008 à 12:00:02

Bonjour, pour l'exercice sur les chiffre sur les chiffre, j'arrive à quelque chose pour le premier niveau avec une brute force, mais j'ai l'impression qu'elle se répète beaucoup : je m'explique : elle me trouve toujours la solutions, quand il y en a une, en moins de 4 seconde, mais si il y en a pas, il faut au moins 40s pour qu'elle fasse toute les possibilités...
Sinon, j'ai une question : mon programme gère les parenthèse, mais que su 1 "niveau"... Je m'explique :
Il peut facilement faire
(a+b*c)-d/(e+f)

mais pas
((a+b)*(d-c)+e)*f


Et sinon, mon programme ne fonctionne que si tous les chiffres sont utilisés, et ne gère pas la priorité des opérateurs (à part avec les parenthèse...). Est ce grave par rapport à l'énoncé?
(Il faut savoir qu'il donne une solution valable dans 70% des cas...)

Merci d'avance! :)
22 août 2008 à 12:11:01

@ Freedom: En effet. Je n'ai pas été assez sélectif dans mes tests. Je vais corriger le code de MatteX en conséquence quand je rentre chez moi.

@Mikechaos: Non, ca date de l'époque où l'informatique n'existait pas. Et puis ça ne fait que peu de différence.

@Zebra: On doit pouvoir faire plus court en brute-force. Pour ce qui est des parenthèses, c'est dommage parce que certains calculs nécessitent une profondeur de 2 (3 ?) parenthèses. C'est peut-être ça qui cause le 70%. Pour ce qui est de la priorité des opérateurs, oui c'est grave, si par exemple ton premier calcul ne donne pas le bon résultat (a+b*c).
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
22 août 2008 à 13:12:51

Me disait bien pourquoi je l'avais pas réussi.....Plein de notions que j'ai jamais vu -___- et qui ne sont pas présente sur le site.....
22 août 2008 à 17:29:47

Citation : Dr.Tenma

Me disait bien pourquoi je l'avais pas réussi.....Plein de notions que j'ai jamais vu -___- et qui ne sont pas présente sur le site.....



Il y avait moyen de le faire sans ces notions. La seule chose que j'ai utilisé que tu n'as peut-être jamais vu, c'est les stack. Mais il y avait moyen de s'arranger avec les vector, qui sont présents sur le site.

Du côté des itérateurs, je ne sais plus s'ils sont présents, mais ils étaient tous remplaçable (par un simple indice, dans le cas de la boucle, et par un pointeur dans le cas des [] imbriqués).

Bref, il était très faisable avec les les éléments présents sur le site ;) .
22 août 2008 à 18:12:08

Ah alors j'ai pas été cherché comme y faut =/
23 août 2008 à 10:23:15

J'ai corrigé le code pour qu'il soit correct au niveau des parenthèses. J'ai également mit un switch à la place des if-else.

Au vu des remarques, voulez-vous que je publie une version plus simple du code ?
Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
23 août 2008 à 11:08:35

Oui ce serait préférable, du moin pour moi.
Merci
23 août 2008 à 12:17:16

Il y a encore quelque chose à enlever (oui, je suis chiant, je sais). Dans le fichier BrainFuck.cpp : la variable globale OPERATEURS ne sert à rien, elle n'est pas (ou plus) utilisé dans le code
23 août 2008 à 18:00:30

bijour !
juste une petite idée comme ça ....
pourquoi ne pas afficher la difficulté de l'exercice dans le tableau d'affichage de la page1 ? ça pourrait être utile ! peut-être est il dur de juger combien un exercice est difficile mais je pense que ce serait bien, on pourrait organiser un soudage sur une trentaine de personne pour mettre une note sur 10 .....
voila, c'était l'idée à la c** du jour (de leopard)
je poste dans la boite à idées !
@++

leoaprd
23 août 2008 à 18:23:34

Puisque c'est une idée à la c**, je pense pas en effet qu'ils l'accepteront :-°
23 août 2008 à 18:26:11

le but n'est pas qu'ils l'acceptent. si ils ne l'acceptent pas, les zéros peuvent juger et faire un topic pour ça ....
23 août 2008 à 18:27:15

La notion de difficulté varie trop selon chaque personne.

Même si on prend deux personnes qui n'ont comme base que le tuto de M@téo, ils peuvent trouver un même exercice très facile ou très difficile selon ce que l'un ou l'autre a/n'a pas compris.
23 août 2008 à 18:32:20

c'est pour ça qu'il faut faire voter au moins 30 personnes :)
23 août 2008 à 18:51:49

Par exemple ceux qui ont crée un os et qu'on leur demande de faire un IDE, ils disent que c'est facile. Un zéro par contre pourra dire que c'est difficile
23 août 2008 à 19:00:36

bah ouai mais en attendant que tu trouves 30 mecs qui ont chacun codé 1 OS et que tu les fasses voter sur le sdz pour un exercice de nanoc, je vais sur la lune en brasse et je reviens ok ? :p
ok il y aura des différence au niveau des votes mais globalement, les zéros qui participent aux exercices de nanoc on le même niveau .... (ou tentent de l'avoir :-° comme moi )

leopard