C言語なら、俺に聞け! <20>

このエントリーをはてなブックマークに追加
じゃあ入門卒業試験の課題としてこれはどうだ。

int match(const char *pattern, const char *string);

patternがstringとマッチするなら0で無い値、マッチしないなら0を返す。

pattern中の文字'?'は任意の1文字とマッチする。
'*'は0文字以上の任意の文字列とマッチする。
それ以外の文字は、その文字自身とマッチする。
トークンきりだし、構文解析、構文チェックまではCで書けるとして、
そのあとにネイティブコードを作るときはやっぱりアセンブラだよね。
Cでもできるのかな。
>>624
ネイティブコードを生成すること自体はCでも書けるだろ。
知識は必要だろうけど。
626デフォルトの名無しさん:02/05/20 22:36
>>623
patternの中に*や?がひとつも含まれていなかったら、strcmp(string,pattern)と等価?それともstrstr(string,pattern)と等価?
>>624
アセンブリ言語コードを出力すればいいんじゃないの?
そういうコンパイラも多い。
>>627
gccとか?
>>626
strcmpと等価ということで。
>>628
そう。UnixのたいがいのCコンパイラと
8bitのころのPCのたいがいのCコンパイラ。
DOSだとLSI-C86もそうじゃなかったっけ。
631デフォルトの名無しさん:02/05/20 22:39
>>629
ちょっとやってみよう。
再帰使うとうまくゆくかな・・・
632631:02/05/20 22:41
いや再帰じゃないな・・・
>>632
出題者は再帰を使う回答を用意しますた。
>>623
じゃあ補足すると、これぐらいは全部通ればとりあえず合格?
あんまり書くと長くなりすぎるから省略。

assert( match("abc", "abc") != 0 );
assert( match("abc", "abcd") == 0 );

assert( match("?bc", "abc") != 0 );
assert( match("a?c", "abc") != 0 );
assert( match("ab?", "abc") != 0 );
assert( match("ab?", "ab") == 0 );

assert( match("ab*", "abc") != 0 );
assert( match("ab*", "ab") != 0 );
assert( match("a*c", "abbbbbc") != 0 );
assert( match("a*c", "abc") != 0 );
assert( match("a*c", "ac") != 0 );

// 最長一致なら以下のチェック.
assert( match("a*c", "acc") != 0 );
assert( match("a*d*g", "adgabcdefg") != 0 );
テストドライバその1

#include <stdio.h>

int main(int argc, char **argv)
{
 if (argc != 3) {
  fprintf(stderr, "usage: %s pattern string\n", argv[0]);
  return 1;
 }
 printf("%smatched\n", match(argv[1], argv[2]) ? "" : "un");
 return 0;
}

テストドライバその2
#include <stdio.h>
#include <string.h>

char line[1024];

int main(int argc, char **argv)
{
 if (argc != 2) {
  fprintf(stderr, "usage: %s pattern\n", argv[0]);
  return 1;
 }
 while (fgets(line, sizeof(line), stdin) != NULL) {
  char *eol = strchr(line, '\n');
  if (eol != NULL) {
   *eol = '\0';
  }
  if (match(argv[1], line)) {
   puts(line);
  }
 }
 return 0;
}
assert並べるほうがいいな。
同じテストを何度でも繰り返せ、実行一発で結果がわかる。
637623:02/05/20 22:57
>>634
あ、そうですね。そのテストのほうが良いかも。

ちなみに>>634で用意したテストプログラムは、

その1: patternとstringをコマンドライン引数で与えて
matchedまたはunmatchedをプリントする。

その2: patternをコマンドライン引数で与えて
標準入力から1行ずつ読み込み、マッチする行だけ出力する。

いずれもpatternはちゃんとクオートしないと切ねぇことになる。
638617=623:02/05/20 22:59
テストドライバその2の方は、
むか〜しワタスィが書いた超簡易grepモドキそのものです。
639600:02/05/20 23:02
サパーリ出来ません。
逝ってきます…
640631:02/05/20 23:03
末尾に*がくると失敗するなぁ・・・・
やりなおそう。
641631:02/05/20 23:03
あ、ちなみに私はあの入門書を読み終えたばかりの人じゃないです。
642634:02/05/20 23:04
>>634
いかん、このassertだと * があったら無条件で1返せば通ってしまう(笑)
ので追加。
assert( match("a*c", "ab") == 0 );

