掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

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

掲示板2

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

No.23576

関数、変数を入力して結果を出力する方法
投稿者---ken(2005/10/12 17:46:50)


ある関数f(x)を入力して、その後にxの値を入力すると関数の値が出力される。
(例)
入力(関数):2x+3
入力(x):5
出力:13

というようなプログラムを作りたいのですが、何か良い方法はありますか?


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:関数、変数を入力して結果を出力する方法 23577 Blue 2005/10/12 17:57:59


No.23577

Re:関数、変数を入力して結果を出力する方法
投稿者---Blue(2005/10/12 17:57:59)


>ある関数f(x)を入力して、その後にxの値を入力すると関数の値が出力される。
>(例)
>入力(関数):2x+3
>入力(x):5
>出力:13
>
>というようなプログラムを作りたいのですが、何か良い方法はありますか?
まず、kenさんに質問させてください。

・あなたのC言語レベルはどれくらいでしょうか?
・おつかいのOS、コンパイラは何でしょうか?(バージョン&SP)

次に仕様についてですが、
・f(x)は一次方程式(でいいのか?)のみの関数でしょうか?
・演算子はどの程度のものを考えているのでしょうか?
  (四則演算子、sinやcos等)

以上で質問終わりです。

個人的な感想をいうと、かなり難易度は高いです。
文字列(構文)解析に関してスペシャリストでないと厳しいと思います。



この投稿にコメントする

削除パスワード

No.23578

Re:関数、変数を入力して結果を出力する方法
投稿者---ken(2005/10/12 18:22:28)


>まず、kenさんに質問させてください。
>
>・あなたのC言語レベルはどれくらいでしょうか?
>・おつかいのOS、コンパイラは何でしょうか?(バージョン&SP)
>
>次に仕様についてですが、
>・f(x)は一次方程式(でいいのか?)のみの関数でしょうか?
>・演算子はどの程度のものを考えているのでしょうか?
> (四則演算子、sinやcos等)
>
>以上で質問終わりです。
>
>個人的な感想をいうと、かなり難易度は高いです。
>文字列(構文)解析に関してスペシャリストでないと厳しいと思います。

早速の返答ありがとうございます。

私が使っているのはVC++6.0、OS:WinXPSP2です。
Cのレベルは基本的なことくらいしかわかりません。

仕様についてですが、
基本的には指数対数や三角関数は行いませんが、四則演算子は+、−、×、÷全て使いたいです。
ですが、出来れば最終的には2変数2次方程式f(x,y)で行えたら、と思っています。

この問題は必須なものではないので、失礼とは思いますが私のレベル以上のことが必要となったら、あきらめるかもしれません。


この投稿にコメントする

削除パスワード

No.23581

Re:関数、変数を入力して結果を出力する方法
投稿者---Blue(2005/10/12 19:40:35)


四則演算程度ならば、自力で文字列を解析して出来そうですが、
C言語の基礎部分(ポインタ等)が固められていないと厳しいです。

とりあえず、変数(xやy)といったものを抜きで普通に電卓的に演算が行えるようになってから次のステップへ進みましょう。


ちなみに、
YACC(Yet Another Compiler Compiler)という構文解析のためのプログラムを自動生成してくれるツールがあります。
YACC入門
こんなツールが出来るほど、構文解析は複雑で難しいです。
# 私は、さわりしかやったことないのでこれについてはアドバイスできません。
# JavaScriptでeval関数みたいのがあれば強力なんですが。



この投稿にコメントする

削除パスワード

No.23628

Re:関数、変数を入力して結果を出力する方法
投稿者---かずま(2005/10/16 00:34:54)


> YACC(Yet Another Compiler Compiler)という構文解析のためのプログラムを
> 自動生成してくれるツールがあります。

私の Windows 2000 には、cygwin をインストールしていて、yacc 互換の
bison があるので試してみました。
C:\tmp>cat a.y
%{
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#define YYSTYPE double
%}
%right  '^'
%left  '+' '-'
%left  '*' '/'
%left  UNARYMINUS UNARYPLUS
%token  NUM VAR
%%
line: expr { printf("  %.15g\n", $1); }
      ;
