アプリ版:「スタンプのみでお礼する」機能のリリースについて

プログラミング初心者の大学生です。よろしくお願いいたします。

 計算式入力し、計算結果を表示するプログラムの作成方法がわかりません。
条件としては、数値は実数の入力を可能とし、括弧の利用は考えないとのことです。
 
 【実行例】

 ・計算式="5.2+20*2-3"
 ・結果=42.2

 いきなり難易度が上がっため、自分の手には負えませんでした…。
どなたか、良いお答えをよろしくお願いいたします。

A 回答 (8件)

今頃このような問題をしているという事は大学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分で完成すると思います。

プログラムの中身は次に説明します。

頑張ってください。
    • good
    • 1

逆ポーランド記法に変換してからあとでまとめて計算しなくても


スタックに中間結果をつみながら変換(と計算)をできないこともありません。

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
    • good
    • 0

#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;
に修正
    • good
    • 0

逆ポーランド法と検索してみて下さい。


どんな入力式に対しても優先順位を一つずつ考慮して計算するルーチンを作るのは骨が折れると思います。
逆ポーランド法を用いるとすべての演算を同じルーチンにかけるだけで優先順位を考慮して計算できるので解りやすく、バグも少なく出来ると思います。
同時にスタックやキューの勉強も出来て一石二鳥です。

プログラムの構造を大きく分けると、
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 …
    • good
    • 0

#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));
}
    • good
    • 0

下の補足です。


プログラムの中身について説明します。
実際にプログラムを上から順に見ながら説明をみてください。

まず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]だけに計算結果が残るので
これを出力すればよい。


プログラムの流れは以上です。
解らない事があれば、聞いてください。
    • good
    • 0
この回答へのお礼

本当に御丁寧にありがとうございました。
一度、このプログラムを回してみて、それから何とかしてみます。
本当にありがとうございます!

お礼日時:2006/07/21 20:13

double eval(char *);


0.前後のスペース文字を取り除く(あるいは、全てのスペース文字を取り除く)
1.左から+を探し2分割する、return eval(左部分)+eval(右部分);
2.先頭の-は、無視して-を探し2分割する、return eval(左部分)-eval(右部分);
3.左から*または/を探し2分割する、return eval(左部分)*(/)eval(右部分);
4.strtod して値を返す
    • good
    • 0

1. */を挟む2数を見つけ、掛ける/割る


2. (1)で見つけた部分をその積/商に置き換える
3. (1)(2)を*/がなくなるまで繰り返す。
4. +-だけになったので、そのまま足す/引く
    • good
    • 0
この回答へのお礼

要点をまとめて説明していただき、ありがとうございました。

お礼日時:2006/07/21 20:09

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!