←検索窓の楽しみ方
  ショッピングモール  掲示板ランキング


【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※返信付き投稿の削除は禁止!
 ※マルチポスト(多重投稿)は慎んで!

 詳しくはこちら


 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板1こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.3846

四則演算のプログラム
投稿者---たくま(2005/06/01 14:03:40)


コマンドライン関数を用いて、文字によって四則演算をするようなプログラムを作ったのですが、前から順番に計算するプログラムとまってしまいました。もっと改良して、加減や乗除を判断して演算するようなプログラムをつくりたいのですができなくて困っています、プログラム例のようなもの教えてもらえるとありがたいです。
現在>>5+4*3=27
目標>>5+4*3=17

今できてるプログラム(前から順番に計算するプログラム)↓
#include <stdio.h>
#include <stdlib.h> /* atof()関数のプロトコル宣言ファイル */
#include <string.h> /* 文字列操作関数を使用するための標準ヘッダファイル*/

double init(double y);
double add(double x,double y);
double sub(double x,double y);
double mul(double x,double y);
double dev(double x,double y);

int main(int argc,char *argv[])
{
  int i;
  int j;
  double x;/*結果*/
  double y;/*数値(引数)*/

  x=0;

  for(i=1;i<argc;i++)
  {
    if(argv[i]==0)
    {
      goto EXIT;
    }
  }
  
  /*無限ループ*/
  for(i=1;i<argc;i+=2)
  {
    /*atof;文字列をdouble型の数値に変換*/
    y=atof(argv[i+1]);  

    /*演算子の判別*/
    if(strcmp(argv[i],"init")==0)
    {
      x=init(y);
    }
    else if(strcmp(argv[i],"add")==0)
    {
      x=add(x,y);
    }
    else if(strcmp(argv[i],"sub")==0)
    {
      x=sub(x,y);
    }
    else if(strcmp(argv[i],"mul")==0)
    {
      x=mul(x,y);
    }
    else if(strcmp(argv[i],"dev")==0)
    {
      x=dev(x,y);
    }
    
  }

EXIT:;
  for(j=2;j<argc;j++)
    {
    printf("%s ",argv[j]);
  }
  
  printf("=%lf\n",x);
  
  
  return 0;
}

/*-----------------------------------四則演算の関数-------------------------------------*/

double init(double b)/*なくてもよい*/
{
  return b;
}

double add(double a,double b)
{
  return a+b;
}

double sub(double a,double b)
{
  return a-b;
}

double mul(double a,double b)
{
  return a*b;
}

double dev(double a,double b)
{
  return a/b;
}




この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:四則演算のプログラム 3847 nop 2005/06/01 14:43:45
<子記事> Re:四則演算のプログラム 3849 tetrapod 2005/06/01 15:15:09


No.3847

Re:四則演算のプログラム
投稿者---nop(2005/06/01 14:43:45)


逆ポーランド記法に変換してから計算するとよいでしょう。


この投稿にコメントする

削除パスワード

No.3848

Re:四則演算のプログラム
投稿者---たくま(2005/06/01 15:10:50)


>逆ポーランド記法に変換してから計算するとよいでしょう。

実行コマンドに引数をつけて計算させたいのですが大丈夫でしょうか?
現在:a.out init 9 add 6 dev 3 =5
目標:a.out init 9 add 6 dev 3 =11
なのですが、この場合でも逆ポーランド記法に変換するようなプログラムは有効ですか?
勉強不足ですいません。


この投稿にコメントする

削除パスワード

No.3851

Re:四則演算のプログラム
投稿者---REE(2005/06/01 15:46:23)


>実行コマンドに引数をつけて計算させたいのですが大丈夫でしょうか?
>現在:a.out init 9 add 6 dev 3 =5
>目標:a.out init 9 add 6 dev 3 =11
>なのですが、この場合でも逆ポーランド記法に変換するようなプログラムは有効ですか?

これは、逆ポーランド記法について調べた上での質問ですか?
そうでなければ、まず逆ポーランド記法について調べて下さい。


この投稿にコメントする

削除パスワード

No.3852

Re:四則演算のプログラム
投稿者---たくま(2005/06/01 17:15:44)


>これは、逆ポーランド記法について調べた上での質問ですか?
>そうでなければ、まず逆ポーランド記法について調べて下さい。

一応調べました。ただ自分がやりたいのは括弧とかを使わずに
a.out init 9 add 6 dev 3 =11 のようにしたいので、逆ポーランド記法を活用できないかもしれないと思って質問しました。
一つ前の演算子が何で、今の演算子が何かによって場合わけできるようにしたいです。