expr:  NUM { $$ = $1; }
     | VAR { $$ = $1; }
     | '-' expr %prec UNARYMINUS { $$ = -$2; }
     | '+' expr %prec UNARYPLUS  { $$ = $2; }
     | expr '+' expr { $$ = $1 + $3; }
     | expr '-' expr { $$ = $1 - $3; }
     | expr '*' expr { $$ = $1 * $3; }
     | expr '/' expr { $$ = $1 / $3; }
     | expr '^' expr { $$ = pow($1, $3); }
     | '(' expr ')'  { $$ = $2; }
     ;
%%
double x;  char *p;

int yylex(void)
{
    int c;
    do c = *p++ & 0xff; while (isspace(c));
    if (c == '.' || isdigit(c)) { yylval = strtod(p-1, &p); return NUM; }
    if (c == 'x') { yylval = x; return VAR; }
    return c;
}

int main(void)
{
    char buf[1024];
    while (printf("func: "), p = fgets(buf, sizeof buf, stdin)) {
        printf("x: ");  if (scanf("%lf", &x) != 1) break;
        yyparse();
        fgets(buf, sizeof buf, stdin);
    }
    return 0;
}

C:\tmp>bison a.y

C:\tmp>gcc a.tab.c -ly -lm

C:\tmp>a
func: 2*x+3
x: 5
  13
func: .
x: .

C:\tmp>



この投稿にコメントする

削除パスワード

No.23662

Re:関数、変数を入力して結果を出力する方法
投稿者---かずま(2005/10/17 02:45:37)


> %right  '^'
> %left  '+' '-'
> %left  '*' '/'

これだと、^ の優先順位が + や - より低くなってしまうので、
次のように訂正します。

%left  '+' '-'
%left  '*' '/'
%right  '^'


または、yacc の持っている優先順位の記述方法を使用しないで、
構文規則のほうで対応することもできます。

%token  '0' 'x' '+' '-' '*' '/' '^'
%%
start: expr { printf("  %.15g\n", $1); }
     ;
expr : term { $$ = $1; }
     | expr '+' term { $$ = $1 + $3; }
     | expr '-' term { $$ = $1 - $3; }
     ;
term : fact { $$ = $1; }
     | term '*' fact { $$ = $1 * $3; }
     | term '/' fact { $$ = $1 / $3; }
     ;
fact : prim { $$ = $1; }
     | prim '^' fact { $$ = pow($1, $3); }
     ;
prim : '0' { $$ = $1; }
     | 'x' { $$ = $1; }
     | '(' expr ')'  { $$ = $2; }
     | '+' prim { $$ = $2; }
     | '-' prim { $$ = -$2; }
     ;
%%

なお、yylex() は、NUM や VAR の代わりに '0' や 'x' を返すものとします。



この投稿にコメントする

削除パスワード

No.23582

Re:関数、変数を入力して結果を出力する方法
投稿者---こん!(2005/10/12 20:05:39)


過去ログから、そのものずばりではありませんが
No695 電卓
このあたりは参考になりますでしょうか。


他にも探すとたくさん出てきますが。

過去ログ


この投稿にコメントする

削除パスワード

No.23586

Re:関数、変数を入力して結果を出力する方法
投稿者---かずま(2005/10/12 23:33:23)


> 過去ログから、そのものずばりではありませんが
> No695 電卓
> このあたりは参考になりますでしょうか。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

int c;  char *p, o[] = "+-*/^ ";  double x;

int get(void) { do c = *p++ & 0xff; while (isspace(c)); return c; }

double expr(const char *s)
{
    double v;
    if (*s)
        for (v = expr(s+2); c == s[0] || c == s[1]; )
            switch (c) {
            case '+': v += expr(s+2); break;
            case '-': v -= expr(s+2); break;
            case '*': v *= expr(s+2); break;
            case '/': v /= expr(s+2); break;
            case '^': v = pow(v, expr(s)); break;
            }
    else if (get() == '.' || isdigit(c)) v = strtod(p-1, &p), get();
    else if (c == '(') v = expr(o), c == ')' ? get() : (c = 1);
    else if (c == '-') v = -expr(s);
    else if (c == '+') v = expr(s);
    else if (c == 'x') v = x, get();
    else v = c = 1;  /* error */
    return v;
}

