ファイルに書かれた式の計算について
ファイルに書かれた式の計算について
お世話になります。
学校の課題で下記を出されました。
----------------------------------------
問 次に示す内容のファイルを作成し、
file start-----
1 + 1 =
10 + 20 =
100 + 20 - 50 =
5 * 0 - 3 + 5 =
2 - 3 / 2 + 0.5 =
-----End of File(EOF)
これを読み込み、ファイルに記述された式を演算して、
式と演算結果を画面に表示する実行可能なプログラムを作成せよ。
----------------------------------------
ここまで
「ファイル操作」については、
・fpopenの使い方
・fputs()関数、fgets()関数、fscanf()関数の使い方
・テキストファイルに記された数値を読み込み、最大と最小を求める
ことは学びました。
ただ、ファイルに入っている記号「* / + - =」の扱いがわかりません。
ファイル読み出しのときに配列に入れて、*の記号があったときに
「掛け算」をするのかな??
と思っています。(推測ですが。。)
簡単かと思いますが、教えてください!
よろしくお願いいたします。
学校の課題で下記を出されました。
----------------------------------------
問 次に示す内容のファイルを作成し、
file start-----
1 + 1 =
10 + 20 =
100 + 20 - 50 =
5 * 0 - 3 + 5 =
2 - 3 / 2 + 0.5 =
-----End of File(EOF)
これを読み込み、ファイルに記述された式を演算して、
式と演算結果を画面に表示する実行可能なプログラムを作成せよ。
----------------------------------------
ここまで
「ファイル操作」については、
・fpopenの使い方
・fputs()関数、fgets()関数、fscanf()関数の使い方
・テキストファイルに記された数値を読み込み、最大と最小を求める
ことは学びました。
ただ、ファイルに入っている記号「* / + - =」の扱いがわかりません。
ファイル読み出しのときに配列に入れて、*の記号があったときに
「掛け算」をするのかな??
と思っています。(推測ですが。。)
簡単かと思いますが、教えてください!
よろしくお願いいたします。
Re: ファイルに書かれた式の計算について
普通の優先順位に従った計算ですか?bucchus さんが書きました:ファイルに記述された式を演算して
それとも、100均で売っているような電卓のように単純に前から計算するのですか?
記号以外、すなわち小数点を含む数字の扱いはわかるのですか?bucchus さんが書きました:ただ、ファイルに入っている記号「* / + - =」の扱いがわかりません。
できるところまでコードを書いていただけると答えやすいです。
*の記号を読んだ時点では、掛ける数を読んでいないので「掛け算」はできません。bucchus さんが書きました:ファイル読み出しのときに配列に入れて、*の記号があったときに
「掛け算」をするのかな??
と思っています。(推測ですが。。)
優先順位をきちんと考える場合、読んだ数字や演算子を配列に格納しながら処理するのはいい方針の一つでしょう。
オフトピック
わざわざ実行可能でないプログラムを作る、もしくは課題で作らせることはあるのだろうか?bucchus さんが書きました:式と演算結果を画面に表示する実行可能なプログラムを作成せよ。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: ファイルに書かれた式の計算について
みけCAT様
返信ありがとうございます!
そうですよね。計算の順序は考える要素ですね。
とりあえず、
----------------
1 + 1 =
10 + 20 =
100 + 20 - 50 =
----------------
これだけでもできたらと思っています。
これならば単純に前から計算できそうです。
また、数字もint型で済むのでやりやすそうです。
今、配列に入れた「1 + 1 =」をどうやって計算するのかを考えています。
返信ありがとうございます!
そうですよね。計算の順序は考える要素ですね。
とりあえず、
----------------
1 + 1 =
10 + 20 =
100 + 20 - 50 =
----------------
これだけでもできたらと思っています。
これならば単純に前から計算できそうです。
また、数字もint型で済むのでやりやすそうです。
今、配列に入れた「1 + 1 =」をどうやって計算するのかを考えています。
Re: ファイルに書かれた式の計算について
>今、配列に入れた「1 + 1 =」をどうやって計算するのかを考えています。
人間が計算していく手順と同じことをすればよいのではないでしょうか.
例えば…
2 - 3 / 2 + 0.5 =
↓
2 - 1.5 + 0.5 =
↓
0.5 + 0.5 =
↓
1.0
これの各段階で「何をやっているか」を具体的に言えれば
それが実装すべき処理内容になると思います.
人間が計算していく手順と同じことをすればよいのではないでしょうか.
例えば…
2 - 3 / 2 + 0.5 =
↓
2 - 1.5 + 0.5 =
↓
0.5 + 0.5 =
↓
1.0
これの各段階で「何をやっているか」を具体的に言えれば
それが実装すべき処理内容になると思います.
Re: ファイルに書かれた式の計算について
わざわざそのように式の途中を変換していくのはめんどくさいでしょう。usao さんが書きました:>今、配列に入れた「1 + 1 =」をどうやって計算するのかを考えています。
人間が計算していく手順と同じことをすればよいのではないでしょうか.
優先順位を考えるなら、式を一旦逆ポーランド記法に変換してから計算するといいでしょう。
数式を逆ポーランド法に変換するための事柄
計算してみる
逆ポーランド記法への変換1
逆ポーランド記法への変換2
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: ファイルに書かれた式の計算について
まあRPNを使うことは他の回答者さんの回答のとおりだと思います。
それにしてもむずかしい課題だな…。質問者さんのC言語のレベルがどの程度かはわかりませんが…。
それにしてもむずかしい課題だな…。質問者さんのC言語のレベルがどの程度かはわかりませんが…。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。
プログラムは思ったとおりには動かない。書いたとおりに動く。
Re: ファイルに書かれた式の計算について
逆ポーランド記法を使うと面倒です。
逆ポーランド記法を使わないほうが簡単になると思います
理解できたら、あなた自身の書き方に書き直して、ここに提示してください。
理解できないなら、どこが分からないのかを質問してください。
逆ポーランド記法を使わないほうが簡単になると思います
#include <stdio.h> // fopen, fclose, fgets, fputs, printf, puts, sscanf
#include <ctype.h> // isspace, isdigit
#define MAX 1024
char *p;
unsigned char c;
int get(void)
{
while (isspace(c = *p++)) ;
return c;
}
double factor(void)
{
int n;
double v = 0;
if (isdigit(get())) {
sscanf(--p, "%lf%n", &v, &n);
p += n;
get();
}
else
c = 1;
return v;
}
double term(void)
{
double v = factor();
while (1)
if (c == '*')
v *= factor();
else if (c == '/')
v /= factor();
else return v;
}
double expr(void)
{
double v = term();
while (1)
if (c == '+')
v += term();
else if (c == '-')
v -= term();
else return v;
}
int calc(char *b, double *v)
{
p = b;
*v = expr();
return c == '=';
}
int main(void)
{
char buf[MAX];
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) return 1;
while (fgets(buf, MAX, fp)) {
double v;
fputs(buf, stdout);
if (calc(buf, &v))
printf(" %.15g\n", v);
else
puts(" error");
}
fclose(fp);
return 0;
}
理解できないなら、どこが分からないのかを質問してください。
Re: ファイルに書かれた式の計算について
オフトピック
>わざわざそのように式の途中を変換していくのはめんどくさいでしょう
>優先順位を考えるなら、式を一旦逆ポーランド記法に変換してから計算するといいでしょう。
個人的には,処理すべき数式が特定の5つに限定されているという「ザ・課題」な状況下において
わざわざ
>逆ポーランド記法に変換
をやるとか圧倒的に面倒(というか,おおげさというか)だと思ってしまうのだけど…
>優先順位を考えるなら、式を一旦逆ポーランド記法に変換してから計算するといいでしょう。
個人的には,処理すべき数式が特定の5つに限定されているという「ザ・課題」な状況下において
わざわざ
>逆ポーランド記法に変換
をやるとか圧倒的に面倒(というか,おおげさというか)だと思ってしまうのだけど…
Re: ファイルに書かれた式の計算について
なるほど、確かにそうですね。usao さんが書きました:個人的には,処理すべき数式が特定の5つに限定されているという「ザ・課題」な状況下において
わざわざ
>逆ポーランド記法に変換
をやるとか圧倒的に面倒(というか,おおげさというか)だと思ってしまうのだけど…
課題で作成するべきプログラムの内容は
「指定された内容のファイルを作成し」
「これを読み込み」
「ファイルに記述された式を演算して」
「式と演算結果を画面に表示する」
であり、ファイルシステムで異常が起きなければファイルの内容はわかっているので、わざわざファイルの内容を処理する必要は無いですね。
#include <stdio.h>
void create_file(void) {
/* ファイルを作成する */
}
void read_file(void) {
/* ファイルを読み捨てる */
}
void calc_and_print(void) {
/* 計算と出力をする */
printf("1 + 1 = %d\n", 1 + 1);
/* 他の式も同様 */
}
int main(void) {
create_file();
read_file();
calc_and_print();
return 0;
}
printf()に渡すデータの型と変換指定子の対応を間違えて未定義動作を起こさないように気をつけましょう。
読み捨てるかわりに、ファイルを正しく作れたかベリファイしてもいいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: ファイルに書かれた式の計算について
ファイルの作成をプログラムでする必要はないんじゃないですか?みけCAT さんが書きました: 課題で作成するべきプログラムの内容は
「指定された内容のファイルを作成し」
Re: ファイルに書かれた式の計算について
このプログラムでは、数を表す文字列を数値に変換するのに sscanf をかずま さんが書きました:逆ポーランド記法を使うと面倒です。
逆ポーランド記法を使わないほうが簡単になると思います
使いましたが、<stdlib.h> にある strtod を使うほうがよいでしょう。
また、term と expr は形がよく似ているので、
一つにまとめられないかと考えて、次のように書き換えてみました。
#include <stdio.h> // fopen, fclose, fgets, fputs, printf, puts
#include <stdlib.h> // strtod
#include <ctype.h> // isspace, isdigit
unsigned char c;
char *p;
int get(void) { while (isspace(c = *p++)) ; return c; }
double expr(const char *b)
{
double v;
if (*b)
for (v = expr(b+2); c == b[0] || c == b[1]; )
c == '+' ? v += expr(b+2) :
c == '-' ? v -= expr(b+2) :
c == '*' ? v *= expr(b+2) : (v /= expr(b+2));
else isdigit(get()) ? v = strtod(p-1, &p), get() : (v = c = 1);
return v;
}
int calc(char *s, double *v) { return p = s, *v = expr("+-*/"), c == '='; }
int main(void)
{
double v;
char buf[1024];
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) return 1;
while (fgets(buf, sizeof buf, fp))
fputs(buf, stdout), calc(buf, &v) ? printf(" %.15g\n", v): puts(" error");
fclose(fp);
return 0;
}
説明していただけないでしょうか?
この中に、次のような記述があります。みけCAT さんが書きました: 数式を逆ポーランド法に変換するための事柄
「/は*より優先順位が高い」は本当でしょうか?▼ まず数学のルールとして演算子の優先順位を考えます
+と-は優先順位は同じ
*は+と-より優先順位が高い
/は*より優先順位が高い
この中では、
5 + 4 - 3 を逆ポーランド記法に変換すると、5 4 3 - + なると書かれていますが、
それは間違っています。5 4 + 3 - になります。
5 + (4 - 3) なら、 5 4 3 - + になります。
Re: ファイルに書かれた式の計算について
この課題内容であれば,一見,
以下のような手続きでもやれそうに思ったのだけど,ダメなのかな.
以下のような手続きでもやれそうに思ったのだけど,ダメなのかな.
●初期状態の準備
例えば
2 - 3 / 2 + 0.5 =
なる式を読み込んだら
値と演算子に分けて以下のような配列に格納する
数[] = { 2, 3, 2, 0.5 }
演[] = { -, /, +, = }
●演算処理
配列 演[] の中で一番優先度の高い演算子を探し,そのindexをiとする.
(この例では'/'が最も優先度が高く,i=1.)
↓
演[i]に応じた計算処理を行う.
数[i] = (数[i] と 数[i+1] を用いた計算結果) として,数[i]を計算結果で上書きし,
不要になった数[i+1]と演[i]は配列から取り除く.
数[] = { 2, 1.5, 0.5 }
演[] = { -, +, = }
この手続きを,演算子が'='一つだけになるまで繰り返す→数[0]に最終的な答えが入っている.
この例だと,以下のようになる.
数[] = { 0.5, 0.5 }
演[] = { +, = }
↓
数[] = { 1 }
演[] = { = }
Re: ファイルに書かれた式の計算について
課題の目的だけを達成するなら、
解析処理すら必要ない気がしたので書いてみました。
シェルに丸投げする方法です。
もちろん環境依存です。
Windows10・VS2013 環境で確認しました。
(パフォーマンスチューニングはしていません。)
解析処理すら必要ない気がしたので書いてみました。
シェルに丸投げする方法です。
もちろん環境依存です。
Windows10・VS2013 環境で確認しました。
(パフォーマンスチューニングはしていません。)
#include <iostream> // io
#include <cstdlib> // system
#include <fstream> // ifstream
#include <vector> // vector
#include <string> // string
// テキスト型
typedef std::vector<std::string> TextString_t;
// ファイルからテキスト読み込み
int LoadTextFromFile(const char* pFilePath, TextString_t& refText)
{
std::ifstream ifs(pFilePath);
if (ifs.fail()) {
std::cerr << "読み込み失敗:" << pFilePath << std::endl;
return -1;
}
std::string str;
refText.clear();
while (getline(ifs, str)) {
refText.emplace_back(str);
}
return 0;
}
int main()
{
// 文字取得
TextString_t fileText;
if (LoadTextFromFile("test.txt", fileText) != 0) {
return -1;
}
// = を消去
auto eraceEqualChar = [](std::string& refStr){
for (size_t c = refStr.find_first_of('=');
c != std::string::npos;
c = refStr.find_first_of('=')) {
refStr.erase(c, 1);
}
};
// 一行ずつ処理
for (auto& refStr : fileText){
std::cout << refStr;
eraceEqualChar(refStr);
// PowerShell に丸投げ
// (※この実装だと、計算毎にPowerShellが起動されるから超遅い)
system(("powershell " + refStr).c_str());
}
// 入力待ち
system("pause");
return 0;
}