ひがきの日記

2016-02-07

C++ で Ruby の inject みたいなやつを作ってみる

仕事で C++ を使うことになったので、リハビリを兼て Ruby の inject みたいな (lisp の apply みたいな) 関数を書いてみた。

#include <iostream>
#include <vector>
#include <functional>

using namespace std;

#define ARRSIZ(a) (sizeof(a)/sizeof((a)[0]))
#define ARREND(a) ((a)+ARRSIZ(a))

template <class T, class R, class F>
R inject(T begin, T end, R result, F func) {
    for (; begin != end; ++begin)
	result = func(result, *begin);
    return result;
}

template <class T>
struct PLUS {
    T operator()(const T& a, const T& b) const {
        return a + b;
    }
};

int main() {
    int src[] = {1, 2, 3, 4, 5};
    vector<int> v;
    vector<double> w;

    for (int* i = src; i != ARREND(src); ++i) {
	v.push_back(*i);
	w.push_back(*i * 1.1);
    }

    cout << inject(src, ARREND(src),   0,   plus<int>())    << endl;
    cout << inject(v.begin(), v.end(), 0,   plus<int>())    << endl;
    cout << inject(w.begin(), w.end(), 0.0, plus<double>()) << endl;
    cout << inject(v.begin(), v.end(), 0,   PLUS<int>())    << endl;
    cout << inject(w.begin(), w.end(), 0.0, PLUS<double>()) << endl;

    return 0;
}

inject の受け取る、型 F がなんかよく分からん。

template 使うと、関数オブジェクト関数内でちょいちょいっと書けないのかね。

    struct _plus_ {
	int operator()(const int& a, const int& b) const {
	    return a + b;
	}
    };
    cout << inject(v.begin(), v.end(), 0, _plus_()) << endl;

上のように main() の中で struct を書くと、

warning: template argument uses local type '_plus_'

こんな感じで怒られる。めんどくさい。

今時ならラムダ式を使うといいんだろうか?

そもそも inject に相当する関数はどこかに定義されてるんじゃないのか?

2009-07-18

るりまを使おう

RubyKaigi2009 で「るりまを gemインストールできたら便利じゃね?」という意見が出ていたけど、るりまのインストールってそんなに面倒か?

スナップショットダウンロード

これを適当なディレクトリに展開する。

ruby-refm-1.9.1-dynamic-日付 というディレクトリができるので、ruby-refm-1.9.1-dynamic という名前 (日付部分を取り除く) でシンボリックリンクしておくと便利。

refe コマンドの作成。

例えばこんな感じ。

#! /bin/bash

cmd="ruby -Eeuc-jp -C 展開したディレクトリ/ruby-refm-1.9.1-dynamic -I bitclust/lib bitclust/bin/refe.rb -d db-1_9_1"

if [ -t 1 ]; then
  $cmd "$@" | ${PAGER:-more}
else
  $cmd "$@"
fi

あとは使うだけ

$ refe File
class File < IO

ファイルアクセスのためのクラスです。

