2

中置問題ソルバーを作成していますが、最後の while ループでクラッシュして、方程式の最後の部分 a を終了します。

メインで最後のwhileループを呼び出して、スタックに残っているものを解決し、そこにハングします。スタックから最後の要素をポップすると、ループを離れて間違った答えを返します。

//
//
//
//
//
#include <iostream>
#include<stack>
#include<string>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <sstream>
using namespace std;
#define size 30
int count=0;
int count2=0;
int total=0;
stack< string > prob;
char equ[size];
char temp[10];
string oper;
string k;
char t[10];
int j=0;
char y;


   int solve(int f,int s, char o)
   {
  cout<<"f="<<f<<endl;
  cout<<"s="<<s<<endl;
  cout<<"o="<<o<<endl;
  int a;
  if (o== '*')//checks the operand stack for operator
  {
    cout << f << "*" << s << endl;
    a= f*s;
  }
  if (o == '/')//checks the operand stack for operator
  {
    cout << f << "/" << s << endl;
    if(s==0)
    {
      cout<<"Cant divide by 0"<<endl;
    }
    else
      a= f/s;
  }
  if (o == '+')//checks the operand stack for operator
  {
    cout << f << "+" << s << endl;
    a= f+s;
  }
  if (o == '-')//checks the operand stack for operator
  {
    cout << f << "-" << s << endl;
    a= f-s;
  }
  return a;
}



int covnum()
{
  int l,c;
  k=prob.top();
  for(int i=0;k[i]!='\n';i++)t[i]=k[i];
  return l=atoi(t);
}


char covchar()
{
  k=prob.top();
  for(int i=0;k[i]!='\n';i++)t[i]=k[i];
  return t[0];
}


void tostring(int a)
{
  stringstream out;
  out << a;
  oper = out.str();
}


void charstack(char op)
{
  oper=op;
  prob.push(oper);
}


void numstack(char n[])
{
  oper=n;
  prob.push(oper);
}

void setprob()
{
  int f,s;
  char o;
  char t;
  int a;
  int i;
  t=covchar();
  if(ispunct(t))
  {
    if(t=='(')
    {
      prob.pop();
    }
    if(t==')')
    {
      prob.pop();
    }
    else if(t=='+'||'-')
    {
      y=t;
      prob.pop();
    }
    else if(t=='/'||'*')
    {
      y=t;
      prob.pop();
    }
  }
  cout<<"y="<<y<<endl;
  i=covnum();
  cout<<"i="<<i<<endl;
  s=i;
  prob.pop();
  t=covchar();
  cout<<"t="<<t<<endl;
  if(ispunct(t))
  {
    o=t;
    prob.pop();
  }
  i=covnum();
  cout<<"i="<<i<<endl;
  f=i;
  prob.pop();
  t=covchar();
  if (t=='('||')')
  {
    prob.pop();
  }
  a=solve(f,s, o);
  tostring(a);
  prob.push(oper);
  cout<<"A="<<prob.top()<<endl;
}


void postfix()
{
  int a=0;
  char k;
  for(int i=0;equ[i]!='\0';i++)
  {
    if(isdigit(equ[i]))//checks array for number
    {
      temp[count]=equ[i];
      count++;
    }
    if(ispunct(equ[i]))//checks array for operator
    {
      if(count>0)//if the int input is done convert it to a string and push to stack
      {
        numstack(temp);
        count=0;//resets the counter
      }
      if(equ[i]==')')//if char equals the ')' then set up and solve that bracket
      {
        setprob();
        i++;//pushes i to the next thing in the array
        total++;
      }
      while(equ[i]==')')//if char equals the ')' then set up and solve that bracket
      {
        i++;
      }
      if(isdigit(equ[i]))//checks array for number
      {
        temp[count]=equ[i];
        count++;
      }
      if(ispunct(equ[i]))
      {
        if(equ[i]==')')//if char equals the ')' then set up and solve that bracket
        {
          i++;
        }
        charstack(equ[i]);
      }
      if(isdigit(equ[i]))//checks array for number
      {
        temp[count]=equ[i];
        count++;
      }
    }
  }
}



int main()
{
  int a=0;
  char o;
  int c=0;

  cout<<"Enter Equation: ";
  cin>>equ;
  postfix();
  while(!prob.empty())
  {
    setprob();
    a=covnum();
    cout<<a<<" <=="<<endl;
    prob.pop();
    cout<<prob.top()<<"<top before c"<<endl;
    c=covnum();
    a=solve(c,a,y);
  }
  cout<<"Final Awnser"<<a<<endl;
  system ("PAUSE");
  return 0;
}
4

2 に答える 2

3

これが厳しすぎないことを願っていますが、コードにはさまざまな問題がたくさんあるようです。それらすべてに対処しようとするつもりはありませんが、まず、範囲外の集計へのアクセスに対処する即時クラッシュが発生します。

例:

for(int i=0;k[i]!='\n';i++)

k は std::string のインスタンスです。std::string は null で終了しません。文字列の長さを追跡するため、代わりに次のようにする必要があります。

for(int i=0;i<k.size();i++)

これらはより単純な種類のエラーですが、全体的なロジックにもいくつかのエラーが見られます。たとえば、トークナイザー (後置関数) は、式の最後の部分がオペランドであるケースを処理しません。それが許可された条件かどうかはわかりませんが、これは中置ソルバーが処理する必要があるものです (中置ソルバーに「postfix」という関数があると本当に混乱するため、この関数の名前を tokenize のような名前に変更することをお勧めします)。