この投稿にコメントする

削除パスワード

No.3853

Re:四則演算のプログラム
投稿者---おでん(2005/06/01 17:38:22)


>a.out init 9 add 6 dev 3 =11 のようにしたいので、逆ポーランド記法を活用できないかもしれないと思って質問しました。

“init 9 add 6 dev 3 =11”

9に6を足して3で割っても11にはなりません
ヒント・・・
9 6 + 3 / -> 5

9 6 3 / + ->11

6 3 / 9 + -> 11


この投稿にコメントする

削除パスワード

No.3854

Re:四則演算のプログラム
投稿者---たくま(2005/06/01 17:45:02)


>9に6を足して3で割っても11にはなりません

だから a.out init 9 add 6 dev 3 のままで一つ前の演算子が何で、今の演算子が何かによって場合わけできるようにしたいんです。
普通にやれば5になります。そこまではできたのですが、演算子に優先順位をつけて判断するようなプログラムがつくれなくて困っています。
説明不足ですいません。



この投稿にコメントする

削除パスワード

No.3855

Re:四則演算のプログラム
投稿者---おでん(2005/06/01 18:03:07)


9 6 + 3 / -> 5
9 6 3 / + ->11
の違いがわかりますか?

数値   演算子



     div  → 6を3で割る(1)
     add  →(1)に9を足す 答え:11


この投稿にコメントする

削除パスワード

No.3857

Re:四則演算のプログラム(訂正)
投稿者---おでん(2005/06/01 18:19:44)


>数値   演算子
>9
>6
>3
>     div  → 6を3で割る(1)
>     add  →(1)に9を足す 答え:11