int main(void)
{
    char buf[1024];  double v;

    while (printf("func: "), p = fgets(buf, sizeof buf, stdin)) {
        printf("x: ");
        if (scanf("%lf", &x) != 1) break;
        v = expr(o);
        printf(c ? "  error\n" : "  %.15g\n", v);
        fgets(buf, sizeof buf, stdin);
    }
    return 0;
}



この投稿にコメントする

削除パスワード

No.23592

Re:関数、変数を入力して結果を出力する方法
投稿者---ken(2005/10/13 14:41:04)


皆さんいろいろとありがとうございます。
Blueさんの言う通りJavaScriptのeval関数みたいなものがCにもあれば最適なのですが。

かずまさんの<A HREF="http://f4.aaa.livedoor.jp/~pointc/log695.html/">No695 電卓</A>のソースを参考にさせてもらいます。
これで私の中での第1段階ができそうです。
少々考えてみたいと思います。


この投稿にコメントする

削除パスワード

No.23682

Re:関数、変数を入力して結果を出力する方法
投稿者---かずま(2005/10/18 10:49:35)


> これで私の中での第1段階ができそうです。

第2段階はできたのでしょうか?
変数を増やすのは簡単ですが、その値を指定するのが面倒になりそうなので、
代入演算子を導入してみてはどうかと考えて、ちょっと書いてみました。

・変数は a〜z の 26個。
・「変数 = 式」の入力で変数に値を設定
・「式」の入力でその式の値を表示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

int c;  char *p, o[] = "= +-*/^ ";  double x[26];

int get(void) { do c = *p++ & 0xff; while (isspace(c)); return c; }

double expr(const char *s)
{
    double v;

    if (*s == '=') {
        char *q = p;  int n = get();
        v = (islower(n) && get() == '=') ? (x[n-'a'] = expr(s)) : (p = q, expr(s+2));
    } else if (*s)
        for (v = expr(s+2); c == s[0] || c == s[1]; )
            switch (c) {
            case '+': v += expr(s+2); break;
            case '-': v -= expr(s+2); break;
            case '*': v *= expr(s+2); break;
            case '/': v /= expr(s+2); break;
            case '^': v = pow(v, expr(s)); break;
            }
    else if (get() == '.' || isdigit(c)) v = strtod(p-1, &p), get();
    else if (c == '(') v = expr(o), c == ')' ? get() : (c = 1);
    else if (c == '-') v = -expr(s);
    else if (c == '+') v = expr(s);
    else if (!memcmp(p-1, "sqrt",4)) p += 3, v = sqrt(expr(s));
    else if (!memcmp(p-1, "exp", 3)) p += 2, v = exp(expr(s));
    else if (!memcmp(p-1, "log", 3)) p += 2, v = log(expr(s));
    else if (!memcmp(p-1, "sin", 3)) p += 2, v = sin(expr(s));
    else if (!memcmp(p-1, "cos", 3)) p += 2, v = cos(expr(s));
    else if (!memcmp(p-1, "tan", 3)) p += 2, v = tan(expr(s));
    else if (!memcmp(p-1, "atan",4)) p += 3, v = atan(expr(s));
    else if (islower(c)) v = x[c-'a'], get();
    else v = c = 1;  /* error */
    return v;
}

int main(void)
{
    char buf[1024];  double v;

    while (printf("> "), fgets(buf, sizeof buf, stdin) && *buf != '.') {
        p = buf;  v = expr(o);
        if (c) puts("    error");
        else {
            p = buf;
            if (!islower(get()) || get() != '=') printf("    %.15g\n", v);
        }
    }
    return 0;
}

-----------------------------
実行例
> x = 5
> 2*x + 3
    13
> y = 12
> sqrt(x^2 + y^2)
    13
> exp(1)
    2.71828182845905
> 4 * atan(1)
    3.14159265358979
> .



この投稿にコメントする

削除パスワード

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