2013年02月11日

TB: check_xxx がなんでダメなのか

check_xxx がなんでダメなのか - Yamashiro0217の日記

"check_xxx"みたいなメソッド名は避けようという意見には賛成。

だけど、流石にそれは例が悪い。

はてブのコメントでid:r-westが指摘しているように、いろいろな問題を詰め込んでしまってるから、「なぜその命名が拙いのか」という本題がかすんでしまっている印象。
"check_xxx の理由最初のだけ"ならば、それに絞って書くべき。例え、"check_xxx 書くやつ、絶対他のこともそのメソッドでやるんだ"としても。

以下、"check_xxx がなんでダメなのか"を自分流に解説し直してみる。
元記事に則って、"check_user"というメソッド名を例に説明する。
元記事のコードは……Perl? PHP? どっちにしろ忘却の彼方なので、以下Cのコードという事で。

例えば、以下のコード片、一体何をやっているのだろうか?

enum User_Kind {
	USER_KIND_UNKNOWN,
	USER_KIND_NORMAL,
	USER_KIND_SUPER,
	USER_KIND_ADMINISTRATOR
};

struct User {
	char *name;
	unsigned int age;
	enum User_Kind kind;
} user;

/*
 * なんかいろいろ
 * ながったるい処理が
 * 在ったり無かったり
 */

if(check_user(&user)) {
	/*
	 * なんかいろいろ
	 * ながったるい処理が
	 * 在ったり無かったり
	 */

	 // (1)
} else {
	/*
	 * なんかいろいろ
	 * ながったるい処理が
	 * 在ったり無かったり
	 */
}

うん、User型オブジェクトの「何か」をチェックして、それで処理を切り分けている……でも、何をチェックしてるの?

パッと思いつくのは次の2つか。

  • userの各メンバが「正しい形式に基づいているか」をチェックする。
    (nameに使われている文字種や最大長、ageの値が想定内の範囲か、kindの値が不正ではないか、等)
  • userの各メンバに設定した値に合致するユーザーが居るかどうかを(DB等から)チェックする。

他にも考えられる処理があるかも知れない。

仮に(1)の位置に"removeUser(&user)"とでも書かれていれば、後者の意味だったのだろうと予想(あくまで予想)出来るけれど、そこまで読み進めないと意味が読み取れないというのは、どうよ。可読性高いだろうか?
更に、1つだけならばまだしも二重三重と"check_xxx"な条件分岐がネストされていたらと考えたら……

え? 定義元を読めばいいじゃんって?

確かに定義元を読めばその関数が何やってるのかは分かる。理屈の上では。

でもさ、その定義元(この例の場合では"check_user")内に、更に"check_xxx"が在ったら? その定義も読むの? 更に更に"check_xxx"が在ったら? そうすると、どんどん読む必要が有る場所が増えていくよ?

で、読み終わって何やるメソッドか把握出来たら元の場所に戻って続きを……あれ?元の場所って何処だっけ?


// (各関数プロトタイプは省略)

// 略
	if(check_user(&user)) {	// 今読んでいる場所
		// 略
	}
// 略

_Bool check_user(const User *user) {
	// 略
	check_puyo(user);
	// 略
}

Something check_puyo(const User *user) {
	// 略
	check_piyo(something);
	// 略
}

Something check_piyo(const Something something) {
	// 略
	check_huga(something);
	// 略
}

Something check_huga(const Something something) {
	// 略
	check_hoge(something);
	// 略
}

Something check_hoge(const Something something) {
	// 略
	// ここまで読んで、やっとcheck_userが何やってたか分かる
}

え? コメント書けばいいじゃんって?

