Tag Archive for 'book'

夕立の月曜日

会社が早く終わったので、家でじっくり勉強しようと思いました。

しばらく遠ざかっていた、ふつける を読み返します。例題と、練習問題をちょこちょここなすつもり。

しかし、しばらくやっているうちに眠くなってきてしまいます。暑いし。ベッドにごろんとして、ピクニックバイブルなんて雑誌を読みながらうとうとと。

雷の音で目を覚まして、おおいそぎで窓を閉めたりします。すごい勢いで窓を打ちつける雨粒。夕立ってこわい。

それでまた起き出して、演習問題をつらつらと。また眠くなってきます。外には夕立の後の夏の青空が。

なんかのんびりしてていい。自分が回復されていくようです。

新刊ラジオ

ちょっと前から利用しているPodcastingの一つが、新刊.jp/新刊ラジオ。 おすすめ本の情報を毎週耳から仕入れることができる。

通勤中、電車の中だと座っていられるので、本を読んだりいろいろできるのだけど、駅から会社まで歩いている間はぼんやり考えごとをするか音楽を聴くかくらいしかできなかったので、聞くだけで情報を得られるのはなかなか便利。


新刊ラジオに出てきたもので、これまでに買ったものは1冊。持っていたものは3冊。 なかなかいい線を突いてくると思う。

買った

持ってた

千円札は拾うな。
千円札は拾うな。
posted with amazlet on 06.05.23
安田 佳生
サンマーク出版 (2006/01/20)
ザ・サーチ グーグルが世界を変えた
ジョン・バッテル 中谷 和男
日経BP社 (2005/11/17)
スティーブ・ジョブズ-偶像復活
ジェフリー・S・ヤング ウィリアム・L・サイモン 井口 耕二
東洋経済新報社 (2005/11/05)
おすすめ度の平均: 5
5 勉強になります。

極私的読書ポータル

やたらと本を読む。

本屋に行くと気に入ったものが目に入り次第買ってしまう。そのおかげで未読スタックが高くたかくなってしまう。積ん読だらけ。

あと、読んだ本は読み返してまとめをつくり (MindMap!)、記憶に定着させたい。そしたら再読キューもたまっていってしまう。

あと、AmazonのWishListを使って買う予定リストもつくっているのだけど、AWSから使えないとかいろいろ不便が多い。

このへんをどうにかしたいと思っていた。半日くらいかけてRailsアプリを書いてみた。

bookStack proto

Amazonでみている商品ページからBookmarkletで一発登録ができたりする。本棚.orgにいくつかの種類のリストを足したようなものにしたい。

あとは、リストを晒すためのJavascriptでも書けば当初の目標は達成かな。しかしRailsの生産性すご。

Google Book Searchが実現しなくても

Google Book Searchや、Amazon なか見検索は、出版社の抵抗が強いらしくなかなか全開バリバリというわけにはいかないらしい。

そこで考えてみた。

たくさんのひとが、blogである本について書くとしよう。一般的なblogでは、気になったところを引用しつつ、そこについての自分の考えを書くというのスタイルになる。

ならば、その引用部分だけを抽出してつないでいくことができれば、一冊の本が自動的に再構築できるのではないか?

方法

  1. 本のタイトル(A)、著者名(B)、書き出し(C)をキーワードにして検索
  2. それらしいものが見つからなければ(C)をひとつ先の文章にして1. に戻る
  3. <blockquote>...</blockquote>の部分を抜き出す
  4. 抜き出した文章の最後の文節でキーワード(C)を置換
  5. 1.に戻る

ためしに、読み終わったばかりのウェブ進化論 本当の大変化はこれから始まるでできるかやってみた。

書き出し(C)は

情報技術(IT)が社会に及ぼす…

なので、

  1. Google:ウェブ進化論 + 梅田望夫 + 情報技術(IT)が社会に及ぼす
  2. 見つからないので1. に戻る
  3. しばらくやってるうちにweb kikakuがみつかる。ここには、ほぼ本文そのままがある。
  4. 3.で見つかったページの一番最後の文章である いずれ振り返ることになるだろう を(C)にして1. に戻る

なんてことをやってみたけれど、やはりそう簡単にはいかない。序章すら再構築できなかった。

まとめ

とはいえ、この本はいくらベストセラーといってもごく最近出たものなので、まだあまりwebに引用が載っていないということを差し引かないといけないだろう。 多くの人が引用するような興味深い古典だと、実現可能かもしれない。


More Effective C++::Item 5. Be wary of use-defined conversion functions

暗黙的な型変換に注意しようという話。

暗黙的な型変換には、3とおりの方法がある。

  1. 引数を一つだけ取るコンストラクタ、あるいは 引数に対してデフォルトの値を設定しているコンストラクタ
  2. operator sometype() const みたいな型変換演算子

しかし、暗黙的な型変換は予期せぬ振る舞いに発展する可能性があるので、できれば避けたほうがよい。あなたの意図したものと、コンパイラが解釈した結果は異なる場合があるからだ。

型変換演算子

たとえば、ペットショップの犬を表すクラスを考える。 それぞれの犬にはIDが振られている。

class Dog {
   public:
      void bark();
      operator int() const { return id_; }
   private:
      int id_;
      char *name_;
};

いま、犬の名前を表示しようとして、うっかりDogクラスには operator char*()がないことを忘れているとすると、

Dog pochi;
std::cout << pochi << std::endl;

なんてやってしまう。結果は、pochiのIDが表示されるというもの。

経験を積んだC++プログラマは、たいてい型変換演算子を避ける。 たとえば、std::stringにはoperator char*()の代わりに c_str()が存在する。

引数を一つだけ取るコンストラクタ

こいつはよりたちがわるい。

ふつうにクラスを設計していても、こうしたコンストラクタを記述することはままある。そして、こうしたコンストラクタも、暗黙的な型変換に一役買ってしまうことがあり得る。

class Array {
   public:
      Array(int n); // n個の要素で初期化するコンストラクタ
};


Array a(10);
Array b(10);

for (int i=0; i<10; i++) {
   if (a == b[i]) {        // ここでミスしている !
      ...
   }
}

なんて風に、a[i] == b[i]と書くべきだったところを書き間違えても、コンパイラは文脈から if (a == static_cast(b[i])) といったように解釈してしまう。意図が正確に反映されていないばかりか、見つけにくいバグになってしまっている。

対策

explicitキーワードをコンストラクタにつける。このキーワードがついたコンストラクタは、暗黙的な型変換には使えなくなる。

もう一つ方法はあるけど、それはトリッキーになるので略 (ヒント: proxy class (see Item 30))。


所感

暗黙的な型変換は、こちらがコーディングをミスったときに、コンパイラがプログラムをコンパイルできるように、都合良く解釈してしまうため、どこで間違いを犯したか分かりづらいことが問題。

既存の大規模プログラムに手を入れたり、バグを発見するような場合を考えると、こうした言語仕様は厄介だ。

自分の意図と異なる振る舞いをプログラムが見せることほど腹立たしいものはない。コードの字面の下で何が行われているかを、正確に把握していないと正しいコードが書けないような言語は人に優しくない。これで大規模なプロジェクトをまともに作ろうという方が無理だと思う。

自分の意図を自然に表現でき、そして書いたとおりのことがストレートにコンピュータに伝わり、実行されることが重要だろうと思う。

More Effective C++::Item 4. Avoid gratuitous default constructors

デフォルトコンストラクタは大きなお世話な場合があるので、コンパイラに生成させないようにしようという話。

オブジェクトを生成する際に、なんらかの情報が必至であるようなクラスもある。この場合、デフォルトコンストラクタの存在が邪魔だったりする。しかし、デフォルトコンストラクタがないと面倒なことになる場合がある。

たとえば、

class Hoge {
   public:
      Hoge(int x) : x_(x) {}
   private:
      int x_;
};

なんてクラスを考える。

問題1. オブジェクトの配列をつくる場合

オブジェクトの配列を定義する際に、引数を与えることはできない。

Hoge hoges[3]; // NG
Hoge *hoges = new Hoge[3]; // NG

ではどうするか。

定義時に初期化

Hoge hoges[3] = { // OK
   Hoge(0),
   Hoge(10),
   Hoge(20)
};

しかし、この方法も完全ではない。

Hoge *hoges = new Hoge[3];

の場合を扱えないから。

ポインタの配列

(Hoge *hoges)[10];  // OK
(Hoge *)*hoges = new (Hoge *)[10]

この方法にも問題がある。

  1. 配列の要素すべてにdeleteをかけないといけない
  2. ポインタのぶんだけ余計にメモリを消費する

placement new

2つ目の問題は、placement newを導入すれば解決できる (see Item 8)。しかし、placement newを用いるにも問題がある。

  1. 多くのプログラマは、このイディオムに不慣れである
  2. デストラクタの呼び出し方法がトリッキーになる

問題2. template-based container class

テンプレートを用いたコンテナクラスは、デフォルトコンストラクタを要求する場合が多い。std::vectorみたいに、慎重に設計されたクラスだと問題ないけど、すべてのクラスがそうだとはいえない。

問題3. 仮想基底クラス

class Hoge {
   public:
      Hoge(int x) : x_(x);

      virtual void some_method();
};

class Fuga : public Hoge {
   public:
      Fuga();                  // NG
      Fuga(int x) : Hoge(x) {} // OK
      void some_method();
};

ある仮想基底クラスから派生したクラスのコンストラクタは、親クラスがデフォルトコンストラクタを持たないことを知っておかないといけないという問題。

class Hoge {
   public:
      Hoge(int x = DEFAULT_VALUE) : x_(x);
   private:
      static const int DEFAULT_VALUE;
};

のように、引数にデフォルト値をセットするという方法もある。しかし、これだとちゃんと初期化できているか分からない。なので、このようにコンストラクタを定義しておき、引数が与えられなかった場合は、例外を投げたり強制終了させたりする向きもある。

結論

以上のような問題にうんざりして、コンストラクタにデフォルトコンストラクタをつくってもらいたいと思うかもしれない。しかし、その場合は、メンバがちゃんと初期化されているかをチェックしないといけなくなる。チェックのためのコードが走る時間、コードサイズの増加といったコストを払わないといけない。

なので、初期化しなければいけないメンバをもつクラスで、コンストラクタに引数を与えないと行けないようなものの場合は、デフォルトコンストラクタを使うのは避けよう。そうすることによる弊害を受け入れるのは苦痛かもしれないけれども、ちゃんと初期化されており、無駄なチェックコードが走らないといった利点を享受できるわけだ。


所感

だんだん言語仕様の複雑なところに入り込んできている。

デフォルトコンストラクタを生成させないようにすると、自然なコードが書けないようになる。やっぱりそんな言語はイヤだなぁと思う。

More Effective C++::Item 3. never treat arrays polymorphically

基底クラスの配列を通して派生クラスを多態的に使ってはいけないという話。

基底クラスへのポインタを使って、派生クラスのメソッドを呼べるのが 多態性のうれしいところ。CやC++では、配列とポインタは兄弟みたいなもの。 int a[10]なんて配列があったら、*(a+0)は配列aの先頭の要素のことだ。

でも、

class Base {...};
class Derived : public Base {...};

なんてクラスたちがあったときに、基底クラスの配列を用いて

Base array[10];
for (int i=0; i<10; i++)
{
   array[i] = new Derived(); // 派生クラスのオブジェクトをつっこむ
}

for (int i=0; i<10; i++)
{
   array[i].someMethod();
}

とするのはまずい。 array[0]からarray[1]までのメモリ上の距離がsizeof(Base)に なってしまう。ちゃんとインクリメントできない。

同様の問題は、基底クラスの配列をdeleteするのにfor文で回すときにも起こる。 基底クラスのポインタを通して派生クラスの配列に対してを.delete[].した ときの動作は不定だというのが仕様。

