ゆとりーなの日記

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

Javaだと耐えなそうでもC++/Boostなら耐える

来年のJava普通に耐えなそうだったのでC++/Boostで書いてみました。取り敢えず簡単そうだったyappyの日記に挑戦。日本国憲法の前文を読み、行ごとに単語単位でひっくり返せとか書いてありますが、

This is a pen.
That is a pencil.

.pen a is This
.pensil a is That

が出来れば耐えるだろうということで。以下コード。

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <vector>
#include <boost/tokenizer.hpp>

int main() {
  std::ifstream fin("constitute.txt");
  std::ofstream fout("reverse.txt");
  if (!(fin && fout)) {
    return -1;
  }
  std::string str;
  while (std::getline(fin, str)) {
    const boost::char_separator<char> sep(" ");
    const boost::tokenizer<boost::char_separator<char>> tokens(str, sep);
    const std::vector<std::string> v(tokens.begin(), tokens.end());
    std::for_each(v.rbegin(), v.rend(), [&fout](const std::string &str) {
      const auto length(str.length());
      const auto last_word(str.at(length - 1));
      const std::string out_str(last_word == '.' || last_word == ',' || last_word == ';' ? 
        str.substr(length - 1) + str.substr(0, length - 1) : str);
      fout << out_str << " ";
    });
    fout << std::endl;
  }
  return 0;
}

Boost.tokenizer使いたかっただけ感が出てます。
で、Boost.tokenizerはどうもリバースイテレータが取れなかったりstd::reverceにぶち込めなかったりしたのでstd::vectorにコピーしてリバースイテレータ経由で文字を出すというなんとも言えないコードになりました。記号の部分の処理もyappy_t氏の案を○パクリ(と言ってもC++にはisLetter相当のものがないのでその部分は記号比較してますが。ひょっとして探せばあるのかしら?)だし、やっぱり最後に空白がでます。これでも一応、上の例は上手く行きましたよとだけ言っておきます。
以上。
追記:
Boost.tokenizerよりもBoost::algorithmの方が短く書けるということで、

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <vector>
#include <boost/algorithm/string.hpp>

int main() {
  std::ifstream fin("constitute.txt");
  std::ofstream fout("reverse.txt");
  if (!(fin && fout)) {
    return -1;
  }
  std::string str;
  while (std::getline(fin, str)) {
    std::vector<std::string> v;
    boost::algorithm::split(v, str, boost::algorithm::is_space());
    std::for_each(v.rbegin(), v.rend(), [&fout](const std::string &str) {
      const auto length(str.length());
      const auto last_word(str.at(length - 1));
      const std::string out_str(last_word == '.' || last_word == ',' || last_word == ';' ? 
        str.substr(length - 1) + str.substr(0, length - 1) : str);
      fout << out_str << " ";
    });
    fout << std::endl;
  }
  return 0;
}

こうなりました。やってることは上のと殆ど変わりません。使ってるBoostの中の人が違うだけです。
更に追記:
で、Rubystに喧嘩を売られたので正規表現&&C++0x標準のみで書きました。

#include <regex>
#include <fstream>
#include <string>
#include <algorithm>
#include <vector>

int main() {
  std::ifstream fin("constitute.txt");
  std::ofstream fout("reverse.txt");
  if (!(fin && fout)) {
    return -1;
  }
  std::string str;
  while (std::getline(fin, str)) {
    std::vector<std::smatch> v(std::sregex_iterator(str.begin(), str.end(), std::regex("\\s+|\\w+|\\W")), std::sregex_iterator());
    std::for_each(v.rbegin(), v.rend(), [&fout](const std::smatch &str) {
      fout << str[0];
    });
    fout << std::endl;
  }
  return 0; 
}

なんだかんだでこれが一番短い説があります。唯正規表現はあんまりやったことがなくて正直まだよくわかってないことも多いので勉強が必要そうな空気です。
更に更に追記:
Boost.RangeEx使ったら更に楽しくかけました。

#include <regex>
#include <fstream>
#include <string>
#include <vector>
#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/adaptor/reversed.hpp>

int main() {
  std::ifstream fin("constitute.txt");
  std::ofstream fout("reverse.txt");
  if (!(fin && fout)) {
    return -1;
  }
  std::string str;
  while (std::getline(fin, str)) {
    boost::for_each(std::vector<std::smatch>(std::sregex_iterator(str.begin(), str.end(), std::regex("\\s+|\\w+|\\W")),
      std::sregex_iterator()) | boost::adaptors::reversed, [&fout](const std::smatch &str) {
      fout << str[0];
    });
    fout << std::endl;
  }
  return 0; 
}