何よりも、あなたへの私のアドバイスは、あなたのアプローチにいくつかの一般的な変更を加えることです.

  1. デバッガーについて学びます。これを十分に強調することはできません。コードを書いているときにコードをテストし、デバッガーを使用してコードをトレースし、状態変数が正しく設定されていることを確認する必要があります。

  2. この問題を解決するためにグローバル変数を使用しないでください。どこにでも物を渡すのを避けたくなるかもしれませんが、#1 を行うのが難しくなり、ソリューションの一般性も制限されます。変数を渡さないことで節約できたわずかな時間は、間違った操作を行うと、さらに多くの時間を費やすことになります。これらのいくつかをメンバー変数として格納するクラスを作成することも検討できます。これは、クラス メソッドで渡すのを避けることができますが、特に「equ」のような一時的な状態については、トークン化した後でも必要ありません。渡すだけです。それを必要なトークン化機能に入れ、それを廃止します。

  3. できるだけ早く変数を初期化します (理想的には、変数を最初に定義したとき)。スコープの先頭ですべての変数を宣言する、時代遅れの C スタイルのプラクティスがたくさん見られます。変数を使用するスコープを制限するようにしてください。これにより、コードがより安全になり、修正が容易になります。これは、グローバルを回避することと結びついています (#2)。

  4. 可能な場合はマクロに代わるものを優先し、そうでない場合は BIG_UGLY_NAMES を使用して他のものと区別してください。#define を使用して「サイズ」のプリプロセッサ定義を作成すると、実際には文字列の「サイズ」メソッドを使用する上記のコードが機能しなくなります。これは単純な整数定数にすることができますし、そうあるべきです。さらに良いことに、「equ」に std::string を使用することもできます (ファイル スコープをグローバルにしないことは別として)。

  5. 可能な場合は、標準の C++ ライブラリ ヘッダーを優先してください。<ctype.h>あるべきだ<cctype><stdlib.h>あるべきだ<cstdlib>、そして<stdio.h>あるべきだ<stdio>。非標準ヘッダーと .h 拡張子および標準ヘッダーを同じコンパイル ユニットに混在させると、一部のコンパイラで問題が発生する可能性があり、名前空間のスコープや関数のオーバーロードなどのいくつかの重要なことを見逃すことにもなります。

最後に、解決策に時間をかけて、注意と愛を込めてください。これは宿題であり、期限が迫っていることは理解していますが、この種のコーディングが受け入れられない現実の世界では、さらに厳しい期限に直面することになります。識別子に適切な名前を付け、コードを読みやすくフォーマットし、関数が何をするかを文書化します (コードの各行がどのように機能するかだけでなく、言語をよりよく理解するようになった後で実際に行うべきではありません)。コーディング TLC の中には、長い道のりを歩むものもあります。問題に対する解決策を設計する方法を真剣に考えてください (手続き型のアプローチを採用している場合は、問題を全体的なロジックの単なる切り刻みバージョンではなく、一般的な作業単位として手順に分解してください)。#2はこれに役立ちます。

** 例: グローバル入力文字列を処理し、グローバル スタックを操作して式を部分的に評価する「postfix」という名前の関数ではなく、入力文字列を受け入れて個々のトークンを返す*ようにします。これは、どこでも再利用できる一般的な関数であり、解決とテストがはるかに簡単な問題になりました。それを文書化し、そのように名前を付けます。使用法と、受け入れて返すものに焦点を当てます。例えば:

// Tokenize an input string. Returns the individual tokens as
// a collection of strings.
std::vector<std::string> tokenize(const std::string& input);

これは単なる例であり、この特定の問題に最適な方法である場合とそうでない場合がありますが、手順を適切に設計すれば、最終的には十分にテストされた再利用可能なコードのライブラリを自分で構築する必要があります。何度も何度も使用して、将来のすべてのプロジェクトをより簡単にすることができます。また、複雑な問題をいくつかの単純な問題に分解して解決することも容易になります。これにより、すべてが簡単になり、コーディングとテストのプロセス全体がはるかにスムーズになります。

于 2012-02-26T23:46:49.003 に答える
2

機能しないという問題に寄与する可能性が高い多くのことがわかります。

  • エラーまたは境界チェックはありません。これは宿題であり、特定の要件/仕様がいくつかのチェックの必要性を排除している可能性があることを理解していますが、入力を正しく解析していることを確認するためにまだいくつか必要です。equ/tmp/t の配列サイズを超えるとどうなりますか? スタックをポップ/トップしようとしたときにスタックが空の場合はどうなりますか?
  • else if (t == '+' || '-')意図したとおりに動作しない可能性が最も高いif ステートメントがいくつかあります。「-」は非ゼロであり、真の値に変換されるため、この式は実際には常に真です。あなたはおそらくしたいですelse if (t == '+' || t == '-')
  • 私が知る限り、スタックへの '(' の解析または追加をスキップしているようです。これにより、式を実際に適切に評価することができなくなります。
  • 途中でpostfix()複数の ')' をスキップする while ループがありますが、何もしません。
  • あなたのコードは従うのが非常に難しいです。変数と関数に適切な名前を付け、大部分のグローバル (実際にはそれらのほとんどは必要ありません) を削除すると、適切なインデントと式にいくつかのスペースを追加するのと同様に、非常に役立ちます。
  • 特に言及する価値のない他の小さな問題があります。たとえば、covchar() および covnum() 関数は必要以上に複雑です。

私は何年にもわたっていくつかの後置パーサーを書いてきましたが、あなたがやろうとしていることを本当に理解することはできません.式、特にネストされたレベルのブラケットを解析するために必要です。

于 2012-02-26T23:48:03.627 に答える