// 各メンバと同じ値を持つユーザーデータが有るか調べる
if(check_user(&user)) {
	// 以下略

それってつまりは、以下の「明らかな事をわざわざコメントに書く」のと一緒だよね。

// fooが0未満の時
if(foo < 0) {
	// 以下略

明らかに無駄。
そんな無駄なコメント書くぐらいならば、分かり易い名前付けようよ、と。

if(exists_user(&user)) {
	// 以下略

これならば、「ユーザーの何について調べたいのか」は自明だよね(もっと良い名前はあるかも知れないけれど)。

posted by 天井冴太 at 02:19 | Comment(0) | TrackBack(0) | Other | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2013年02月06日

GVimの起動時には幾つかの関数が使用出来ない

ここで言う"起動時"というのは、GVim起動直後の.vimrc処理時で、多分、"has('vim_starting')==1"な時の事。

なお、GVim――つまりGUI用のVimでの話であって、コンソールで使う普通の(?)Vimでは問題ない。

:help input()曰く。

NOTE: This function must not be used in a startup file, for
the versions that only run in GUI mode (e.g., the Win32 GUI).

しかしどうもinput()だけの話じゃ無いようで、以下の関数が使えなかった。

  • input()
  • inputdialog()
  • inputlist()
  • confirm()

ユーザーに何か問い合わせる系統かな……そういう関数って他にも在る?

inputで始まる関数群はまともに動かない(空文字列とか0が帰ってきている模様)だけだけど、confirm()が曲者で、Linux(Crunchbang)だとGVimの起動さえしない。

因みにWindows XPだと、第1引数と第2引数を改行で連結した内容がダイアログボックスで表示された。例えば、

call confirm('prompt', "Yes\nNo")

とだけ書いたファイル(%HOMEDRIVE%%HOMEPATH%\test.vim)を用意して、以下のように実行すると、

Windows XPの

gvim -u "%HOMEDRIVE%%HOMEPATH%\test.vim" -U NONE

以下のダイアログボックスが表示される。

1行目に

:autocmdにするとよさげ。やや意味合いが変わってくるけれど、影響は無いんじゃないかなぁ?

autocmd VimEnter * call confirm('prompt', "Yes\nNo")

どうも、--cmdオプションで使った時も同様の問題が発生する模様(あまりきちんと調べていない)。

バージョン7.3で確認。

タグ:Vim_script Gvim vim
posted by 天井冴太 at 00:35 | Comment(0) | TrackBack(0) | Tool | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2013年01月23日

共通部分でなくとも抽象化

読みやすいコードってどんなものか考えてみた -抽象化と名前重要- - 馬鹿と天才は紙一重読んでその2。その1はRubyの「範囲」は分かりづらいを。

"共通部分でなくとも抽象化"項の補足。

共通部分(コード中で繰り返し出てくる記述)でなくても抽象化すべき例として、件の記事ではneed_to_pay_postageメソッドを例として挙げている。

Product.where(:price => 0..5)

このコードでは"productモデルの中で、priceカラムの値が0から5までを取得している"という「処理」は分かっても、それが何を「意図」しているのかは読み取れない。だから以下のようにして「意図」が読み取れるようにしましょうと。

class Product < ActiveRecord::Base
  scope :need_to_pay_postage, lambda {
	where(:price => 0..5)
  }
end

「別にコメント書いとけば良くね?」とも言われそうだけれど、そうでも無いよなぁと。

仕様変更なんていくらでも起こりうる事で、今は「5ドル以下の物には一律送料がかかる」だったとしても、将来それが別の条件に変わる事も有り得るわけで。先日Amazonが仕様を変えたのは記憶に新しいところ。その仕様だって、最初からそうだった訳ではないのだしね。

このコードだって、将来、shim0muraさんが書いているような"書籍については10ドル以下の商品が、また衣類については30ドル以下の商品が送料がかかり、それら以外のカテゴリの商品で5ドル以下のものであれば送料がかかる"なんて複雑な仕様へ変更される可能性はいくらでも有る。その為にも抽象化はしておくべきなのだろうなと思う。変更が必要な部分を探すのも容易になるし、その影響が及ぶ範囲を必要最低限にする事も出来る訳だ。

タグ:Technique
posted by 天井冴太 at 01:47 | Comment(0) | TrackBack(0) | Other | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2013年01月21日

Rubyの「範囲」は分かりづらい

読みやすいコードってどんなものか考えてみた -抽象化と名前重要- - 馬鹿と天才は紙一重

自分は果たして読みやすいコードを書けているのか、やや恐ろしく感じながら読んだ。

で、気づいた事その1。その2は後日その2はこちら。共通部分でなくとも抽象化

件の記事に以下のようなコードが在る。

Product.where(:price => 0..5)

このコードの0..5の部分、これは「0から5まで」という「範囲」を表すRubyの記法で、これの意味は"productモデルの中で、priceカラムの値が0から5までを取得"という事になる。

なるんだけれど、最初、以下のように誤読してしまい、その後の文の意味が一瞬解らなかった。

Product.where(:price => 0.5)

ドット(.を1つ見落としてしまった。これだと「priceカラムの値が0.5の物を取得」という事になってしまう。

"."なんて見た目ただの「点」なので認識しにくい事、認識しにくいこと。

ではどうすれば良いんだろう? ……括弧で囲ってみてはどうか。

Product.where(:price => (0..5))

単純な実数を括弧で囲う奴なんて先ず居ないだろうし。実数だとしたら不要な記述とする事で読み手の注意を引きつけようという魂胆。

或いは、空白で区切るのは?

Product.where(:price => 0 .. 5)

これがもし実数ならば、小数点の前後に空白を差し挟む事は出来ない(そもそもしない)ので、読み間違える事はないだろう。更にこの場合、仮にコーディング時にドットを1つ書き漏らしてしまっても、Syntax Errorになるので、直ぐに気づく事が出来そうだ。

……うーん、個人的には前者の方が好みかなぁ。メリットが多いのは後者だろうけど。

タグ:ruby
posted by 天井冴太 at 02:10 | Comment(2) | TrackBack(1) | Study | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2013年01月07日

Gitでtypoしたコマンドの正しい綴りを推測、自動実行させる

Avner Cohen : Git - Autocorrect spelling

へー、知らなかった。というわけで紹介。


Gitコマンドを打つ時(いや、Gitに限らないが)、何らかのtypoを犯してしまう事は良くある。

例えば、

git pusj	# 本当はpush

勿論Gitに"pusj"なんてコマンドは無いので、本来ならばエラーとなる。

エラーを確認し、正しいコマンドを打ち直すのは何とも面倒だ。しかし、ユーザーが意図していたコマンドを推測し、それを実行するようにGitを設定する事が出来る。

その為にはhelp.autocorrectを使う。

git config --global help.autocorrect 1

help.autocorrectの引数は、補完対象のコマンドを実行するまでの待ち時間(0.1秒単位)である。上記の場合であれば0.1秒待つ事を意味する。

先の"pusj"の場合であれば、"push"のtypoである事が推測出来るので、Gitは以下の警告メッセージを出力後、指定の時間だけ待ってから"push"を実行する。

WARNING: You called a Git command named 'pusj', which does not exist.
Continuing under the assumption that you meant 'push'
in 0.1 seconds automatically...

(勿論"0.1 seconds"の部分は、実際にhelp.autocorrectに設定した値によって変動する。)

待ち時間が在るのは、ユーザーの意図と異なる推測が行われた時に、それを(例えばCTRL-Cで)キャンセル出来るようにする為だろう。

なお、値を0にすると候補の列挙のみ(つまりデフォルトの挙動)、負数にすると待ち時間無しに推測したコマンドを実行する。

因みに私は20、即ち2秒待機するように設定している。誤った推測をキャンセルする事を考えると、数秒のウェイトは必要ではないかと思う。

タグ:tips vcs git
posted by 天井冴太 at 02:34 | Comment(0) | TrackBack(0) | Tool | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2012年12月25日

Vimperator Advent Calendar 2012 開催します

Vimperator Advent Calendar 2012 : ATND

(Vimっぽく)ほぼキーボードだけで快適にwebブラウジング出来るVimperatorという、Firefox用アドオンがある。JavaScript製のプラグインを用いることで容易にカスタマイズする事が出来る、有る意味で究極のブラウザだ(プラグインだけど)。

今回、そのVimperatorのAdvent Calendarを開催する事にした。

(開催決定についてのログはVimperator ? LingrLingrのVimperator部屋に残っている。)

Advent Calendarとは、12月1日から25日まで、参加者が一人1日ずつ、お題に沿った記事を書くという、IT業界ではおなじみのイベントである。

Vimperatorに関する事ならばどんな内容でも構わない。「私なんかのレベルじゃ申し訳ないし」などといった心配は無用である。なにせ私自身、過去Advent Calendarに参加したのは1回だけなのに主催、という無謀っぷりである。

詳細は上に挙げたリンク先を参照して欲しい。

皆様ふるってご参加下さい。

Vimperator Advent Calendar 2012 : ATND

posted by 天井冴太 at 23:59 | Comment(0) | TrackBack(0) | Information | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2012年12月09日

プログラミング言語なんてアセンブリ言語だけで良かった

ネタ元: プログラミング言語なんてCだけで良かった


なぜ、使い捨てみたいな技術が乱立しているのか。

プログラマの学習コストがひとつの言語に集中された世界ならば
その世界の底辺PGでもいまの最高クラスのPGを超えているだろう。

プログラミング=アセンブリ言語という世界ならば
教える側のノウハウもずっと集約されているに違いない
教材もずっと洗練されたモノになっていたに違いない。

プログラマの仕事=アセンブリ言語ならば、学生も将来のために安心して何の迷いもなく学習に集中できる。

最近の使い捨てクソ言語は効率が良いとか言ってるが
アセンブリ言語で作られたシステムの何倍も遅いので結局効率が悪いのだ。

遅いシステムは電力の無駄遣い。

世の中は脱原発に向かっているのにトチ狂っている。


以下蛇足という名の無粋なツッコミ

  • 学習コストを集約するというのであれば、世界初以降は全て無駄だという話になるけど……
  • C言語よりアセンブリ言語の方が歴史が古い。C言語誕生は1972年、アセンブリ言語は1949年である。
  • え?アセンブリ言語は高級言語じゃないって? 贅沢だなぁ。じゃあFORTRANはいかが? 1957年生まれ。C言語より古い。
  • この増田が言うC言語は、勿論K&R Cの事なんだよね? ISO Cじゃなくて。K&RとISOのそれは文法に相違点があるし。
  • 個人的にはオブジェクト指向もジェネリックプログラミングもダックタイピングもガーベジコレクション(スマートポインタ含む)もアレもコレも、つまりC言語にない概念は何も要らんという様は、「あーこの人ちょっと可哀想だな」と思った。
  • 更に言うと、アセンブリ言語には型もスコープもへったくれもないね。ステキナセカイデスネー(棒
  • 開発効率の事も思い出してあげて下さい。
  • ところで、高度な抽象化概念を持つ言語の方がシンプルな言語より効率が悪いという考えは完全に誤りである。例えばC++で書いたプログラムがCで書いたプログラムより非効率だという事はない。……という話がC++関係の文献に載ってたな。何だったっけ……C++の設計と進化だったかな……?

  • mattnさんが同じネタ既に書いてそうだなと思いつつ書いた(投稿直前に調べてみたが書いてなかった)。てか何で「プログラミング系改変ネタ==mattnさん」のイメージがあるんだろう。我ながら分からん。mattnさんごめんなさい。
posted by 天井冴太 at 01:34 | Comment(0) | TrackBack(0) | Other | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2012年11月05日

Herokuのconfig varをローカル環境下のスクリプト内で取得するには(heroku-api版)

AMAgrammar <> PROgrammer: Herokuのconfig varをローカル環境下のスクリプト内で取得するには

……という記事を以前書いたが、何時の間にやらheroku gemを使う方法がdeprecatedになっており、以下のような感じで警告文が出力されるようになっていた。

require 'heroku'

EMAIL = '(Herokuに登録した時に使ったメールアドレス)'
PASSWORD = '(Herokuのパスワード)'
APPLICATION = '(アプリケーション名)'

heroku = Heroku::Client.new(EMAIL, PASSWORD)
vars = heroku.config_vars(APPLICATION)
 !    DEPRECATED: Heroku::Client#deprecate is deprecated, please use the heroku-api gem.
 !    DEPRECATED: More information available at https://github.com/heroku/heroku.rb
 !    DEPRECATED: Deprecated method called from /usr/lib/ruby/gems/1.9.1/gems/heroku-2.32.14/lib/heroku/client.rb:129.

どうやら今後はheroku-apiというgemを使えばいいらしい。早速gem installして、GitHubのREADME.md(と、heroku-apiのコード)を読みつつ書き換える。

require 'heroku-api'

EMAIL       = '(Herokuに登録した時に使ったメールアドレス)'
PASSWORD    = '(Herokuのパスワード)'
API_KEY     = '(API Key)'
APPLICATION = '(アプリケーション名)'
KEY         = '(値を取得したいconfig varのKEY)'

# API Keyが解っている場合
heroku = Heroku::API.new(:api_key => API_KEY)

# API Keyが解らない場合 (Thank you geemus!)
heroku  = Heroku::API.new(:username => EMAIL, :password => PASSWORD)

# APPLICATIONの全config varを取得
vars = heroku.get_config_vars(APPLICATION).body

pp vars

heroku-apiの各種APIの戻り値は大体Excon:Responseのインスタンスになってるっぽい。

以前は出来たHeroku::Auth.clientと同等の方法、herokuコマンドの認証情報を使うというのは出来なくなっているようだ。冷静に考えてみたら、herokuコマンドの認証情報を第三者のアプリケーションから利用出来るとか、セキュリティホールになり得るしねぇ。仕方ないか。

API Keyであれば、(以前の)メールアドレスとパスワードに拠る認証よりはセキュアなのではないか、多分。READMEに書かれているように、API Keyは環境変数HEROKU_API_KEYを設定しておけばそれを利用するようになっている。なお、API KeyはHerokuのAccountページから確認と再生成が可能だ。

タグ:ruby heroku
posted by 天井冴太 at 17:00 | Comment(0) | TrackBack(0) | Study | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する

なぜJavaScriptでは 10..toString() は動き、 10.toString() は動かないのか

javascript - Why 10..toString() works but 10.toString() does not? - Stack Overflow

非常に興味深かったので紹介。

ちょっとよく解らんっちゃけど。

152..toString(2)

これだとバイナリ文字列"10011000"が正しく生成されるっちゃけど、

152.toString(2)

これだと"SyntaxError: identifier starts immediately after numeric literal"って内容の例外が発生するっちゃんね。

なんで? 後者の方がまともな文で、前者は変な文に見えるっちゃけど?!

確かに。

回答を総合すると、これは、"."が複数の意味を持つ所為だという。即ち、小数点としてのそれと、"object member operator"としてのそれと。

JavaScriptの数値リテラルは小数点の前後の数値が0の場合は、それを省略する事が出来る(Final draft Standard ECMA-262 edition 5.1, March 2011 (Rev. 6) - Ecma-262.pdfの7.8.3 Numeric Literals)。

(1.) == (1.0)	// true
(.1) == (0.1)	// true

因って、"152.toString(2)"は、"152."迄で1つの数値リテラルトークンとして処理され、続く"toString"が解釈出来なくなりSyntax Errorとなるそうだ。

対し、"152..toString(2)"と"."を2つ連続させた場合は、1つ目が小数点、2つ目がobject member operatorとして解釈されるため、正しく処理出来ると。

言われて初めて気づいたが、確かに小数点とobject member operatorは同じ記号だ。あまりにも当たり前すぎて、今までまったく気にかけた事がなかったなぁ……

object member operator
この回答で使われていた言葉を拝借。オブジェクトとそのメンバを区切る"."記号の事。EcmaScriptの規格書を簡単に攫ってみたが、この"."の正式な名称はよく分からなかった。
posted by 天井冴太 at 04:51 | Comment(0) | TrackBack(0) | Study | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する
2012年11月01日

2012年度のAdvent Calendarメモ

もうそんな時期か……目について気になるAdvent Calendarのメモ。

何処かにメモしとかないとすぐ忘れちゃうからね。

随時追加予定。

あと、JavaScriptとRubyあたりのAdvent Calendarが欲しい所。

勿論こんなに読める訳無いので、タイトル見て気になったものだけ読む方向で。そうして考えるとタイトル重要だな(今更)。

昨年はBoost Advent Calendarに参加して以下の記事を書いた。

BOOST_TEST_DONT_PRINT_LOG_VALUE()が効くBOOST_CHECK_EQUAL_COLLECTIONS()を作る

今年は……どうしようかな。ネタがないなぁ……

posted by 天井冴太 at 22:17 | Comment(0) | TrackBack(0) | Other | このブログの読者になる | 更新情報をチェックする
投票お願いします 人気blogランキング - 投票する