あと複合系(もっと要る気が)
assert( match("a**?*", "ab") != 0 );
assert( match("a**?*", "a") == 0 );
643631:02/05/20 23:05
と思ったけどできた。
644デフォルトの名無しさん:02/05/20 23:15
あるモジュールAが別のモジュールBをよんで、さらにモジュールB
が別のモジュールCを呼んで...という具合にモジュールAを頂点とし
て呼ばれているモジュールすべてを洗い出すにはどうやって調べて
いけばよいのでしょうか?モジュールA、B、Cなどすべてのモジュ
ールのソースはあります。どなたか効率よく調べる方法がありましたら、
教えてくださいませんか。
645631:02/05/20 23:18
末尾が?で終わるときに失敗するんですが、いいですか?
だめですよね?(^_^;)
646仕様書無しさん:02/05/20 23:19
M-.
>>644
このへんはどう? http://ctool.sourceforge.net/
他にもいろいろあるはず。
>>644
クロスリファレンスや呼び出しツリーを作ってくれるツールを使えば?
644
もれはソースブラウザ(M$)でしらみつぶしかな。
650623:02/05/20 23:25
あんまり引っ張っても何なので…
出題者が用意した回答です。
あんまり叩かないで(w

int match(const char *pattern, const char *string)
{
 while (*pattern) {
  switch (*pattern) {
  case '?':
   if (! *string) {
    return 0;
   }
   pattern++;
   string++;
   break;
  case '*':
   pattern++;
   do {
    if (match(pattern, string)) {
     return 1;
    }
   } while (*string++);
   return 0;
  default:
   if (*pattern++ != *string++) {
    return 0;
   }
  }
 }
 return ! *string;
}
651631:02/05/20 23:29
ギブアップ 末尾に?があると失敗します。

int match(const char *pattern,const char *str)
{
for(;*str||*pattern;str++,pattern++){
if(*pattern=='?'){
str++,pattern++;
continue;
}
if(*pattern=='*'){
while(*pattern=='?'||*pattern=='*')
pattern++;
if(*pattern=='\0'){
return 1;
}
while(*str!=*pattern){
str++;
}
}
if(*str!=*pattern)
return 0;
}

return 1;
}
652651:02/05/20 23:39
Visual C++の方に持っていって、デバッガにかけてみよう。
でないとどこが間違っているのかわからない(T_T)
653 :02/05/20 23:42
ついでに正規表現版まで作ってくれ!!
>>653
どの正規表現?
.と*だけで良いなら何とか…

(正規表現の文法自体は正規文法でないという罠)
655644:02/05/20 23:55
>>647,648,649
ありがとうございます。ckrefを使用してみます。
656651:02/05/20 23:55
for(;*str||*pattern;str++,pattern++){
の部分を
for(;;str++,pattern++){
if(*str||*pattern){
break;
}
に変えたら成功した・・・・
もしかしたらforについて勘違いしている部分があるかも(汗
657 :02/05/20 23:56
とりあえずperlでつかえるくらい。。ナンチッテ

グループとかは無理ですか?
658651:02/05/20 23:57
あ!!
もしかしたらcontinueした時はforの条件式が評価されないんですか?
>>657
Perlの正規表現だと、それこそちょっとしたコンパイラ書くくらいの
手間がかかると思われ。

グループって[a-z]みたいなやつ?
暇潰しの課題にはちょっと面倒すぎるなあ。
660651:02/05/21 00:06
やっと出来ました・・・・
forについての間違った認識を正すことが出来てよかったです。
ありがとうございました。

int match(const char *pattern,const char *str)
{
for(;*str||*pattern;str++,pattern++){
if(*pattern=='?')
goto loopend;
if(*pattern=='*'){
while(*pattern=='?'||*pattern=='*')
pattern++;
if(*pattern=='\0')
return 1;
while(*str!=*pattern)
str++;
}
if(*str!=*pattern)
return 0;
loopend: ;
}

return 1;
}
661 :02/05/21 00:06
orとかは?

U(MA|FO)

みたいに
>>656
for(;*str||*pattern;str++,pattern++){
は条件式が真ならループは続いて、
for(;;str++,pattern++){
if(*str||*pattern){
break;
}
はifの条件式が真ならループを抜ける。
前者と後者じゃまったく逆の処理じゃないの?
>>662
すみません。
if!(*str||*pattern)) break;の間違いです。
664663:02/05/21 00:09
じゃなくて、
if(!(*str||*pattern)) break;
665デフォルトの名無しさん:02/05/21 00:35
あのなぁ。
if (*str) って書くなら、素直に if (*str != '\0') って書け。
省略すればいいって訳じゃないぜ。
if (isalpha(XXX)) とか、if (found) はこう書くべきだがな。
>>665
同意
strtokを書いたことがある方に聞きます
staticな局所変数を使いました?
>>660
>goto loopend;
continue;じゃいかんの?

Cの文法に躍らされてる気がする。
そんなにまちがえるなら素直に状態遷移書けばいいのに