ゆとりーなの日記

日記的な事を書いて行くと思はれる

Dメーカーにやられた可能性は大いにある

本日某ラボにてC言語の試験が行われました。結果はまぁ残念なことになってしまったわけですので、今日はここで反省会です。

端末の挙動に慣れてないとこうなる

問の雰囲気:標準入力から一文字ずつ読んで連続したスペース、タブを一つのスペースに置き換えて標準出力に出力する。
getcharの挙動にテンパった訳です。要は、

int main(void) {
  int str;
  while ((str = getchar()) != EOF) {
    putchar(str);
  }
  return 0;
}

ってのがあった時に、ちゃんと一行読み取ってから出力されるという挙動がどうも直観的じゃなかったんですね。日ごろからstd::getline系統に親しんでいた影響ですね。
この問題に関して言えば、std::getlineで文字取ってから置換ってのをやると文字列の操作が無駄るのでC++で書くにしてもstd::getcharが正解なんだと思います。

#include <iostream>

int main() {
  int str;
  while ((str = std::getchar()) != EOF) {
    if (str == ' ' || str == '\t') {
      for (str = std::getchar(); ; str = std::getchar()) {
        if (str != ' ' && str != '\t') {
          std::putchar(' ');
          std::putchar(str);
          break;
        }
      }
    } else {
      std::putchar(str);
    }
  }
  return 0;
}

取り敢えずこんな感じですかね。書いてて気付きましたが提出したコードにバグがありました。単位落とす率が・・・。

RAIIがないと萎えるんです

問の雰囲気:コマンドライン引数のファイルの内容を順番に結合して最後に指定したファイルにコピーする。引数なしは標準入力から入力して標準出力に出力、一つなら標準入力から入力してファイルに出力する。
fcloseが面倒なんですよ。最初ファイルポインタの配列とか思ったのがよくなかったんですね。ポインタ一個で回していけば別にfcloseで面倒だとか思いませんね。
まぁでもC++だとやっぱりfstream系列ですかね。しかし相変わらず標準入力から入力して標準出力に出力ってのがどういう状況なのかが窓プラーにはイマイチよく分かりません。

#include <algorithm>
#include <fstream>
#include <iostream>
#include <stdexcept>

int main(int argc, char **argv) {
  try {
    if (argc == 1) {
      int str;
      while ((str = std::getchar()) != EOF) {
        std::putchar(str);
      }
    } else if (argc == 2) {
      int str;
      std::ofstream fout(argv[1]);
      if (!fout) {
        throw std::runtime_error("file open error");
      }
      while ((str = std::getchar()) != EOF) {
        fout << static_cast<char>(str);
      }
    } else if (argc) {
      std::ofstream fout(argv[argc - 1]);
      if (!fout) {
        throw std::runtime_error("file open error");
      }
      for (int i = 1; i < argc - 1; ++i) {
        std::ifstream fin(argv[1]);
        if (!fin) {
          throw std::runtime_error("file open error");
        }
        std::copy(std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(fout));
      }
    }
  } catch (const std::exception &error) {
    std::cerr << error.what() << std::endl;
    return 1;
  }
  return 0;
}

これで大丈夫ですかね。試験では当然爆死してます。

マクロとか書かないんです

問の雰囲気:文字列を逆順に並べ変えるマクロを作る。
プリプロセッサはコード生成の為にあるのであって、マクロの為にあるのではないと言ってみる試験です。そもそもマクロじゃ引数とかに使えませんし。まぁassertの様なマクロじゃなきゃ駄目なのもありますのでね。
問題の仕様を読み間違えて悲劇が起きました。テストはコマンドライン引数を取るんだそうです。最初そう書いていたのに出力例に釣られて問題文を読み違えました。これも今から見ると端末に慣れていれば釣られなそうなものだったのがなんとも・・・。まぁマクロ自体は耐えてると思うのですが・・・。

#include <cstring>
#include <algorithm>
#define strrev(s)\
  do {\
    std::reverse(s, s + std::strlen(s));\
  } while (0)

C++で無理矢理書くとこうなるんですかね。
あと二問ほどありましたがなんか面倒になってきたのでまたの機会という事で。