通常 [[m:Kernel.#open]] または [[m:File.open]] を使って生成します。
[[c:IO]] クラスがインクルードしている [[c:File::Constants]] は File クラスに関係する定数を
格納したモジュールです。
また [[c:File::Stat]] は stat 構造体( [[man:stat(2)]] 参照)を表すクラスです。

とってもカンタンだ。

emacs からも使える。

2009-06-18

オブジェクトの順序

オレオレクラスのオブジェクトを STL のコンテナに詰めたい。

vector になら詰められるけど、set に詰めようと思ったら、オブジェクトの順序を決めないといけない。

operator< さえあればオブジェクトの順序は決まるみたい。

class Foo
{
        ...
public:
	bool operator<(const Foo& rhs) const { return id < rhs.id; }
    ...

これで set に詰めても怒られない。

#include <set>
    ...
    set<Foo> foo;
    foo.insert(Foo(1));
    foo.insert(Foo(2));
    ...

operator< は、カプセル化を崩さないなら、メンバ関数にしない方がいいらしい。

class Foo
{
        ...
public:
	int id() const;  // 判断材料が公開されているなら ...
    ...
};

bool operator<(const Foo& lhs, const Foo& rhs)
{
	return lhs.id() < rhs.id();
}

こうすると < の右辺でも左辺でも暗黙の型変換をしてくれる。*1

<=, >=, > は < から自動生成するらしい。

順序とは関係ないけど、!= は == から自動生成するらしい。

#include <utility>
    ...
using namespace std::rel_ops;
    ...
    Foo f1(1);
    Foo f2(2);
    if (f1 >= f2)
        ...

*1:コンストラクタが explicit でなければ

2009-06-13

オレオレクラスを ostream に出力する

C++ プログラミング入門 によると、print() というメンバ関数を作って、operator<< から呼び出すらしい。

class Foo
{
    ...
public:
    void print(ostream* out) const { *out << value; }
    ...
};

ostream& operator<<(ostream& out, const Foo& foo);

なんで print なのか?
なんで ostream& じゃなくて ostream* なのか?

operator<< の実体は、print() を呼び出すだけの簡単なお仕事です。

ostream& operator<<(ostream& out, const Foo& foo)
{
    foo.print(&out);
    return out;
}

これで cout << foo << endl; とかできるようになる。


クラスの雛形

class Foo
{
    // constructor, destructor and ...
public:
    Foo(int val = 0): value(val) {}
    ~Foo() {}
    Foo(const Foo& src);
    Foo& operator=(const Foo& src);
    // accessor
    int getValue() const { return value; }
    void setValue(int val) { value = val; }

    // member function

    // data member
private:
    int value;
};

入門書を見ると必ず

  1. メンバ関数
  2. データメンバ

の順に宣言されるけど、逆じゃダメなのかな?

2009-06-11

インデントと TAB の宗教論争

職場の大先輩が「インデントに TAB とスペースが混ったコードは困る」とボヤいておられた。

Emacs 使いの私からすると、何が問題なのか理解できない。

大先輩:「そういうコードは (俺のエディタで開くと) インデントがグチャグチャになる。」
ひがき:「TAB 文字の表示幅を 8 以外にするからですよ。」

この後、激しくお説教された。*1

大先輩がおっしゃるには、

  • インデントには TAB 以外使わない。
  • インデント以外に TAB は使わない。

という制約の下でコーディングしなければならないらしい。

そうすることで TAB 文字の表示幅を自分の好きなように変更してもインデントが崩れないんだとか。*2

常々思うことは、

  • そこでコードをインデントしたいという要求*3と、
  • TAB キーを押すという行為と、
  • ファイルのその位置に TAB 文字を書き込むという動作*4は、

それぞれ別の概念なんじゃないかと。*5

それから、

  • インデント幅を何文字分にするかという設定*6と、
  • TAB 文字の表示幅を何文字分にするかという設定*7も、

それぞれ別の概念なんじゃないかと。

Emacs という名のガラパゴスにいるから、こう思うんだろうか。*8


宗教論争はスルーするとして、ひとつ気になることがある。

昔 Sun が公開していた Java のコード規約には、

Four spaces should be used as the unit of indentation. The exact construction of the indentation (spaces vs. tabs) is unspecified. Tabs must be set exactly every 8 spaces (not 4).

とあり、

  • インデント幅は 4 文字分。
  • TAB の表示幅は 8 文字分。
  • インデントに TAB を使ってもスペースを使ってもよい。

と言ってるんだよね。

明らかにデファクトスタンダードとは違うわけだが、みんなどうしてるんだろうか?

  1. これを守るために苦労している。
  2. Sun の文書なんか無視。
  3. 参考にしてる文書が古過ぎる。
  4. 俺の使ってる IDE ならふつうにそうなってるよ。
  5. その他。

たぶん 4 なのかな?
すごく気になる。

*1:いやいや、先輩と一緒に仕事するときは、ちゃんと tabify してますよ。

*2:だから何でいちいち TAB 文字の表示幅を変更する必要があるんだろうか?

*3Emacs では、たとえば c-indent-command

*4Emacs では、C-q TAB

*5:バックスペースキーを押して「ファイルに 0x08 が書き込まれていない」と言って怒る人は、あまり見かけない。TAB キーだって同じだと思うんだが。

*6Emacs では、たとえば c-basic-offset

*7Emacs では、tab-width

*8:きっと vim script とかでも同じことができるんじゃないのかなぁ。