No.3ベストアンサー
- 回答日時:
今頃このような問題をしているという事は大学1年生位でしょうかね?
確かにこの前始めた人にこの問題は結構辛いですね。
回答例・・というか99%答えなんですけど^^;
サンプルプログラムを書きましたんで、見てください。
入門者さんみたいなのでなるべく簡単な関数や書き方で書きました。
#include<stdio.h>
#include<string.h>
int num[20],atainokosu;
char input_str[100],enzanshi[10],str[10][20];
int a2i(char*str){
int cnt;
int num=0;
for (cnt = 0; (str[cnt] >= '0') && (str[cnt] <= '9') ; cnt++) {
num = 10 * num + (str[cnt]-'0');
}
return num;
}
int getch(){
int i=0;
while( ( input_str[i] = getchar() ) != EOF)
i++;
input_str[i-1]='\0';
return 0;
}
int divch(){
int i=0,j=0,s=0;
while(input_str[i]!='\0'){
if(input_str[i] == '+' || input_str[i]=='-' || input_str[i]=='*' || input_str[i] =='/'){
enzanshi[j]=input_str[i];
j++;
s=0;
}
else{
str[j][s]=input_str[i];
s++;
}
i++;
}
atainokosu=j;
for(i=0;i<=j;i++)
num[i]=a2i(str[i]);
return 0;
}
int calc(){
int i=0,j;
while(i<=atainokosu){
if(enzanshi[i]=='*'){
num[i]=num[i]*num[i+1];
j=i;
while(j<=atainokosu){
num[j+1]=num[j+2];
enzanshi[j]=enzanshi[j+1];
j++;
}
atainokosu--;
}
else if(enzanshi[i]=='/'){
num[i]=num[i]/num[i+1];
j=i;
while(j<=atainokosu){
num[j+1]=num[j+2];
enzanshi[j]=enzanshi[j+1];
j++;
}
atainokosu--;
}
else
i++;
}
i=0;
while(0<atainokosu){
if(enzanshi[i]=='+'){
num[i]=num[i]+num[i+1];
j=i;
while(j<=atainokosu){
num[j+1]=num[j+2];
enzanshi[j]=enzanshi[j+1];
j++;
}
atainokosu--;
}
else if(enzanshi[i]=='-'){
num[i]=num[i]-num[i+1];
j=i;
while(j<=atainokosu){
num[j+1]=num[j+2];
enzanshi[j]=enzanshi[j+1];
j++;
}
atainokosu--;
}
else
i++;
}
return 0;
}
int main(){
int i;
getch();
divch();
calc();
printf("計算式=\"%s\"\n結果=%d\n",input_str,num[0]);
return 0;
}
これは整数の四則演算ができるプログラムです。
浮動小数点には対応していません。
例:
「10/5+2*5」と入力し、エンターを押す。その後Ctrl + Zを押してエンターを押す。すると計算結果が表示される。
100%答えを書いてしまってはよくないので(といっても99%答えですけど)
ココまでプログラム書いたんで、今度は小数点も計算できるように改良してください。
ヒントは#2さんが既におっしゃっています。
4.strtod して値を返す
この辺をヒントに頑張ってください。
わかれば1分で完成すると思います。
プログラムの中身は次に説明します。
頑張ってください。
No.8
- 回答日時:
逆ポーランド記法に変換してからあとでまとめて計算しなくても
スタックに中間結果をつみながら変換(と計算)をできないこともありません。
Cで書くと答えになっちゃうのでPerlで。
use strict;
use warnings;
my @src = split /([\/*+-])/, <DATA>;
my @stack;
my %fn_tbl = (
'+' => sub {my ($lhs, $rhs) = @_; $lhs + $rhs},
'-' => sub {my ($lhs, $rhs) = @_; $lhs - $rhs},
'*' => sub {my ($lhs, $rhs) = @_; $lhs * $rhs},
'/' => sub {my ($lhs, $rhs) = @_; if ($rhs != 0) {$lhs / $rhs}
else {warn "divide by 0\n"; 0}}
);
sub do_calc {
return if @stack < 3;
my $arg2 = pop @stack;
my $op = pop @stack;
my $arg1 = pop @stack;
push @stack, $fn_tbl{$op}->($arg1, $arg2);
}
foreach my $item (@src) {
$item =~ s/^\s+//;
$item =~ s/\s+$//;
if ($item =~ m/\d+/) {
push @stack, $item;
next;
}
if ($item =~ m/[+-]/) {
do_calc if @stack > 3 and $stack[-2] =~ m/[*\/+-]/;
push @stack, $item;
}
elsif ($item =~ m/[*\/]/) {
push @stack, $item;
}
}
do_calc while @stack > 1;
print join ':', @stack;
__END__
5.2+20*2-3
No.7
- 回答日時:
#2>3.左から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
の部分、間違ってました。
これだと右結合になってしまいます。
3.右から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
ですね。(+、-は、ぶっちゃけどっちでも変わらないけど)
#5>//3.左から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
#5>pos=strpbrk(buff, "*/");
を
//3.右から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
pos =strrchr(buff, '*');
pos2=strrchr(buff, '/');
if(pos<pos2)
pos=pos2;
に修正
No.6
- 回答日時:
逆ポーランド法と検索してみて下さい。
どんな入力式に対しても優先順位を一つずつ考慮して計算するルーチンを作るのは骨が折れると思います。
逆ポーランド法を用いるとすべての演算を同じルーチンにかけるだけで優先順位を考慮して計算できるので解りやすく、バグも少なく出来ると思います。
同時にスタックやキューの勉強も出来て一石二鳥です。
プログラムの構造を大きく分けると、
1.数式を格納した文字列を数値、演算子ごとに配列に格納する
2.配列を逆ポーランド記法に並び替える
3.逆ポーランド法に従って計算する
という三段構成です。
1の部分は他の方の回答を参考に出来ると思います。
2と3はアルゴリズムが書籍やwebで明確に示されているのでプログラムを起こすのは容易に出来ると思います。
この方法を用いるとすべての演算をひとつのアルゴリズムで行えるため他の演算子(たとえばmod)や関数(たとえばsin,cos,tan,nPr)、括弧などへの拡張が驚くほど簡単です。
参考サイトはVisualBasicのコードしかありませんがCも探せばあるはずです。アルゴリズムもスタックの図を用いて説明してありますので自分で起こすことも出来ると思います。
参考URL:http://www.nextftp.com/swlabo/m1_vbnet/hp_tips/h …
No.5
- 回答日時:
#2の方針のプログラム
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
double eval(char *exp){
int len;
char *buff, *pd, *ps;
char *pos;
//0.全てのスペース文字を取り除く
buff=strdup(exp);
for(pd=buff,ps=exp;*ps;ps++){
if(isspace(*ps))continue;
*pd++=*ps;
}
*pd=*ps;
//1.左から+を探し2分割する、return eval(左部分)+eval(右部分);
pos=strchr(buff, '+');
if(NULL!=pos){
*pos='\0';
return eval(buff)+eval(pos+1);
}
//2.先頭の-は、無視して-を探し2分割する、return eval(左部分)-eval(右部分);
pos=strchr(buff+1, '-');
if(NULL!=pos){
*pos='\0';
return eval(buff)-eval(pos+1);
}
//3.左から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
pos=strpbrk(buff, "*/");
if(NULL!=pos){
if(*pos=='*'){
*pos='\0';
return eval(buff)*eval(pos+1);
} else {
*pos='\0';
return eval(buff)/eval(pos+1);
}
}
//4.strtod して値を返す
return atof(buff);//手抜き、本当はstrtod で全て消費したか、エラーがないか調べる
//手抜き2,retvalue = eval(左) 演算子 eval(右);free(buff);return retvalue; すべき
}
void main(void){
char buff[128];
printf("計算式=");
fgets(buff, 128, stdin);
printf("結果=%g\n",eval(buff));
}
No.4
- 回答日時:
下の補足です。
プログラムの中身について説明します。
実際にプログラムを上から順に見ながら説明をみてください。
まずa2i()関数は無理に理解しなくて結構です。
この関数名を検索したらヒットする有名な
「文字列としての数字」から「int型の数字」に変換する関数です。
いつもコピペで使えば良いので構造を無理に覚えなくて大丈夫です。
getch()関数で入力文字列を取得します。
getcharは標準関数なので自分で調べてください。
EOFは^Zが入力された時判断するものです。
Ctrl Z を押すのはそのためです。
なぜinput_str[i-1]に\0を代入しているかと言うと文字列は終端記号が必要で、しかも^Zする前に1回エンターを押しているせいで\n改行記号が1つ前にはいっているため、このような代入をしています。
divch()関数は演算子ごとに塊をわけています。
数字文字列は数字文字列、記号は記号の配列に代入しています。
atainokosuに項数を格納します。いくつ項があるか数えています。
終わったら、文字列としての数字をint型の数字にa2iで変換します。
calc()関数で実際に計算しています。
ちょっとこれは説明が難しいですね~・・。
まず四則演算って+-*/のうち*/を先にしないといけないですね。
whileをその2つに大分します。
*/を全部計算しおわったら+-の計算をしています。
今の項と次の項を計算し計算結果を今の項に代入します。
代入し終わったら計算した次の甲を順次左によせていきます。
例:
num[0]に3
num[1]に2
num[2]に10
num[3]に2
が入っているとする。
num[0]*num[1]の計算結果をnum[0]に代入する。
後は左よせする。
num[1]に10
num[2]に2
を代入。
3 * 2 + 10 - 2
↓
6 + 10 - 2
こんな感じです。
演算子も
enzanshi[0]=='*'
だったものを
enzanshi[0]=enzanshi[1]して+に変化させています。
つまり左よせしています。
四則演算すべてを計算し終わったらnum配列は[0]だけに計算結果が残るので
これを出力すればよい。
プログラムの流れは以上です。
解らない事があれば、聞いてください。
この回答へのお礼
お礼日時:2006/07/21 20:13
本当に御丁寧にありがとうございました。
一度、このプログラムを回してみて、それから何とかしてみます。
本当にありがとうございます!
No.2
- 回答日時:
double eval(char *);
0.前後のスペース文字を取り除く(あるいは、全てのスペース文字を取り除く)
1.左から+を探し2分割する、return eval(左部分)+eval(右部分);
2.先頭の-は、無視して-を探し2分割する、return eval(左部分)-eval(右部分);
3.左から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
4.strtod して値を返す
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- C言語・C++・C# C言語 3 2022/10/04 15:07
- Java java 飾子を付けること(public static・・・) ・コンソールへの出力処理はmainメ 2 2022/06/16 19:34
- Java javaの質問です 次の機能を有するメソッド4つを自クラスに作成し、実装したいです 【機能】 足し算 1 2022/06/15 17:49
- C言語・C++・C# このプログラミングの問題を教えて欲しいです。 キーボードから整数kを入力し、kが配列aの中に何個存在 2 2022/12/19 22:50
- その他(プログラミング・Web制作) プログラムが書けません。 6 2023/01/22 22:58
- Excel(エクセル) エクセルで値ではなく関数を参照する方法 6 2023/03/19 00:50
- Excel(エクセル) エクセルでSUMIFS関数で条件範囲の部分が#valueになる。 4 2023/04/28 12:42
- Excel(エクセル) 文字列を数式として変換する事はできますか? 6 2022/06/23 10:38
- その他(Microsoft Office) Excelで時間計算(負) 8 2023/02/26 05:47
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
65536は2の何乗なのでしょうか?
-
C言語の課題で、1年の秒数を計...
-
VBAの再計算が反映されない件に...
-
排他的論理和 BCC(水平パリテ...
-
変化させるセルが変化しない
-
Java 電卓の連続計算
-
C言語についてです。 再帰を使...
-
2進数の乗算をc言語で計算した...
-
C# 計算処理中に実行中ウィン...
-
加速度から変位の変換について
-
バッチファイルでウインドウを...
-
電卓でmodの計算
-
C言語のプログラミングの問題で...
-
先行評価と遅延評価
-
EXCELなどで「返す」という表現
-
VBA 九九 Do While
-
階乗のマクロ
-
太陽の位置計算のプログラムを...
-
行列計算の速度
-
VB.NETで16bit符号なしを使いたい
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
65536は2の何乗なのでしょうか?
-
VBAの再計算が反映されない件に...
-
排他的論理和 BCC(水平パリテ...
-
EXCELなどで「返す」という表現
-
バッチファイルでウインドウを...
-
モジュラス103の計算とは何でし...
-
傾いた四角形内の範囲の条件式
-
Visual C++でdebugとreleaseで...
-
変化させるセルが変化しない
-
骨折リスク評価のFRAXについて...
-
C# 計算処理中に実行中ウィン...
-
VBAでの勤務時間計算
-
べき乗の計算が遅い理由
-
C言語についてです。 再帰を使...
-
Excel VBAにてFFT
-
数値計算の高速化 (cos, sin, exp)
-
VBとVBAの違い
-
VB6で正確なミリ秒を計測したい...
-
スレッド処理からダイアログを...
-
matlabで計算終了
おすすめ情報