ちっと間違えた _(_"_)_
数値   演算子

     add  →(1)に9を足す(ここでは、何に足していいかわからない)

     div  → 6を3で割る(1)


なんか変でしょ?
これは、逆ポーランドじゃないよ。

6 3 / 9 + -> 11か9 6 3 / + ->11 でなきゃおかしい・・・

逆ポーランドにする時は、日本語で考えればいい
6を3で割って9を足す。(6 3 / 9 +) 


この投稿にコメントする

削除パスワード

No.3859

Re:四則演算のプログラム(訂正)
投稿者---円零(2005/06/01 18:38:17)


逆ポーランド記法でない入力を処理するプログラムを作りたい人に対し、
延々逆ポーランド記法を、しかもわざと回りくどく説明してもしょうがないのでは。
逆ポーランド記法については一応調べたそうですし。


この投稿にコメントする

削除パスワード

No.3860

Re:四則演算のプログラム(訂正)
投稿者---おでん(2005/06/01 18:59:08)


>逆ポーランド記法でない入力を処理するプログラムを作りたい人に対し、
>延々逆ポーランド記法を、しかもわざと回りくどく説明してもしょうがないのでは。

解りました、以後慎みます。


この投稿にコメントする

削除パスワード

No.3861

Re:四則演算のプログラム(訂正)
投稿者---かずま(2005/06/01 19:04:36)


> 逆ポーランド記法でない入力を処理するプログラムを作りたい人に対し、
> 延々逆ポーランド記法を、しかもわざと回りくどく説明してもしょうがないのでは。

同感です。

・mul なら数値の掛け算をして積を求める。
・add なら積の足し算をして和を求める。

なぜ、こんな単純な考え方を無視して、逆ポーランド記法にこだわるのか
理解できません。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int ac, ci = 1;  char **av, *token;

char *get(void) { return token = (ci < ac) ? av[ci++] : (char*)""; }

double fact(void)
{
    char *p;  double v = strtod(get(), &p);
    p == token || *p ? (token = "bad") : get();
    return v;
}

double term(void)
{
    double v = fact();
    for (;;)
        if      (!strcmp(token, "mul")) v *= fact();
        else if (!strcmp(token, "div")) v /= fact();
        else return v;
}

double expr(void)
{
    double v = term();
    for (;;)
        if      (!strcmp(token, "add")) v += term();
        else if (!strcmp(token, "sub")) v -= term();
        else return v;
}

int main(int argc, char *argv[])
{
    double v;  int i;
    ac = argc, av = argv;
    if (strcmp(get(), "init")) ci = 1;
    v = expr();
    for (i = 1; i < argc; i++) printf(" %s", argv[i]);
    printf(*token ? " = Error\n" : " = %.15g\n", v);
    return 0;
}



この投稿にコメントする

削除パスワード

No.3862

Re:四則演算のプログラム(訂正)
投稿者---たくま(2005/06/01 23:15:53)


ちょっと忙しくて今、読ましてもらいました。
いろいろと意見ほんとうにありがとうございます。
逆ポーランド記法を使わずに中間記法でがんばってみます。
どんな意見もありがたいことに変わりはないので逆ポーランド記法についてももう少し勉強してみます。
またいろいろ質問すると思うのでよろしくお願いします。


この投稿にコメントする

削除パスワード

No.3863

Re:四則演算のプログラム(訂正)
投稿者---たくま(2005/06/02 00:50:47)


ソースを読ましてもらいながら条件演算子について少し勉強したのですが、理解不足なので、できれば条件演算子を用いてるところをif文で書いてもらえませんか?

i = (a > b) ? a : b;
が
if (a >= b) 
{
    i = a;
} else {
    i = b;
}
</pre>

程度しか理解できてません。


この投稿にコメントする

削除パスワード

No.3864

Re:四則演算のプログラム(訂正)
投稿者---かずま(2005/06/02 01:35:41)


> ソースを読ましてもらいながら条件演算子について少し勉強したのですが、
> 理解不足なので、できれば条件演算子を用いてるところをif文で書いて
> もらえませんか?
> char *get(void) { return token = (ci < ac) ? av[ci++] : (char*)""; }

char *get(void)
{
    if (ci < ac)
        token = av[ci++];
    else
        token = "";
    return token;
}


>   p == token || *p ? (token = "bad") : get();

    if (p == token || *p != '\0')
        token = "bad";
    else
        get();


>   printf(*token ? " = Error\n" : " = %.15g\n", v);

    if (*token != '\0')
        printf(" = Error\n");
    else
        printf(" = %.15g\n", v);

これでよろしいでしょうか?

ところで、プログラムのほうはご理解いただけるでしょうか?

たくまさんのプログラムは、

・mul なら数値の掛け算をして積を求める。
・add なら数値の足し算をして和を求める。

となっているから、左から順にしか計算をしないのです。

・add なら「積」の足し算をして和を求める。

とすればよいのです。ちょっと関数の説明をすると、

・expr() は、積の和を求める関数。
・term() は、数値の積を求める関数。
・fact() は、トークンを数値に変え、次のトークンを用意する関数。
・get() は、argv[i] を順次トークンとして返す関数。

これでお解りいただけますか?


この投稿にコメントする

削除パスワード

No.3870

Re:四則演算のプログラム(訂正)
投稿者---たくま(2005/06/02 14:59:35)


if (p == token || *p != '\0')
token = "bad";
else
get();


の部分がよくわからないです。


この投稿にコメントする

削除パスワード

No.3872

Re:四則演算のプログラム(訂正)
投稿者---まきじ(2005/06/02 20:16:25)


>の部分がよくわからないです。

strtod(get(),&p) の get() で ""へのポインタ が返された時、
p と token は、同じポインタを示します。
token が "" へのポインタであるということは、
式の最後であるという事になり、計算終了になります。


この投稿にコメントする

削除パスワード

No.3889

Re:四則演算のプログラム(訂正)
投稿者---かずま(2005/06/03 10:29:49)


> if (p == token || *p != '\0')
> token = "bad";
> else
> get();
> 
> 
> の部分がよくわからないです。

私は、インデント(字下げ)のないプログラムを見ると不快になるため、原則
として回答しないことにしているんですが、他の方が私の意図とは異なる
回答をしているので、真意を伝えるために説明します。

v = strtod(get(), &p); で、get() を実行したとき、token が "355" で
あれば v は 355 になり、p は "355" の最後の '\0' を指します。

token が "E55" の場合、v は 0 になり、p は 'E' を指します。
token が "3SS" の場合、v は 3 になり、p は 'S' を指します。

すなわち、数値表現として正しくない入力があった場合、strtod() でそれが
検出できるので、Error を表示させるために token を "bad" にしています。



この投稿にコメントする

削除パスワード

No.3858

Re:四則演算のプログラム
投稿者---nop(2005/06/01 18:38:09)


>だから a.out init 9 add 6 dev 3 のままで一つ前の演算子が何で、今の演算子が何かによって場合わけできるようにしたいんです。
>普通にやれば5になります。そこまではできたのですが、演算子に優先順位をつけて判断するようなプログラムがつくれなくて困っています。
>説明不足ですいません。

入力は今のままでよいです。
それを、内部で

1.入力データを逆ポーランド記法に変換
2.変換したデータを逆ポーランド記法に乗っ取って計算
3.計算結果を出力

と言う手順になります。
もちろん、計算ルーチンには演算子による場合分けが必要となります。

早い話、人間が慣れ親しんでる計算式を
コンピュータが計算しやすい形式に変換することで、
計算ルーチンが分かりやすくなります。

モジュール分けもしやすく、
モジュール毎に処理の詳細を考えれば、
シンプルに考えられるはずです。



この投稿にコメントする

削除パスワード

No.3911

Re:四則演算のプログラム
投稿者---かずま(2005/06/05 21:36:56)


> 1.入力データを逆ポーランド記法に変換
> 2.変換したデータを逆ポーランド記法に乗っ取って計算
> 3.計算結果を出力

逆ポーランド記法に変換してから計算するプログラムを書いてみました。
確かに、計算の処理は簡単です。
しかし、その前の変換の処理のところで計算をしたほうが、余分な場所と
後からの計算が不要になって、シンプルになると思います。
なぜ、多くの人が逆ポーランド記法への変換をすすめるのでしょうか?


> モジュール分けもしやすく、
> モジュール毎に処理の詳細を考えれば、
> シンプルに考えられるはずです。

このことについてもっと具体的に説明していただけませんか?
----------------------------------------------------------------------
// 逆ポーランド記法に変換してから計算するプログラム

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define g() (t = i < argc ? argv[i++] : "")
#define f() (strtod(g(), &p), p!=t && !*p ? (a[n++] = t, g()) : (t = "bad"))

int main(int argc, char *argv[])
{
    double v[4]; int i = 1, n = 0, s, k; char *t, *p, *a[64];

    for (f(); ; )
        if (!(s = strcmp(t, "add")) || !strcmp(t, "sub")) {
            for (f(); ; )
                if      (!strcmp(t, "mul")) f(), a[n++] = "mul";
                else if (!strcmp(t, "div")) f(), a[n++] = "div";
                else { a[n++] = s ? "sub" : "add"; break; }
        }
        else if (!strcmp(t, "mul")) f(), a[n++] = "mul";
        else if (!strcmp(t, "div")) f(), a[n++] = "div";
        else break;
    if (*t) puts(" Error");
    else {
        for (k = i = 0; i < n; i++)
            if      (a[i][0] == 'a') --k, v[k-1] += v[k];
            else if (a[i][0] == 's') --k, v[k-1] -= v[k];
            else if (a[i][0] == 'm') --k, v[k-1] *= v[k];
            else if (a[i][0] == 'd') --k, v[k-1] /= v[k];
            else v[k++] = atof(a[i]);
        for (i = 0; i < n; i++) printf(" %s", a[i]);
        printf(" = %.15g\n", v[0]);
    }
    return 0;
}

----------------------------------------------------------------------
// 逆ポーランド記法に変換せずに計算するプログラム

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define g() (t = i < argc ? argv[i++] : "")
#define f() (v = strtod(g(), &p), p!=t && !*p ? g(), v : (t = "bad", 0))

int main(int argc, char *argv[])
{
    double x, y, v;  int s, i = 1;  char *t, *p;

    for (x = f(); ; )
        if (!(s = strcmp(t, "add")) || !strcmp(t, "sub"))
            for (y = f(); ; )
                if (!strcmp(t, "mul")) y *= f();
                else if (!strcmp(t, "div")) y /= f();
                else { s ? (x -= y) : (x += y); break; }
        else if (!strcmp(t, "mul")) x *= f();
        else if (!strcmp(t, "div")) x /= f();
        else break;
    if (*t) puts(" Error");
    else {
        for (i = 1; i < argc; i++) printf(" %s", argv[i]);
        printf(" = %.15g\n", x);
    }
    return 0;
}

----------------------------------------------------------------------



この投稿にコメントする

削除パスワード

No.3912

Re:四則演算のプログラム
投稿者---RAPT(2005/06/05 21:57:49)


まったくもってPGには関係無いことですみませんが、
>> 2.変換したデータを逆ポーランド記法に乗っ取って計算
「乗っ取って」…奪ってはいけません。
「則って」もしくは「法って」が正しいです。



この投稿にコメントする

削除パスワード

No.3913

Re:四則演算のプログラム
投稿者---コンパイラ屋(2005/06/06 23:55:54)


>> 1.入力データを逆ポーランド記法に変換
>> 2.変換したデータを逆ポーランド記法に乗っ取って計算
>> 3.計算結果を出力
>
>逆ポーランド記法に変換してから計算するプログラムを書いてみました。
>確かに、計算の処理は簡単です。
>しかし、その前の変換の処理のところで計算をしたほうが、余分な場所と
>後からの計算が不要になって、シンプルになると思います。
>なぜ、多くの人が逆ポーランド記法への変換をすすめるのでしょうか?

この変換のしかたの手法について、ここにいたるまで誰も語っていないからだと思います。
かずまさんの方法は再帰下降法と呼ばれる手法で、
これと逆ポーランド語記法との間にはあまり関連性がないと思います。
それに比べて演算子順位法のほうですと、まったく同じというわけではないのですが
逆ポーランド語記法の文法をまじめにスタック構造を構築して処理する場合と
似たような動きになります。

私はコメントされた人ではないので真相はわかりませんが
この動きを持って逆ポーランド語記法に変換と呼んでいるのではないかという気がします。
もしくはまじめにスタック構造を構築した逆ポーランド語記法の処理を
学んでもらってから演算子順位法について教えるつもりだったのかもしれません。
四則演算が処理できるようになると、次にやりたくなるのが演算子の追加が相場ですが、
演算子順位法では容易です。この特性も念頭にあったという可能性はあります。


この投稿にコメントする

削除パスワード

No.3956

Re:四則演算のプログラム
投稿者---かずま(2005/06/08 10:11:44)


> かずまさんの方法は再帰下降法と呼ばれる手法で、
> これと逆ポーランド語記法との間にはあまり関連性がないと思います。

最初のプログラムは、expr、term、fact という関数名から再帰下降法のよう
に見えますが、四則演算だけで括弧がないので、再帰は必要ありません。実際、
書き直したプログラムでは、main の中の二重ループで実現されています。

「逆ポーランド語記法」を Google で検索すると 1件しかヒットしません。
演算子の前置記法をポーランド記法、後置記法を逆ポーランド記法というのは、
ポーランドの論理学者Jan Lukasiewicz(ヤン・ウカシェーヴィッチ)氏が考案
したものですが、彼の名前が発音しにくい、記憶しにくいということから
「ウカシェーヴィッチ記法」とはならず、「ポーランド人の記法」ということ
で (Reverse) Polish Notation となったと学んだ覚えがあります。
したがって、「ポーランド語」ではないと思います。


> それに比べて演算子順位法のほうですと、まったく同じというわけではないのですが
> 逆ポーランド語記法の文法をまじめにスタック構造を構築して処理する場合と
> 似たような動きになります。
>
> 私はコメントされた人ではないので真相はわかりませんが
> この動きを持って逆ポーランド語記法に変換と呼んでいるのではないかという気がします。

それなら、「演算子順位法」を使いましょうと最初から言えばよさそうなもの
ですが。

とにかく、最初の質問は、括弧がなくて足し算と掛け算の 2つの演算子順位
しかない単純な問題なので、牛刀を持ち出すことはないということでしょう。

書き直したプログラムで "init" の処理が抜けていましたので追加し、
他もちょっと修正してみました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define g() (t = i < argc ? argv[i++] : "")
#define f() (v = strtod(g(), &p), p==t && *p ? t = "bad": g(), v)

int main(int argc, char *argv[])
{
    double x = 0, y, v;  int s = 0, i = 1;  char *t, *p;

    if (strcmp(g(), "init")) i = 1;
    do {
        for (y = f(); ; )
            if (!strcmp(t, "mul")) y *= f();
            else if (!strcmp(t, "div")) y /= f();
            else { s ? (x -= y) : (x += y); break; }
    } while (!(s = strcmp(t, "add")) || !strcmp(t, "sub"));
    for (i = 1; i < argc; i++) printf(" %s", argv[i]);
    printf(*t ? " = Error\n" : " = %.15g\n", x);
    return 0;
}



この投稿にコメントする

削除パスワード

No.3856

Re:四則演算のプログラム
投稿者---REE(2005/06/01 18:10:14)


>一応調べました。ただ自分がやりたいのは括弧とかを使わずに
>a.out init 9 add 6 dev 3 =11 のようにしたいので、逆ポーランド記法を活用できないかもしれないと思って質問しました。
>一つ前の演算子が何で、今の演算子が何かによって場合わけできるようにしたいです。

逆ポーランド記法で入力するのではなく、逆ポーランド記法に変換するのです。
上記の例では、9 6 3 dev add と変換されます (initを除く)

また、逆ポーランド記法では元々括弧は使いません。括弧を使わないのが、どうして障害になるのですか?



この投稿にコメントする

削除パスワード

No.3849

Re:四則演算のプログラム
投稿者---tetrapod(2005/06/01 15:15:09)


どーでもいいけど、除算は divide ではないかと思う今日この頃。


この投稿にコメントする

削除パスワード

No.3850

Re:四則演算のプログラム
投稿者---たくま(2005/06/01 15:18:18)


>どーでもいいけど、除算は divide ではないかと思う今日この頃。

指摘ありがとうございます。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity