Archive Page 2
だめプログラマー症候群 (1): だめなプログラマー
プログラムが理解できない
プログラムを理解するということは、プログラムに必要な機能を把握でき、実際のプログラムがどの様に実行されるのかを頭の中でイメージできるということです。
症状
- おまじないとしか言えない命令があったり、必要な機能とは無関係の命令がある。
- 念のためといって、save() 関数などを2, 3回呼び出す。
- バグがある関数を直す際、その関数の出力を操作して直す。
- いちいち別の型にキャストして変数の操作を行う。例えば、小数を文字列にして、文字列分割で整数部を取り出し、整数にキャストしたりする。
- 再利用性を考えずに、既存のプログラムを無駄にサブルーチン化する。
処置
この様なプログラマーは、開発環境のデバッガーを利用して一行ずつステップインし、プログラムを理解する訓練が必要です。例えば Visual Studio なら、プログラムの初めの方にブレークポイントを設定させ、F11 を使って一つ一つ丁寧にステップインさせ、プログラムが一体何を行っているのか理解できる様になるまで、変数の内容の変化を理解させると良いでしょう。万が一ステップインといった機能の無い環境でプログラミングをしている場合は、すぐに他の環境で訓練させるべきでしょう。
プログラミングパラダイムを理解できない
プログラミングパラダイムとはプログラムの見方を与えるものです。例えば、オブジェクト指向は近代のプログラミングパラダイムの代表的な例です。他には関数型や手続き型、宣言型や命令型といったパラダイムがあります。アセンブラや GOTO を使用する言語と、それらのパラダイムが大きく違うことは明確ですが、個々のパラダイムもそれぞれ大きく違います。また、普及している近代的なプログラミング言語の多くがオブジェクト指向を取り入れていますが、オブジェクト指向でも個々の言語で、リスト内包表記 (”List Comprehensions”) や、総称型 (”Generics”)、ダックタイピング (”Duck-typing”) といった独自の拡張がなされています。
症状
- 禁じ手を使ってパラダイムから抜け出し、残りのプログラムを命令型や手続き型のプログラミングを行う。
- オブジェクト指向において、非スタティックな関数をインスタンス化していないクラス自身から呼び出そうしてコンパイルエラーを起こすが、なぜエラーになるのかわからない。クラスとインスタンスの概念を理解していない。
- オブジェクト指向において、あるクラスのオブジェクトを操作する為のメソッドを “xxxxxManager” といったクラスを定義してしまい、肝心のオブジェクトには何も定義しない。
- リレーショナルデータベースに、データベースネイティブな値で保存せずに、不用意にシリアル化したオブジェクトを保存する。
- 関数型プログラミングにおいて、同じアルゴリズムの関数を引数の違いの為に何個も実装してしまう。
- 決定的関数 (”Deterministic Functions”) の結果を自前でキャッシュする。
- 純粋な関数型プログラミングにおいて、入出力やモナドの実装になると、他のプログラムからコピペをする。
- 宣言型プログラミングにおいて、複数の値をデータバインドで設定せずに、一つ一つ命令を使って設定する。
処置
教育を受けていないことが原因でこの様な症状が出ている場合は、適切な教育を受けさせるか、実際のプログラミングを通じて学ばせると良いでしょう。実際のプログラミングを通じて新しいパラダイムを身につけさせるには、プロジェクトに参加させるなどして、とにかく本人をそのパラダイムに集中させる他ありません。また、パラダイムの特徴を理解できるまでは、簡単な言葉で順を追って理解できる様にすると良いでしょう。
オブジェクト指向の例
- オブジェクト指向は、レコードとメソッドの集まりである。
- メソッドは、独自のグローバル変数を持つ細分化されたプログラム中の関数にすぎない。
- それらのグローバル変数はフィールドと呼ばれ、必要であれば外部から隠すことができる。
- プライベート宣言とパブリック宣言の目的は、外部からプログラムの実装を隠し、実装を意識しないで外部から利用できる様にするインターフェースを提供することである。これをカプセル化と呼ぶ。
- カプセル化の結果、ビジネスロジックがプログラムの実装に影響されなくなる。
最後のカプセル化の結果は、ほとんどのオブジェクト指向言語で共通しています。オブジェクト指向の主たる目的は、プログラマーがある機能を利用する際に、その機能の実装を意識しないで済む様にすることだからです。
関数型プログラミングの例
- 関数型プログラミングは、複数の決定的関数を組み合わせである。
- もし関数が決定的な場合、結果が必要になるまで実行される必要は無く、必要な回数だけ実行すれば良い。これらは、消極的評価 (”Lazy Evaluation”) と、部分評価 (”Partial Evaluation”) と呼ばれる。
- 消極的評価と部分評価を利用するためには、渡されたひとつの引数をどの様に変更するか、どう他の関数に渡すべきなのかを関数で定義しなければならない。これをカリー化 (”Currying”) と呼ぶ。
- 関数がカリー化された場合、コンパイラーは制約解決 (”Constraint Solver”) を通じて最適なプログラム実行方法を選ぶことができる。
- 制約解決に通すことで、どう引数を渡してもらうかではなく、どんな引数が欲しいかを記述することに集中できる。
調べことができない・慢性的にプラットフォームを知らない
近代的なプログラミング言語やフレームワークは、非常に幅が広くかつ深い命令と機能が提供されており、先進的なフレームワークである Java や、.NET、Cocoa では、才能のあるプログラマーでも学ぶのに最低でも一年はかかります。才能のあるプログラマーは、事前に必要な命令を探してからプログラミングを始め、特に優れたプログラマーは、目標を達成するために必要なプロセスを細分化し、既存のフレームワークやデザインパターン、さらにモデルやプログラミング言語を選び、実際にプログラミングを始めます。
症状
そのプラットフォームで長くプログラミングをしているにも関わらずこれらの症状が現れた場合、プラットフォームの習得が遅すぎることを意味しています。
- イベントやハンドラー、正規表現など、フレームワークで提供されている機能を再実装する。
- フレームワークで提供されているクラスを再実装する。タイマー、コレクション、ソートや検索アルゴリズムなど。
- 掲示板で教えて君をする。
- 簡単に済ませられる機能をやたらと遠回りなプログラムで実装する。小数を文字列に変換してまるめた後に、再度小数に戻したりなど。
- 同じ機能でもより良い新しい実装方法があるのにも関わらず、既存の古い方法で実装する。例えば、ラムダを使わず名前付きデリゲート関数を未だに書いているなど。
- 自分の知っているレベルでの「心地の良い」プログラミングに執着し、複雑な問題を単純な機能を使って非常に長いプログラムで解決する。
処置
空き時間が無ければこの様なプラットフォームの知識はなかなか身に付かないし、現場ではとにかく実装することを急ぐのでなおさら身につきません。根本的な解決にはなりませんが、手元に便利なプラットフォームのリファレンスを置かせ、できる限り短時間で全体を把握できるようにすべきです。本でもいいですし、2画面にして片方に PDF や、ウェブサイトを開くのでもいいでしょう。リファレンスに目を通す癖を付けさせる為に、まずは既存のプログラムを 10:1 以上に縮めさると効果的です。
ポインターを理解できない
ポインターを理解していないプログラマーは、複雑なデータ構造や効率的な API の設計ができず、限られた種類のプログラムしか書けないでしょう。マネージド言語では、ポインター演算を無くし、一方で自動開放などを追加した参照という機能が替わりに提供されていますが、これもポインターの概念が理解できなければ、値渡しや参照渡しの違いが理解できず、酷いデータ構造やバグを生み出してしまいます。
症状
- 連結リストの実装ができない。連結リストで項目を追加、削除してもデータが失われない様に実装できない。
- 連結リストなどの動的なデータ構造を使わず、不定量の大きな配列を割り当て、配列の長さを別の変数でカウントする。
- ポインター演算によって起きているバグを見つけられない、修正できない。
- ポインターの参照している値が関数外から変更される可能性を意識しない。
- ポインターを複製した後に、参照が解放された元のポインターから値を変更しようとする。
- ポインター自身をシリアル化して保存しようとする。
- ポインターの配列をポインター自身の比較でソートする。
山岡さんはホテルのどこかの部屋に泊まっています。どの部屋かはわかりません。しかし、友達の林さんの部屋はわかるので、そこまでいって山岡さんがどの部屋に泊まっているのか聞きました。すると、林さんは、どこに山岡さんがいるのかわからない様でしたが、山岡さんの別の友達である北島さんの部屋を教えてくれました。そこで北島さんの部屋に行き、山岡さんの部屋を教えてもらい、結果、山岡さんの部屋に行き、山岡さん自身に会うことができました。
ポインターは様々な比喩を用いて説明することができ、データ構造は様々な類推を用いて説明することができます。上の例は連結リストの例で、プログラマーでなくとも思いつくデータ構造です。この様なポインターの説明は、理解できないものではありません。それでもなぜポインターを理解できない人がいるのかというと、コンピューターのメモリー内で一体何が起きているのかを理解する際に、よく似ている変数の概念と絡まってしまい混乱するからです。ポインターの概念が正確に理解できイメージできる様になるまでは、プログラムの内容をわかりやすい他の話にたとえるのがいいでしょう。
再帰関数の処理を把握できない
再帰関数の概念は簡単に理解できますが、処理結果がイメージできなかったり、複雑な計算を単純な再帰関数で実装できるのに気がつかないことがあります。再帰の条件式を書く際にそうなってしまうと、非常にやっかいです。
症状
- 再帰関数で簡単に処理できるにも関わらず、非常に複雑な反復処理を行う。
- 再帰条件を、処理開始時と処理終了時に確認させる。
- 再帰条件を確認しない関数を書く。
- 末尾再帰 (”Tail recursion”) を使うべきところで、グローバル変数や持ち越される変数に再帰処理をまとめる。
- 再帰関数の引数に何を渡して良いのかわからない。引数を操作しない再帰関数に何を渡して良いのかわからない。
まずは一つの条件と、同じ渡された引数を操作しない一つの再帰関数を書かせましょう。何かふに落ちていない様子でも、そこでやめさせ、実際に実行させましょう。スタックオーバーフローが起きるはずですから、プログラムに戻らせ、渡された引数を操作して再帰関数に渡す様に変更させましょう。それでも、スタックオーバーフローや、永遠と出力が続く様であれば、再帰条件を書き換えるなど、適切な結果が出力されるまであれこれ実験させましょう。二つ以上の条件を持つ再帰関数は、完全に理解できるまでは書かせるべきではありません。
目的は、再帰関数を扱うだけの自信を持たせることにあります。再帰処理内で、今どこにいるのかが正確にわからなくてもいいのです。自信が付き、実際に現場で実装する時が来たら、まずは単体テストを書かせ、同じような試行錯誤を繰り返させればいいのです。
きょうのはま
耳が痛いです。
Filed under: ソフトウェア開発, 翻訳, 読み物 | 0 Comments
6月25日付け: 開発版 Rails の変更点 “先を行くこと” (”Living on the Edge”) その2: パフォーマンス改善 — Ruby on Rails 公式ブログより
最初の “先を行くこと” では API の変更について扱いましたが、前回も述べたとおり今回はパフォーマンス改善について扱おうと思います。
では、早速。
高速になった ERB テンプレート
ジェーミー・キャンパー (”Jeremy Kemper”) が ERB の処理を、特にヘルパーメソッド concat と capture を効率化してくれました。また「特別な」ERB である _erbout は、インスタンス変数に置き換えられ、以下の様な結果が生まれました。
- バインディングがあちこちにコピーされなくなった結果、(メモリー)パフォーマンスの改善
- 一般的に重い処理が多い eval メソッドの使用回数の減少
- 文字列バッファーをスワップできるので、_erbout を切り出す必要が無くなった
- output_beffer で文字列バッファーを読み書きでき、必要に応じてオーバーライド可能になった
関連する変更セットは 993697a obdb7d3 4d4c8e2 です。
部分テンプレートと JavaScript ヘルパーの高速化
キャンパーさんによって、部分テンプレートと JavaScript ヘルパーの初期化にリファクタリングが施され、処理速度と処理効率が最適化されました。これらは、キャンパーさんが最近コミットした最適化のほんの一部でしかありません。詳細は Rails へのコミットをご覧ください。他の高品質なオープンソースプロジェクトからも学べる様に、これらのコミットからも学ぶことがたくさんあります。
RecordIdentifier メソッドの高速化
RecordIdentifier が「メモ化機能」(”Memo-ization”) の単純な利用により、語形変化 (”Inflection”) が少なくなる等して高速化されました。RecordIdentifier は、ActiveRecord モデルの ID を気にせず一意に認識する為に使われることが多い他、キャッシュキーや、部分テンプレート等でも使われています。
キャンパーさんによる関連する変更セットは c1a9820 566d717 です。
キャッシュの消極的読み込み (”Lazy-loading”)
お知らせ: この変更は、後ほどの変更 6573f6a で元に戻されました。
ActiveSupport::Cache モジュールに含まれるいくつかのキャッシュストアが、消極的読み込みを行う様になりました。実際に使い始めた時にのみ、“require” メソッドで読み込まれることを意味します。
きょうのはま
先日受けた英検一級の一次試験に通過していました。受験時に語彙問題だけずいぶん難しいと思ったのですが、案の定、語彙問題の得点が一番悪かったです。
Filed under: Ruby, ソフトウェア開発, ドキュメント, 翻訳, 開発版 Rails "edge" | 0 Comments
Ruby で書くと恐らく必要なパフォーマンスが出せないことや、C言語で似た様な関数の実装が既にあることから、とあるメソッドの実装で Ruby のネイティブ拡張ライブラリを書くことになりました。
そこで、いくつか必要な情報がまとまっているページを集めたので、リンクしておこうと思います。
- Ruby 拡張ライブラリの作り方の説明 — 公式リポジトリ
- Ruby 拡張ライブラリ作成チュートリアル — 地球流体電脳倶楽部
- Writing your very own Ruby extension with C — Fixnum.org
- How to create a Ruby extension in C in under 5 minutes by Peter Cooper — RubyInside
- Ruby Plays Well With Others - Part 2: Ruby C Extensions by Mark Volkmann (PDF) — Object Computing, Inc.
おまけですが、Ruby のコード内に埋め込まれたC言語の関数を自動的にコンパイルして Ruby のメソッドとして提供する RubyInline も。
例えば、以下の様な感じ。
#!/usr/bin/ruby
require "rubygems"
require "inline"
class Hoge
builder.c %q{
int moge(int x) {
return x*3;
}
}
end
hoge = Hoge.new
hoge.respond_to?(:moge) # => true
x = hoge.moge(3) # => 9
x.class # => Fixnum
実行環境は POSIX 系 (Linux, Mac OS X, Cygwin 等) のみの様ですが、なかなか便利そうです。また、引数と戻り値に使われる Ruby のオブジェクトと C の変数を相互に自動変換してはくれますが、Numeric 系と String 系だけの様で、他は自前で変換する必要がありそうです。ただ、ruby.h をインクルードしているので、ネイティブ拡張ライブラリでしたいことの多くはできると思います。
7月8日 追記
プログラミング Ruby 第2版、第2部 第21章 “Ruby を拡張する” で、Ruby のネイティブ拡張ライブラリを作る方法を、チュートリアル付きで詳しく解説しています。
C 内で Ruby のオブジェクトをどう扱ったらいいのか、C から Ruby に値を渡したい時はどうすればいいのか、メソッドの定義は、クラスの定義は、コンパイルは、リンクは、インストールはどうしたらいいのか、大抵のことはこれでわかると思います。
Filed under: Ruby, ソフトウェア開発 | 0 Comments
日本からも何人かの技術者が参加したらしい先月末に行われた Ruby on Rails と Ruby 関連の会議 RailsConf 2008 (5月29日〜6月〜1日) からか、週一回程度、ブログで開発版の Rails の更新点を的確な形で紹介しているチュー・ヤオさん (”Chu Yeow”) が、Ruby on Rails の公式ブログ Riding Rails に参加した様で、公式ブログでも開発版の変更点や更新点が気軽に確認できるようになった様です。
もっとも、実務で開発している方々の多くが開発版の Rails を現場で使っていると思いませんし、Rails に直接コントリビュートしている人は IRC チャンネルにいたり、日常的に Rails の ChangeLog を見ていると思うので必要が無いとは思いますが、今後の公開版がどういった機能を備えるのかを把握するのには役に立つと思いますし、自分自身も更新を追うのにもちょうど良いと思うので、公式ブログの更新に合わせてこちらでも軽く翻訳を流していこうと思います。
というわけで、早速。
開発版 Ruby on Rails の最新情報 (2008年6月20日)
グレッグ・ポーラック (”Gregg Pollack,” Rails Envy Podcast) が一週間ほど前に言った様に、一週間前後で更新しているこの開発版 Rails の記事を今後も続けようと思います。Ruby on Rails の公式ブログでは初めての投稿なので、まずは少し自己紹介をさせてください。
Living on the Edge (”先を行くこと”) とは、Rails Envy のグレッグ・ポーラックにそそのかされて、2007年12月から私が週刊で投稿していた記事のことです。というのも、以前は私自身も活発に Rails にコントリビュートしていたので、たいしたことではありませんでした。嬉しいことに、グレッグとジェイソン (”Jason Seifer,” Rails Envy) の週に一度のポッドキャストでは、紹介もされました。
これからは、この公式ブログでの更新になります。皆さんのご期待に添える様に有益な記事を書こうとを思います。今までは小さな個人的なブログだったので、それほどたいした価値もなかったかもしれませんが、ここでは話が違い、訪問者の量が違います。何か提案や批評がありましたらコメント欄にお気兼ねなく投稿してください。
本題ですが、Rails 2.1 が公開されてからの過去二週間で、既にたくさんに新しい機能が Rails に実装され、API の変更や、パフォーマンス改善がされました。それらの投稿をまとめて大きな一つの投稿にする変わりに、新機能と API の変更という投稿と、パフォーマンス改善という投稿の二つに分けたいと思います。まずは、新機能と API の変更から。
小規模な API 変更
まずは小規模な API の変更から見てみましょう。
ヘルパーメソッドの link_to が、非常に長いアンカーテキストを渡す場合などの為に、以下の様に、ブロック引数を受け取る様になりました。
<% link_to(@profile) do %> <strong><%= @profile.name %></strong> - <span>Status: <%= @profile.status %></span> <% end %>
同じ機能をする以下のコードより綺麗かもしれません。
<%= link_to "#{@profile.name} -- Status: #{@profile.status}", @profile %>
prototype.js で有名なサム・ステファンソン (”Sam Stephenson”)と、デビッド=ハイネマイヤー・ハンソン (”David Heinemeier Hansson”) が実装してくれました。
ActiveRecord::Base#merge_conditions が公開メソッドに
ジェーミー・キャンパー (”Jeremy Kamper”) が、ActiveRecord::Base#merge_conditions メソッドを公開メソッドにしてくれました。
これは、何らかの理由で複数のソースに条件があるときや、やはり何らかの理由で複数の条件をまとめる必要がある場合に便利です。
Post.merge_conditions(
{:title => 'Lucky ☆ Star'},
['rating IN (?)', 1..5]
)
=> "(`posts`.`title` = 'Lucky ☆ Star') AND (rating IN (1,2,3,4,5))"
ただ、SQL のブール式 OR ではなく、AND を使って結合されることに気をつけてください。
関連付けの :validate オプション
関連付けマクロが以下の様に :validation マクロを受け取る様になりました。
class Anime > ActiveRecord::Base has_many :characters, :validate => true end
この様にすると、:validates_associated の様に、Anime モデルが保存される際、関連付けられた characters が正規化されます。初期値は false で、Rails 2.1 からの挙動の変化はありませんから、気にしなくても大丈夫です。
この変更と合わせて、難しいバグを直してくれたジャン・ド・ポールテル (”Jan De Poorter”) とプラティック・ネイク (”Pratik Naik”) に感謝。
コミット内容は GitHub から閲覧できます。バグのチケットはこちら。
ハンソンさんが ActiveSupport::StringInquirer という以下の様なことを可能にする String のサブクラスを実装してくれました。
s = ActiveSupport::StringInquirer.new('awesome')
=> "awesome"
s.awesome?
=> true
s.sucks?
=> false
利用方法としては、既に Rails.env が StringInquirer にされているので、現在の実行環境を Rails.env.development? や、Rails.env.production? といった方法で確認することが考えられます。
コア拡張 Object#present? と Enumerable#many?
ハンソンさんは、他にもコードの可読性を高めるコア拡張も実装してくれました。一つは Object#present? メソッドで、実機能は !Object#blank? です。
[].present? => false [1, 2].present? => true "".present? => false "i'm here".present? => true
enumerable.size > 1 と同じ機能を果たす、Enumerable#many? メソッドも実装してくれました。
[].many? => false [:just_me].many? => false [:just_me, 'my_friend'].many? => true
Object#present? と Enumerable#many? コミット内容は GitHub から閲覧できます。
テスト用の宣言的ブロック文法
ハンソンさんは、ジェイ・フィールズ (”Jay Fields”) に感銘を受け、以下の様な糖衣構文を追加してくれました。これにより、Test::Unit を使ったテストで、以下の様にブロック構造で記述ができる様になりました。
test "an anime should be invalid if any of its characters are invalid" do # Your usual test code here. end
私自身は、Rails にパッチをアップロードする時以外は、めったに Test::Unit を使わず RSpec を利用していますが、この構造は確実に可読性を高めます。
パフォーマンステスト
近頃 Rails の最適化とパフォーマンス改善に力を注いでいるキャンパーさんが、新しい結合テスト、パフォーマンステストを実装してくれました。
また、パフォーマンステストのスケルトンを生成してくれるジェネレーターも、ネイクさんによって実装されました。以下のコードで生成できます。
script/generate performance_test LoginStories
これらのパフォーマンステストを実行するには、まだ公開されていない ruby-prof のバージョン 0.6.1 以上が必要ですが、開発版がリポジトリからチェックアウトして gem を自分でインストールすることができます。今のところ、キャンパーさんのフォークした ruby-prof がおすすめです。ruby-prof 0.6.1 では、Test::Unit を使ってプロファイルテストを書けるところですが…。
先に進みましょう。試しに、ユーザー操作の仮説テスト (”Story Test”) に合わせてパフォーマンステストをしたいコントローラーに対していくつかのリクエストを行うテストを書き、実行すると、以下の様な実行結果が表示されます。また、/tmp/performance には、ruby-profile の出力が書き出されます。
> ruby performance/login_stories_test.rb
Loaded suite performance/login_stories_test
Started
LoginStoriesTest#test_homepage (32 ms warmup)
process_time: 11 ms
memory: unsupported
objects: unsupported
.
Finished in 0.870842 seconds.
memory と objects は未対応 (”unsupported”) となっていますが、これは私が、Ruby のインタープリターにパッチを当てていない為です。メモリープロファイリングと、ガベージコレクションプロファイリングを有効にするには、パッチを当てないといけないのです。この辺りはあまり詳しくないので詳しい情報を伝えられませんが、幸いにも詳しい方がこちらでメモリープロファイリング用のパッチに関する詳細を伝えてくれています。
あとがき
Rails 2.1 から今までの新機能と API の変更点はこんなところです。パフォーマンス改善とについては次の投稿で紹介します。また、今回は、開発版に部分的にマージされた Rack への対応をあえて取り上げませんでした。
もし、何か間違えがあったり改善すべき点がありましたら、コメント欄でご指摘ください。Ruby インタープリターをメモリープロファイリングに対応させるパッチに関する情報は特に大歓迎です。取り上げていないものは、それほど重要でないと私が判断したものですが、そうでない場合もまたコメント欄でご指摘ください。
Ruby on Rails 公式ブログの記事
Filed under: Ruby, ソフトウェア開発, ドキュメント, 翻訳, 開発版 Rails "edge" | 1 Comment
先日途中ながら公開した Rails 2.1 の変更点 PDF 日本語訳 ですが、更新しやすい様に、GitHub のリポジトリに追加しました。
GitHub リポジトリのページ
Filed under: Ruby, ソフトウェア開発, ドキュメント, バージョン管理, 翻訳, 読み物 | 0 Comments