こうした問題は、実装クラスから実装クラスを継承するといった習慣をやめれば 回避できる。(see Item. 33)

所感

実装クラスから実装クラスを継承しないでいようと自分では思う。 しかし、他人が設計したクラスだと、なかなかそうもいかないので、 そういう部分を読む場合には、特別な注意が必要だと思う。

あるいは、基底クラスへのポインタの配列を使えばいいんじゃないかな。

More Effective C++::Chapter1. Distinguish between pointers and references

ポインタとリファレンスとを区別しようという話。 要点は、NULLを指せるかどうか。

NULLなリファレンスはない

でも、

char *pch = NULL;
char &rch = *pch;

なんてコードは書けてしまう。これは動作不定。 こんなコードを書くようなプログラマとは一緒に仕事をすべきではない。

  • リファレンスは初期化されていないといけない。
  • 関数の引数にリファレンスを使うと、NULLチェックをしなくて良くてラク

リファレンスは指している先が不変

変数のエイリアスに過ぎないから。 初期化された値をそのまま保持する。

operator[]を実装するならリファレンスを使う

ポインタで実装すると、

*v[5] = 10

なんて書かないといけない。これだと、ポインタのvectorなのかと 勘違いしてしまう。


なので、

ポインタを使うとき:

  • NULLを指す可能性がある
  • 指す先が変わる可能性がある

リファレンスを使うとき:

  • なにがしかのオブジェクトを指している
  • 指す先が変わらない
  • operator[]のようなオペレータを実装する際

となる。


参照を引数にする関数を書くと、クライアントコードを読むときに その引数が入力用なのか出力用なのか、一目では判断できなくなる という点はある。その関数の仕様を見れば分かるんだけど、仕様が 明記されていない場合 (ソースしかない) なんかには辛いことにな る。とかそんなことを考えてしまうのは、まだCの感覚を引きずっ ているからなのかな。

リファレンスを引数にしたらNULLチェックしなくてよいというのは 、コードをすっきりさせる効果があると思う。

本の読み方

たくさん本を買う。通勤時間にがんばって消化しようとする。でも、ただ漫然と読んでいるだけでは、内容が頭に入ってこない。もったいない。

最近の読み方:

  • 本の背表紙あたりに、付箋を数十枚あらかじめはっておく
  • 気に留まる箇所には赤ペンでカギ括弧してマークアップ。赤線を引くときもある
  • 赤線だけでは足らなそうと思ったら、ページに付箋をはっておく

しかし、最近付箋を貼っていることに疑問が出てきている。結局、再読する際は前から順番にページをくっていって赤マークアップを探すわけなので、付箋の存在意義が謎になってしまう。赤ペンだけでよいのでは。

付箋が役立つ場合:

  • 雑誌なんかだと、読み飛ばすページ (広告とか) が多いから、付箋が活躍しそう
  • APIリファレンスや仕様書といった膨大な冊子も、頭からめくることはないので、付箋と赤ペンとの併用がよさげ
  • 借りた本には線を引くわけにはいかないから、付箋だけで代用する

とかとか。

see also:

[を] ちびポストイットを使おう

知的生産の技術
知的生産の技術
posted with amazlet on 06.01.14
梅棹 忠夫
岩波書店 (1969/07)
売り上げランキング: 3,232
おすすめ度の平均: 4.62
5 かつてのバイブル
5 方法論確立の端緒に
3 前半だけは良書

プロ論。

プロ論。
プロ論。
posted with amazlet on 05.10.21
B-ing編集部
徳間書店 (2004/12/19)
売り上げランキング: 5,430
おすすめ度の平均: 4.39
4 「好き」
5 自分が変われば、世界が変わる
4 それぞれ人はナニを思う

ちょっと気を惹かれて読んでみた。 一人一人のインタビューが3ページなので、さっさと読める。 付箋によると、井筒監督と堀紘一氏のが印象に残った模様。