「連想配列」を含む日記 RSS

はてなキーワード: 連想配列とは

2017-02-24

祝日csv文句言ってる奴は間違いなく三流プログラマー

祝日の日付が毎年変わることは当たり前だし、春分秋分の日みたいに発表されて初めて確定する日もある。

祝日名称が変わることは容易に想像できるし、例のcsvはそういった事情を鑑みて優れてるとは言えないが妥当ものといえる。

そもそも仮に特定の日付について祝日かどうか判定して名称を取得するだけならいわゆる連想配列作るわけで

あのcsvからそれを作ることの何が大変かさっぱり理解できん。

2要素ずつ取り出してkeyとvalueに突っ込むだけだろうが。

そりゃjsonになってたら楽かもしれんけど言語によっては逆に面倒だわ。

2015-08-29

すげえ

いまテレビで「図書館に逃げておいでツイート」を取り上げてる


あんたもよく図書館いってたよね」って超めんどくさい空気になった


おかげで図書館って単語自殺連想配列になって禁句になったわ

2015-06-07

おでんの集計

結局どうやるのが良かったんだろう。grepwcで頑張るには複雑すぎる気がした。

ツイートの取得のやり方は思いつかないんだけど、集計はmecabに渡した後Perlなりで名詞連想配列に放り込んでカウントダンプするのがよさそうかなと思った。

…量そんなないし手作業でやるのが一番だったかなぁ。。。

2014-03-06

http://anond.hatelabo.jp/20140306125206

データ構造に柔軟性を!変化を! → XMLDB

ここに、ドキュメント指向MongoDBとかCouchDB系が入ってきてる。

データ構造/表現最近プログラミング言語ハッシュマップ(or 連想配列 or 辞書)型に近くて

十分に直感的に扱えるから、わざわざ階層DBとか持ち出さないんじゃない。

2014-01-17

http://anond.hatelabo.jp/20140116221703

一理あるご意見いただきました。ありがとうございます

英語として読みにくいなら、メソッド名のほうを変えるよ。

モンスター.attackedBy(勇者.attackPower)

誤解を招いてしまったようですいません。先の理由1では、"能動態のほうが受動態より解りやすい"、ということを主張したかったのです。

あとづけで申し訳ないのですが、その順序で書くのにはもう一つ、MVC的な理由があります

理由3: 入力Controllerのコントロール対象が明確になる

RPG戦闘シーンの入力Controllerは、ユーザーメニュー選択等によって、勇者の攻撃方法攻撃対象モンスターを決定し、攻撃を実行する。

まり、この入力Controllerがコントロールする対象は、勇者と彼の攻撃対象だ。

勇者.attack(モンスター)

と書くと、勇者に命令していることが明示されるので、その設計意図がはっきりする。

攻撃方法型は、モンスター側への攻撃にも、勇者型への攻撃にも使える

なるほど、その設計は思いつきませんでした。〈モンスターへの攻撃と勇者への攻撃を対称に捉える〉という視点目からウロコです。

増田さまへ二つ質問がございます

ひとつめの質問は、攻撃方法型は、具体的にはどんなメソッドフィールドが並ぶのか、です。

考えてみたのですが、どうしても"通常攻撃","ディレイ攻撃","attackPowerの値","すばやさ", などが並ぶ、ディクショナリ(連想配列)的なものが思い浮かんでしまます。そうすると、結局モンスターのattackedByメソッドにif分岐を並べることになってしまうので、増田さまお考えの設計とは違うのでしょう。

ふたつめの質問は、増田さまが推す設計

モンスター.attackedBy(攻撃方法型: ジャンプ攻撃)

の方が良い利点はなんでしょうか。私は"時間的な処理の流れがわかりやすい"設計になりやすいことを主張しました(理由2参照)・

増田さまが考えるほかの利点があればぜひ教えてください。

2014-01-16

http://anond.hatelabo.jp/20140116080422

意見くださって、ありがとうございます

例にあげたRPGモデル設計http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1178047006を参考にしました。

ですが自分で書くとしても、十中八九そのような設計にするでしょう。

X
モンスター.attack(勇者.attackPower)
O
勇者.attack(モンスター)

だと主張します。その根拠を説明します。

理由1: 読みやす

主語.動詞(目的語)

英語の文のようで読みやすい。

理由2: 変更が分散しにくい

モデルに対する命令は、ふつう入力Controllerのイベントハンドラに書かれる。attackに関する命令はどちらの設計をとったとしても"攻撃が選択された"イベントハンドラに書かれるだろう。

モンスター.attack(勇者.attackPower)

だと、たとえばモンスターダメージを与える要素として、他に「すばやさ」などを加えようとしたら、モンスターのattackメソッドを変更する必要が生じる。

また、FF5には"竜騎士ジャンプ"という攻撃方法があった。ジャンプを選択すると、竜騎士空高く飛び、一定時間が経ったのちに落ちてきて、空中からモンスターを攻撃する、というもの。これを実装しようとすると、入力Controllerのイベントハンドラの直前の部分で別スレッドに投げるなり、タイマーを起こすなりして処理したくなる。

味方同士の”連携攻撃”を実装しようとするとやはり、入力Controllerのイベントハンドラを修正したくなるだろう。

まり、悪い方の設計ではModel勇者」への設計変更の都合で、Modelモンスター」や入力Controllerを修正する必要が出てきてしまう。

勇者.attack(モンスター)

では、どの設計変更の例でも、勇者側を変更するだけで良い。この設計には、クラス間の責任の分担がはっきりするという利点がある。

あれ?それならリストは??

リストライブラリは、

list.append(elem)

でしょ。

リスト実質的にはオブジェクトというより、ただのデータ構造

リストやディクショナリ(連想配列)は便利な道具だが、これを前面に押し出し設計は複雑さを招く場合がある。

たとえば、"複数のモンスターの集まり"や"ソートされたリスト"を、リスト継承して実装しようとすると、とたんにドツボにハマる。

Onスライムが仲間を呼んだ(){

    スライムの集まり.append(スライム)
    スライムの個数ラベル.更新(スライムの集まり.length)
    if(スライムの集まり.length == 1 and スライムの集まり.get(0).name == "キングスライム"){
        スライム名前ベル.更新("キングスライム")
    }

}

スライムは8匹集まるとキングスライムになるので、最初の一行でスライムの集まりを変更して以降、スライムの個数を気にする必要があることに注意して欲しい。

上記のコードを洗練させるために、Modelスライムの集まり」で個数が変わった時にイベント"OnLengthChange"を発行するようにしても良いが、そのようにすると、lengthメソッド安全に呼べるのはそのイベントハンドラのみ、ということになる。プログラマに注意を促すために、lengthメソッドコメントにその旨を注意書きしておく必要がある。

"スライムの集まり"リストクラスは、プログラマが期待するだろうリストクラスの振る舞い《append後の個数はappend前+1》を破壊している。

"ソートされたリスト"は、appendメソッドオーバーライドして、追加時に勝手に順番を並び替えてくれるようなものだが、これも《リストに要素を追加した時には、その要素は末尾に置かれる》というリストクラスの振る舞いを破壊する。スライムの例と同じように、イベントの発行とメソッドの注意書きが必要になる。プログラマはそれらのクラスを使うときに、より注意深くならなくてはいけない。

このように、クラスの振る舞いを壊してしまうようなクラス設計は、プログラマ地雷原におくりこむことになる。

時間的な流れの分かりやすさ v.s. 責任分担

はいえ、リストのようなデータ構造を便利に使えるシーンもある。

はじめからおわりまで100行程度のスクリプトで、とくにイベントリブンにするほどの複雑さがない場合。順序を追って読めるので、時間的な流れがわかりやすい。

一方、RPGの例で示したような設計は、オブジェクト指向をフルに使って、コード責任に応じて役割分担しやすくなる利点がある。

前者は"トランザクションスクリプト"的設計と呼ばれ、後者は"ドメインモデル"設計と呼ばれる。

よく利点・欠点比較され、それぞれを推進する派閥があったりする。

google:トランザクションスクリプト ドメインモデル

2013-12-20

PHP生産性の高さはやばい

自分JavaからPHPに入ったので、最初はとにかくクソな言語だと思っていた。

配列仕様がとにかくひどい。

Copy On Writeという謎仕様なのに加え、すべての配列が常に「順序付きマップ」という謎なものになっている。ただ単にマップ連想配列)を使いたいだけの場合でも、なぜが挿入順でキーが順序付けされているという仕様で、内部でどんだけオーバーヘッドがあるんだろうと考えると、それだけでストレスが溜まったものだ。

あと、何も宣言せず、$array['key1']['key2'] = 1としただけで、要素が1の配列が2つ作られる。これも気持ち悪い。この仕様のせいで、どれだけ見つけづらいバグが誘発されているかと思うと、それだけで痒くなった。

そんな風に思いながら、2年近く仕事をして、そこそこ大規模なシステムを一人で書けるようになった。それでも、PHPをやっている引け目のようなものはなくならなかった。

ただ、そんな思いのまま「意識が高い」系のプログラマが集まる会社に移り、RubyだとかPythonだとかScalaだとかが書けるようになった上で、あらためてPHPを見ると、その仕様が優れていることを痛感させられる。同じことを書こうとしたときの行数が圧倒的に少ないのだ。配列についてもたしかに、オーバーヘッドはあるが、実際にはパフォーマンスに影響することは少なく、それを通して得られる開発効率は半端じゃない。他の言語だといくつものライブラリを介して可能になることが、単にサーバファイルを置くだけで可能になるというのは、特に小規模なプロジェクトでは生産性の高さに直結する。

もちろん、プロセススレッドデータ共有の仕組みが前時代的で、パフォーマンス効率は高くない。言語仕様が、ノンブロッキングな処理に向いていないなど、用途によっては致命的な欠点もないわけではない。ただ、きちんと言語特性を理解するレベルで使ったことがないのに、仕様的に他の言語と変わっているところを挙げて、「PHPを使うのはレベルの低いエンジニア」というのは、そろそろ終わりにした方が良いと思う。

あと、話は変わるが「意識が高い系エンジニア」は、システムを開発する上で人件費採用コストの問題をあまり考えていないのではないかと思う。たとえば、ビジネスが急に大きくなって、取り急ぎ100人エンジニアを雇おうとなったときPHPならとりあえず書ける人間をかき集めやすい。RubyPythonで同じことをやると採用にかかる時間が大幅に伸びるか、人件費が大幅にアップするかになるだろう。これは、一緒に働くエンジニア所与の条件として見るか、お金を払って雇うべきダイナミックなものとして見るかの違うじゃないかと思う。

2013-08-02

Sails.jsを使ってpixiv検索サービス作った

Pixearch(ピクサーチ)

http://pixearch.net/

node.jsMongoDB勉強がてらpixiv画像タグ検索サービス作りました

はてブタグ検索のようにpixiv投稿された最近画像pixiv内でのブックマーク数でフィルタをかけて検索できるのが特徴です。

検索したり、タグをたどって、ダラダラと良い絵を眺めるのを目的としています

一応スマホからも見られるはず。

普段はブログを書いたりしてないので、今回学んだことのメモがてらの投稿です。

使ったもの

MongoDBを試そうと思ったのがサービスを作り始めた発端です。

Web部分はmongoDBと相性が良さそうなnode.js採用

MVCフレームワークで何か適当ものはないかとググってSails.jsが良さそうだったので今回採用しました。

ホスティング

今回はせっかくnode.js採用したので、噂のnode.jsPaaSのnodejitsuを試しに使っています

500MB分の容量のMongoDB最初から使えるのも大きかったです。

とりあえず最小のプランにしてるのでどのくらい捌けるのか気になるところ。

作ってみての感想
Sails.js

Web開発用のモジュール自分で用意するのがめんどいなー、という人向けな印象。

このくらいの規模のものだったらサクッと作れました。

ただある程度の規模のちゃんとしたサービスを作るのには色々足りてないので、自分カスタマイズしたりできる人じゃないと使うのは辛そうです。

後、ドキュメントも公式のものだけだと説明されてない機能結構あったりします。

デフォルトだとDBMySQL対応していて、MongoDBを使うにはsails-mongoを入れる必要がありました。

開発中に困ったことは、nodejitsuで動かそうとしてsails-mongoでエラーが出て、調べてみたらauthenticationに対応していないというバグがあったことでした。

手元で直したのでpull requestを送ろうかと思ったら既に他の人が送っていて、3日前ぐらいに取り込まれているので今は大丈夫なはず。

https://github.com/balderdashy/sails-mongo/pull/36

現在進行形で色々Issueが上がって修正がされているのでそのうちこなれてくるのに期待。

かいところで設計考慮がちゃんとされてるなーと感じたところも多かったので、node.jsで開発してる人は一回試してみると勉強になりそうです。

MongoDB

最初コレクション操作に戸惑ったのですが、結局JS連想配列なので思ったより早く馴染みました。

$setとか$gteとか特殊な意味を持つキーがいくつかあるので、その辺を把握できてから色々と捗りました。

MySQLに比べて特に更新系で複雑なクエリが発行できるので、ORMで使うと十全に機能を発揮できないのではないかな、と思ったり。

ご存知スキーマレスなので、何も考えずにデータを突っ込んでるとIntegerで保存したい値がStringになっててソートときに困ったりするので要注意。

指定した容量を超えたら自動で消してくれるCapped Collectionがあると知ったので、今回みたいな容量が限られてる場合に便利かなと試してみたのですが、このオプション有効にしたコレクションだとデータアップデートや削除ができなくなりました。

おそらく、追加しかしないログのようなデータの保存に使うもののようです。

nodejitsu

まだ微妙な部分も多かったですが、デプロイとかコマンド一発でできて、設定管理がpackage.jsonでできたりして面白かったです。

今回は、特に問題が起きたとき環境sshで入ったりできないので、表示されてるログだけで問題を調査するのに苦労しました。

MongoLabとMongoHQというMongoDBの外部ホスティングサービスが上述したように使えるのですが、無料の容量を超えて使うにはそこそこお金が掛かるのでモリモリ容量を使うものを考えている場合は注意がいります。(もちろん値段に見合ったプロダクトを提供してくれると思いますが)

ということで、せっかく作ったので是非試してみてください。

2011-03-20

より良いPHPerにならないための20Tips

http://1-byte.jp/2011/03/20/20_tips_you_need_to_learn_to_become_a_better_php_programmer/

良いPHPerだって?そんなものは丸めゴミ箱にでも捨ててしまった方が資源の再利用になる分いくらかマシだ。

つまり俺たちがしなくちゃならないことは「より良いPHPerにならないため」に何ができるかってことなのさ。

それじゃ、始めよう。

1. ?>を使うな

?>なんて使っちゃいけない。そう俺たちはBAD PHPer。

無駄ホワイトスペースの出力に悩まされるくらいなら対称性なんて丸めゴミ箱にでも捨てた方がまだマシだ。非対称性こそが賛美。

2. 設定ファイルPHPで書くな

require_once("config.php");

未だにこんなことやってるやつがいるのかいベイベー。絶対にダメだ。この一行を見たら俺は悶絶する。

ダメだ、早く何とかしないと。

大抵このconfig.phpの中身はこうなっている。見て絶望だ。

$hoge_path = '';
if (!LOCAL) {
    define('FOO_FLAG', 1);
    if (HONBAN) {
        define('HOGE_FLAG', 1);
    }
    else if (TEST) {
        define('HOGE_FLAG', 2);
    }
}
else {
    $hoge_path = '/local';
    define('FOO_FLAG', 2);
    define('HOGE_FLAG', 3);
}

define('HOGE_URL', $hoge_path.'/hoge/');

こういうのが延々と続くわけだ。もういやだ。もう見たくない。

本番環境テスト環境でどういう値の違いがあるのか、ローカル環境だとどうなるのか、まったく把握できる気がしない。

なまじPHPな設定ファイルのせいで処理をついつい書いてしまう。そしてどんどん複雑になってしまう。

やはり設定データは基本的にYAML等のデータしか定義できない形式のもので用意すべきだ。そして環境ごとに設定ファイルを分けるべきである

そうすることで何にどういう違いがあるのかすぐにわかるし、diffすれば一度にすべて把握することができる。

# 本番環境設定ファイル
foo_flag: 1
hoge_flag: 1
hoge_url: '/hoge/'
# テスト環境設定ファイル
foo_flag: 1
hoge_flag: 2
hoge_url: '/hoge/'
# ローカル環境設定ファイル
foo_flag: 2
hoge_flag: 3
hoge_url: '/local/hoge/'

3. コメントを信用するな

そう、あなたはこんな状況に遭遇したことがあるんじゃ?

// ここで後の処理のためにhogeメソッドを呼び出しておく
$q->foo();

// $a['foo']はここに来る時点で真のはず
// 2010-03-10 判定がおかしいので修正
// 2010-06-21 やっぱり値が入ってる方が正しい
if ( !isset($hoge[0]) ) {
}

コメント保守されない。そう、それは真実。こんなコメント発見したら即効削除しよう。コメントは基本信じるな。

俺たちにちょっとしたヒントと大きな損害を与えてくれる、それがコメントの役割なのだ。

4. タブとうまく付き合うしかない

わかる。いいたいはとてもわかる。俺たちはしばしばインデントにスペースを使うはずだ。一方でIDEのしっかりした言語ではタブも使うことがある。しかし悪いことに、両者を混同しているプログラマも一定数いるのだ。

タブを画面上で認識しにくいエディタが世の中には存在する(何とは言わないが)

そして画面上で認識しにくいことを理由にタブを気にしないプログラマがいる。

この二つの条件が重なると、タブとスペースの交じり合ったインデントが完成する。もうぐちゃぐちゃだ。これは永遠に続く戦いだ。

私たちが勝利を掴むためにできることなどせいぜい、常にスペースしか使わない。タブを見つけたらその都度スペースに変換する。そういった地道な活動が明日へとつながるのだ。

5. 変数名に時間をかけるな

われわれがプログラムをするとき、何に一番時間がかかってるか。実は変数の命名なのである。ここで拘り過ぎて時間をかけ過ぎては何も進まない。

御託はイイからさっさと書け、だ。しかしとはいっても変数名は重要。日頃からどういうときにどんな名前を使うかを決めておくといい。

そして変数名に型はまったく必要ない。型宣言のないPHPにおいて、型の変数名をつけること自体ナンセンスだ。

コンパイラ様に保証されてない状態での

$iNumber = 'aaa';

になんの意味もない。コメントを信じるなでも言ったが、これはプログラマを混乱させるだけの害悪なものだ。

6. 変数初期化場所

変数を使う前に初期化するのは、警告を出さないという意味でも良い癖だ。しかし具体的にどこでやるかが問題だ。

$foo = null;
$foo = $q->foo();

こんな初期化意味はない。よくあるのはやはり、if文で値を振り分けるケースだろう

$foo = null;
if ( $hoge ) {
    $foo = 1;
}
else if ( $bar ) {
    $foo = 2;
}

このとき初期化はとても有効だ。もしnullの初期化を忘れたまま$fooを使うと警告が出るが、ちゃんと初期化してるので出ない。基本中の基本だ。

7. 不正なら常に死ね

function getStatus() {
    $bReturn = false;
    if ($i == 2) $bReturn = true;
    return $bReturn;
}

(中略)

もし、何かしらの理由で、あなたの書いたif文が間違っていたら?

この書き方をしていれば、間違った値に対して、常にfalseが返る。

私たちが、PHPでsensitiveなデータを取り扱うなら、正しいデータ入力されるまでは、動かないコードを書くべきだ。

trueとfalseの条件がいまいち明確ではないが、本当に動かないコードを書けというのであれば以下のようにすべきだ

function getStatus() {
    $bReturn = false;
    if ($i == 2) $bReturn = true;
    else if ($i == 1) $bReturn = false;
    else throw new Exception("bad status! $i");
    return $bReturn;
}

中途半端にfalseを返して生存させる必要性はまったくない。今すぐ死ね

8. 連想配列キーアクセスする場合

単なる配列に対して数値をクオートで囲う必要はない。

連想配列キーを指定する場合だけ定数と間違わないようにクオートで囲まなければならない。そして逆に定数を使いたい場合はクオートで囲ってはいけない。

更に後世のプログラマ処理を見たときに、定数が使いたかったのか、文字列が使いたかったのかを明確にした場合はconstantを使うと良い。

// 定数のFOOを使うよということが明確になる
print $a[constant('FOO')];

9. echoよりもprintfを使え

もし、文字列変数の値と一緒に出力するときPHPではコンマの代わりにprintfを使うことが使える。

なぜ?コンマを使うよりも可読性がグッとあがるから

printf( “Hello, my name is %s“, $sName);

以下の代わりに上記のコードを使う。

echoHello, my name is “, $sName;

出力すべき変数が増えれば増えるほど、有効になっていく。とにかく迷ったならば、printfを使え、だ。

10. 三項演算子は一回まで

三項演算子はとても有効だ。しか優先順位に難があるせいで三項演算子ネストしようとすると以下のようなコードになってしま

$n = (($i == 1) ? 2 : (($i == 2) ? 3 :$i));

括弧だらけで読みにくいったらありゃしない。三項演算子を使うなら一回まで。約束守れないやつは丸めゴミ箱にでも捨てちまえ。

11. 真偽値のチェックは生でいけ

if ( $flag ) {
}

仕様をちゃんと把握しているなら真偽値のチェックなどこれで十分。

もし事前にbool型だというのが確定してるのなら「$flag === true」を使えばいい。

12. ++と--の演算子を見極めろ

インクリメント、デクリメント演算子は前に付くか後ろに付くかで意味が変わるので慣れるまでは非常にややこしい

けがからなくなるくらいなら初めから使わないほうが良い。見極められないなら使うな。それがPHPerなのだ。

13. 代入演算子を使え

文句なしだ。これは文句がない。

他にも色々あるので覚えておこう

$a %=  1;
$a &=  1;
$a |=  1;
$a ^=  1;
$a <<= 1;
$a >>= 1;

14. 変数dump関数はより便利に

てっとり早く画面に表示する際にpreはよく使うが、デザインの関係上画面の文字が見えないときがある。

なのでdivを使って以下のようにしとくと便利だろう。

function p($var) {
    echo "<div align='left' style='background-color:white;color:black;'><pre>";
    print_r($var);
    echo "</pre></div>";
}

15. 定数から手を洗え

君らが通常作るアプリケーションなんぞに、定数なんぞ必要ない。いいか、もう一度言う、お前ら程度のもんが、定数使おう何ぞ、おこがましいわ!

大丈夫。なんでもかんでも定数にする必要はない。結局設定ファイルに定数をずらずら作りまくってわけがからなくなってるパターンが多い。

貴様たいなもんに、定数は制御できん。いいか設定ファイルYAML等のデータで持つようにし、その連想配列データ構造を一つ持ってるだけで定数の変わりになる。

このメリットに比べれば、定数だと書き換えられなくて良いという利点などこの歯のカスほどのものだ。そんなものは丸めゴミ箱へ捨ててしまうといい。

認識を改めろ。俺たちはより良いPHPerにならないために努力している。

16. $_GETと$_POSTを生で使うな

できれば何かしら簡単なクラスでもいいのでラップしろ。

class Request {
    private $parameters;
    private $method;
    function __construct () {
        $this->method = $_SERVER['REQUEST_METHOD'];
        if ( strtoupper($this->method) === 'POST' ) {
            $this->parameters = $_POST;
        }
        else {
            $this->parameters = $_GET;
        }
    }
    function param ($key) {
        return isset($this->parameters[$key]) ? $this->parameters[$key] : null;
    }
}

これだけでもいい。たったこれだけでもとても便利だ。ここから拡張してGETやPOSTを明示的に取るメソッドとかも作ってみるといい。自分の手を動かすのだ!

17. 関数だのオブジェクトだのの問題ではな

例が良くない。こんなのは引数20個ある関数からset20回呼ぶオブジェクトに変わっただけではないか

そもそもこの20個の引数はなんなのか。何かのデータ構造なんであれば連想配列にして引数一つとして渡すべきだし、それぞれまったく異なる用途の変数なのであればWindowsプログラミングじゃあるまいし20個も引数取る時点設計が間違えている。

何がいいたいか。別に関数でもオブジェクトでもどっちでもいいということだ。

そんなことで悩んでる暇があったら設計を見直せ。

18. メソッドチェインを愛用せよ

スキあらば自分自身を返せ。スキあらばオブジェクトを返せ。配列はArrayObjectのARRAY_AS_PROPSで返せ。

ひたすらメソッドチェイン。来る日も来る日もメソッドチェイン。とにかくメソッドチェインを使い続けろ。そこに未来はある。

19. コードの汎用化は慎重に

どんなコードも繰り返すな。もし、少しでも同じコードを書いていたなら、それは関数に置き換えてしまえ。

・・・と、いうのはやめなさい。

一見同じように見えた処理でも前後の流れでまったく違うものということが往々にしてある。

まとめ方にも問題があるケースもある。何でもかんでも関数化すると、関数が膨大に増えていく。君は見たことがあるだろうか。common.phpやfunction.phpの恐ろしさを。

かに細かく関数化はされているが、適切に関数化していないのである。結合度が非常に高い。なんでもかんでも盲目的にまとめれば良いという話ではないのだ!

20. 結合度は適切に減らし、適切に結合せよ

あまりに極度に意識しすぎると、プログラムそのものができなくなる。そういう状態に陥る。

気を抜いて。そう気を抜いて。所詮あなたコードなんてすぐに消えてなくなるよ。きっともっと偉い人が作り直すよ。だからまずは思うが侭にやるといい。

結合度を減らすというのは非常に難しい何度何度も失敗し続けて、ようやくここは分けた方が良かったんだなと気付く。次に活かそうと心に決める。そしてまた同じ過ちを繰り返していくわけだ。

まずは実装することだ。これが一番の早道だ。まずはがっつり結合した関数をあえて作るといい。何も考えずに作ろう。

そしてその後に、一部分使いましたいとおもうことがあるはずだ。その時に関数に切り出そう。それを繰り返すといい。そのうち初めから分けた方が良いと気付く。

何事も経験が必要である経験を積まないプログラマ丸めゴミ箱に捨ててしまえ。

さて、先の例で言うならば、私ならadd_result_outputという関数を作ってしまうだろう。だってaddとresultを連続して呼ぶのはめんどくさいんだもん。一連の流れをいつも使うのなら、その流れをやってくれる関数を作ればいいじゃないか

function add_result_output ($iVar, $iVar2) {
    $r = add($iVar, $iVar2);
    echo result($r);
}

もっと言えばクラス化してしまってもいいかもしれない。どんな感じになるかは君の手を動かして確認しよう!

最後

このTipsはとてもわかりにくく、ニッチ過ぎる部分も多いかもしれない。

しかしもう一度タイトルを確認してほしい

あくまでも「より良いPHPerにならないための20Tips」なのだ。

君はこの記事を鵜呑みにしてはならない。PHPPHPと見抜けないPHPerはPHPを使うのは難しい

おまけ

もし、あなたPHPプログラマなら、公式のPHPドキュメントあなたのケツの穴を拭くための紙になるだろう。

私は、それぞれのセクションを眺めて、各関数でどんなことが出来るかなんぞ、歯クソのゴミ程に役に立たないとおもっている。動けばいい。はは。

あなたは、PHPで用意された既製関数で多くのことが実現できることに、(俺の仕事を減らすなと)驚くはずだ。

この記事があなたの役に立たない事を。

どんなコメントでも待ってます

ふざけんな!

個人的な感想

この記事に書かれている内容は、丸めゴミ箱に捨てた方が良いレベルです

もしここまで読んでしまったら、丸めゴミ箱に捨てましょう。

プログラ増田のあなぐら

2008-10-31

http://anond.hatelabo.jp/20081030235616

まあネタなんだけど(笑)

マジな要素を抽出するとしたら、

1から100までコードをべた書きするのが実行速度的には効率良いのだけど、

それを手で書くのはバカらしいので、

コンパイル時に一種のプログラムを実行してコードを生成しよう、という考え方。(メタプログラミングという)

コンパイル時にFizzBuzzをやってしまっておけば、実行時にはもうしないで済む。

この手のメタプログラミングC++のtemplate周辺が恐らく発祥の地で、

いまD言語がそれを色々発展させてる。

汎用的なデータ構造配列やら連想配列やらツリー)やアルゴリズムの実装とか、

普通にやると似たようなコードをたくさん書かなきゃいけない場合に使われる。

まあスクリプト系の言語だと「文字列コードとして実行」(evalとか?)という機能が割と普通にあるから、

スクリプトプログラマにはあんまり有り難味がわからんかもね……。

2007-12-02

プログラミングしたいがなあ

なんてワガママ言ってたら候補無いですな…たぶん

2007-11-08

60行テンプレートエンジンがパワーアップしてレイアウト機能に対応

前の60行テンプレートエンジンを改良して、レイアウトテンプレート機能を追加してみた(それでも全部で90行)。

レイアウトテンプレート機能とは、例えば個別のテンプレートが<table>...</table>を出力して、それをレイアウトテンプレートが<html><body>...</body></html>で囲って出力するとかそんなの。

詳しくは終わりの方のサンプルをみてくれ。

これは Ruby on Rails(とその仲間たち)にある便利機能のひとつ。

ついでにいうとSmartyにはない機能のひとつ。

今まで知らなかった人はぜひ試してくれ。チョー便利だから。

前回はたくさんのブックマークありがと。

コメントで「男前テンプレート」と名前がついてたので、勝手採用

名前がキモいっていわれるよ?でもそんなのカンケイネー

あと、これ以上の機能追加はしないので、各自勝手に改造して使ってくれ(そのためにコメントをつけてるから)。何でも人任せにするな。

コード

<?php
/*
 *  OtokomaeTemplate.php -- レイアウトテンプレートに対応した90行のテンプレートエンジン
 *
 *  - レイアウトテンプレート中で echo $_content; とすると中身が表示される。
 *  - テンプレート中で設定した変数レイアウトテンプレートで使うことが可能。
 *  - レイアウトテンプレート名をテンプレート側で指定することも可能。
 *  - 使い方:
 *      require_once('OtokomaeTemplate.php');
 *      $TEMPLATE_DIR    = 'templates';  // 省略可、パーミッションに注意
 *      $LAYOUT_TEMPLATE = 'layout.php'; // 省略可
 *      $context = array('title'=>'Example',
 *                       'list'=>array(10,'<A&amp;B>',NULL));
 *      include_template('template.php', $context);
 *  - 要 PHP 5.1 or later
 *  - ライセンス: public domain (自由に改造してね)
 */

/*
 *  設定用のグローバル変数
 */
$TEMPLATE_DIR    = NULL;   /* テンプレートを探すディレクトリ */
$LAYOUT_TEMPLATE = NULL;   /* レイアウトテンプレートファイル名 */

/*
 *  テンプレートを読み込んで実行する。
 *  $_context は変数名をキー、値を要素とする連想配列。
 *  $_layout はレイアウトテンプレートファイル名。
 *  - NULL または省略した場合は $LAYOUT_TEMPLATE を使う。
 *  - FALSE ならレイアウトテンプレートを使わない。
 *  - $_context['_layout'] = '...'; とすればテンプレート側でも指定可能。
 */
function include_template($_filename, $_context, $_layout=NULL) {
    global $LAYOUT_TEMPLATE;
    $_content = render_template($_filename, $_context);
    if (@$_context['_layout'] !== NULL)   // テンプレート側で指定された場合は
        $_layout = $_context['_layout'];  // それを使う。
    elseif ($_layout === NULL)            // 引数で指定されなかった場合は
        $_layout = $LAYOUT_TEMPLATE;      // デフォルトファイル名を使う。
    if ($_layout) {
        $_context['_content'] = $_content;  // レイアウトテンプレート中で使う変数
        $_content = render_template($_layout, $_context);
    }
    echo $_content;   // or return $_content;
}

/*
 *  テンプレートを読み込んで実行し、その結果を文字列で返す。
 *  include_template() の実体。
 */
function render_template($_filename, &amp;$_context) {
    $_cachename = convert_template($_filename);
    extract($_context);     // 連想配列ローカル変数に展開
    ob_start();
    include($_cachename);   // テンプレートを読み込んで実行
    return ob_get_clean();
}

/*
 *  テンプレートファイルを読み込み、convert_string() で置換してから
 *  キャッシュファイルに書き込む。読み込み時のロックは省略。
 *  (file_get_contents() もファイルロックできるようにしてほしいなあ。)
 */
function convert_template($filename) {
    global $TEMPLATE_DIR;
    if (! file_exists($filename) &amp;&amp; $TEMPLATE_DIR)
        $filename = "$TEMPLATE_DIR/$filename";
    $cachename = $filename . '.cache';
    if (! file_exists($cachename) || filemtime($cachename) < filemtime($filename)) {
        $s = file_get_contents($filename);
        $s = convert_string($s);
        file_put_contents($cachename, $s, LOCK_EX); // LOCK_EX サポートは 5.1.0 から
    }
    return $cachename;
}

/*
 *  テンプレートの中身を置換する。
 *  - '#{...}' を 'echo ...;' に置換
 *  - '%{...}' を 'echo htmlspecialchars(...);' に置換
 *  - ついでにXML宣言も置換
 */
function convert_string($s) {
    $s = preg_replace('/^<\?xml/', '<<?php ?>?xml', $s);
    $s = preg_replace('/#\{(.*?)\}/', '<?php echo $1; ?>', $s);
    $s = preg_replace('/%\{(.*?)\}/', '<?php echo htmlspecialchars($1); ?>', $s);
    return $s;
}
?>

サンプルPHPコード:

<?php
require_once('OtokomaeTemplate.php');
$TEMPLATE_DIR    = 'templates';
$LAYOUT_TEMPLATE = 'layout.php';
$context = array('list'=>array(10,'<A&amp;B>',NULL));
include_template('template.php', $context);
?>

レイアウトテンプレート(layout.php):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <body>
    <h1>%{$title}</h1>
    <div id="maincontent">
<!-- テンプレートの内容 -->
<?php echo $_content; ?>
<!-- /テンプレートの内容 -->
    </div>
  </body>
</html>

テンプレート(template.php):

<?php // レイアウトテンプレート名をテンプレート中で指定する場合 ?>
<?php //$_context['_layout'] = 'mylayout.php'; ?>
<?php // レイアウトで使用する変数テンプレート中で指定する場合 ?>
<?php $_context['title'] = 'レイアウトのサンプル'; ?>
<table>
<?php foreach ($list as $i=>$item): ?>
  <tr bgcolor="#{$i % 2 ? '#FFCCCC' : '#CCCCFF'}">
    <td&gt;#{$i}</td&gt;
    <td&gt;%{$item}</td&gt;
  </tr>
<?php endforeach ?>
</table>

出力例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <body>
    <h1>レイアウトのサンプル</h1>
    <div id="maincontent">
<!-- テンプレートの内容 -->
<table>
  <tr bgcolor="#CCCCFF">
    <td&gt;0</td&gt;
    <td&gt;10</td&gt;
  </tr>
  <tr bgcolor="#FFCCCC">
    <td&gt;1</td&gt;
    <td&gt;&lt;A&amp;B&gt;</td&gt;
  </tr>
  <tr bgcolor="#CCCCFF">
    <td&gt;2</td&gt;
    <td&gt;</td&gt;
  </tr>
</table>
<!-- /テンプレートの内容 -->
    </div>
  </body>
</html>

いくつか補足:

2007-10-30

60行で作るPHPテンプレートエンジン

唐突に、PHP用のテンプレートエンジンを作ってみる。

方針:

  • ふつうのPHPファイルテンプレートとして使う。
  • <?php echo $var; ?> は面倒なので #{$var} と書けるようにする。
  • <?php echo htmlspecialchars($var); ?> はもっと面倒なので %{$var} と書けるようにする。
  • ついでにXML宣言も <<?php ?>?xml ... に自動置換する。

【追記】レイアウト機能を追加してみた

コード

<?php
/*
 *  SixtyLinesTemplate.php - 60行しかないけどSmartyより速いテンプレートエンジン
 *
 *  使い方:
 *     require_once('SixtyLinesTemplate.php');
 *     $TEMPLATE_DIR = 'templates'; // 省略可、パーミッションに注意
 *     $context = array('title'=>'Example',
 *                      'list'=>array(10,'<A&amp;B>',NULL));
 *     include_template('template.php', $context);
 *
 *  ライセンス: public domain (自由に改造してね)
 */

/*
 *  テンプレートを探すディレクトリ。
 */
$TEMPLATE_DIR = NULL;

/*
 *  テンプレートを読み込んで実行する。
 *  $_context は変数名をキー、値を要素とする連想配列。
 */
function include_template($_filename, $_context) {
    $_cachename = convert_template($_filename);
    extract($_context);
    include($_cachename);
}

/*
 *  filename を読み込み、convert_string() で置換してから
 *  filename.cache に書き込む。読み書きのロックは省略。
 *  (file_{get,put}_contents() はファイルロックできるようにすべきだ。)
 */
function convert_template($filename) {
    global $TEMPLATE_DIR;
    if (! file_exists($filename) &amp;&amp; $TEMPLATE_DIR)
        $filename = "$TEMPLATE_DIR/$filename";
    $cachename = $filename . '.cache';
    if (! file_exists($cachename) || filemtime($cachename) < filemtime($filename)) {
        $s = file_get_contents($filename);
        $s = convert_string($s);
        file_put_contents($cachename, $s);
    }
    return $cachename;
}

/*
 *  テンプレートの中身を置換する。
 *  - '#{...}' を 'echo ...;' に置換
 *  - '%{...}' を 'echo htmlspecialchars(...);' に置換
 *  - ついでにXML宣言も置換
 */
function convert_string($s) {
    $s = preg_replace('/^<\?xml/', '<<?php ?>?xml', $s);
    $s = preg_replace('/#\{(.*?)\}/', '<?php echo $1; ?>', $s);
    $s = preg_replace('/%\{(.*?)\}/', '<?php echo htmlspecialchars($1); ?>', $s);
    return $s;
}
?>

サンプルPHPコード

<?php
require_once('SixtyLinesTemplate.php');
$TEMPLATE_DIR = 'templates'; // optional
$context = array('title'=>'Example', 'list'=>array(10,'<A&amp;B>',NULL));
include_template('template.php', $context);
?>

サンプルテンプレート

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>%{$title}</title>
  </head>
  <body>
    <h1>%{$title}</h1>
    <table>
<?php foreach ($list as $i=>$item): ?>
      <tr bgcolor="#{$i % 2 ? '#FFCCCC' : '#CCCCFF'}">
        <td>#{$i}</td>
        <td>%{$item}</td>
      </tr>
<?php endforeach ?>
    </table>
  </body>
</html>

出力例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Example</title>
  </head>
  <body>
    <h1>Example</h1>
    <table>
      <tr bgcolor="#CCCCFF">
        <td>0</td>
        <td>10</td>
      </tr>
      <tr bgcolor="#FFCCCC">
        <td>1</td>
        <td>&lt;A&amp;B&gt;</td>
      </tr>
      <tr bgcolor="#CCCCFF">
        <td>2</td>
        <td></td>
      </tr>
    </table>
  </body>
</html>

2007-10-14

速学Ruby railsを使うための最小限の知識

前置き

本講座ではほかの言語は触ったことがあるが、Ruby未経験者を対象にRailsを触るための最小限のrubyの知識を身につけるためのものである。

と、堅苦しく書いたけど仕事で急いでrailsを触れうようになるための最低限の情報だけをまとめてみたよ。

これを読んだだけではRubyプログラムを書けるようにはならないと思うけど、Railsを使った開発だといきなり何もできないってことはなくなるはずだよ。

肩の力を抜いて読んでほしいな。

基本のお話

変数宣言

Rubyでは特別に変数の宣言は必要ないんだ。

いきなり今まで宣言していない名前を使ってもおこらえないよ。

answer_to_life_the_universe_and_everything = 42 
# answer_to_life_the_universe_and_everything ????~A~S??~S????~H~]??~A????~Y??| ??~A~Y??~K??~B

なんて書いても問題ないんだ。

静的言語をやってきた人には型の宣言がないことに違和感を感じる人もいるかもしれないけど、Rubyでは型はあんまり気にしないんだ。

int x = HogeHoge.new

みたいな書き方では

変数xを用意してそこにHogeHoge.newを代入する。といった説明を受けたと思う。

でもRubyではちょっと違う考え方をするんだ。

x = Foo.new

だとFoo.new変数を用意して、それを名前に"束縛"するって説明することが多いよ。

ベルが貼られた箱を用意してそこに何かを入れるか、何かが入った箱を用意してそれにラベルを張るかの違いだね。後者のほうがちょっとだけ柔軟なんだ。

そして、柔らかいほうがRuby流って気がするよ。

でもそんなRubyでもちょっとだけ名前に関するルールがある。

大文字からはじまる名前はクラス

小文字からはじまる名前が局所変数

@からはじまるのがインスタンス変数

@@からはじまるのがクラス変数

ってルールだよ。

Railsだったらとりあえず@からから始まる名前にしておけばたいていは問題ないんじゃないのかな。

分岐分岐

JavaC言語ではif文なんかをよく使うよね。Rubyでもほとんど一緒だよ。

 if something_condition
  do_some_thing
elsif other_condition
  do_another_thing
else
  do_something_if_you_wont
end

もちろんelsifやelseが必要なければ省略することができるよ。

ほかにもRubyには便利な分岐の書き方があるよ。

so_some_thing if some_condition

みたいに後置でも書けるんだ。

どっちでも好きなほうを使えばいいと思うよ

関数宣言

関数宣言のとてもシンプルなんだ。

def func_name ( some_val_names )
  do_some_thing
end

といった感じでdef..endでくくるだけで簡単に関数を宣言できるよ

そして関数戻り値は最後に評価した値が自動的に戻り値になるんだ。

def baz 
  10
end

これは常に10を返す関数の例だよ。

Hash,Array

プログラムをしていて非常によく使うデータの型といえば配列(Array)や連想配列(Hash)だよね。

Rubyではそれらを簡潔に使うために専用の文法を用意している。

それが[]と{}だ。

arr = [1,2,3,"A","B","CDE"]         #??~M很~W宣訾@
has = {1 => "A",2 => "B", 3 => "C"} #??~O??~C????~CťΣ訾@
puts arr[3]
puts has[2]
A
B

とすれば簡単に配列ハッシュが宣言できるよ。

アクセス方法もほかの言語と大きく変わらないのが分かるよね。

2007-06-22

javascriptを理解するためのたった2つの大切なこと:改

9割ぐらいはハッシュ

何がハッシュなのか

javascript存在するほとんどオブジェクトの実体はハッシュだよ。

var arr = [0,1,2,3];

とかをみると配列(人によってはリスト)に見えると思う。でも実際は違うんだ。

これは

var has = {0:0,1:1,2:2,3:3};

と基本的には等価なんだ。ただちょっと束縛されているメソッド(インターフェイス)が違うだけ。

ためしに

arr[4] = 4;
arr['x'] = 'string';
arr[-1]  = -1;

としてみよう。

Firebugで確認してみると[0, 1, 2, undefined, 4]というような値がかえってくるよ。

でもarr[-1]やarr['x']の値は保存されてないのかな?そんなことはないちゃんとアクセスできるんだ。

それどころかarr.xで'string'がかえってくるんだ。

別の例を見てみよう。

var fx = function(){};
fx[0] = 'somestring';

こうするとfx[0]に'somestring'が束縛される。

つまりfunctionも一つのハッシュだったんだ。

これでほとんどのものがハッシュだということが解ってくれたかな?

ハッシュじゃないのは文字列とか数字とかそいういシンプルなものだけなんだ。

ハッシュへのアクセス

ハッシュへはhash[name]でアクセスすることが出来る。

それ以外にもname演算子や空白を含まなくて頭が数字でない場合はhash.nameでアクセスできるんだ。

これでhash[name]が関数だったらhash.name(args)とできるよ。まるでメソッドみたいだね。

関数のあれこれ

無名関数

関数には名前を付けなくても使用可能だよ。

function(x){return x+x}(2); -> 4
宣言文

関数宣言の書き方って次見たいの覚えてる人が多いんじゃないかな?

function funcname(args){ do something};

でもこれはsystax sugerだってことを知ってほしい。

上でも使ってるんだけど。

var funcname = function(args){ do something};

等価になる。もちろんどちらの書き方でもかまわないよ。

ただ、(無名)関数を宣言してそれをfuncnameに束縛しているっていうことを理解しておくと便利だよ。

スコープ closure

javascriptレキシカルスコープを採用してるんだ。

レキシカルスコープなんていうと難しく感じるかもしれないけど、結構単純なんだ。

var x = 'global';
var fx1 = function(){return x};
var fx2 = function(){var x = 'local';return x}

これの実行結果は以下になるよ。

fx1() -> 'global'
fx2() -> 'local'
fx1() -> 'global'

fx2の変数xはほかの場所に影響しないんだ。これは関数の中と外ではスコープが違うからなんだ。

でも以下のような場合に注意してほしいな。

var x = 'global';
var fx1 = function(){return x};
var fx2 = function(){x = 'local';return x}
fx1() -> 'global'
fx2() -> 'local'
fx1() -> 'local'

fx2は自分のスコープ変数xがないからその外側スコープに探しに出かけたんだ。

つまり宣言文varはそのスコープに新しい名前を登場させる機能なんだよ。

別の場合を見てみようね。

var x = 'global';
var fx1 = function(){
  var x = 'local';
  return function(){return x}
};
var fx2 = fx1();
x -> 'global'
fx2() -> 'local'

この例だとfx2()の値がlocalになってるね。

なぜなら外側スコープっていうのは関数を評価した場所じゃなくて、関数を定義した場所の外側なんだ。

関数は返り値として関数ハッシュを指定できるよ。

一つ上の例では実際に関数を返り値にしているね。

戻り値関数を指定するとこんなことが出来るよ。

var fx1 = function(){
  var x = 0;
  return function(){
    x = x+1;
    return x;
  }
};
var fx2 = fx1();
var fx3 = fx1();
fx2() -> 1
fx2() -> 2
fx2() -> 3
fx3() -> 1
fx3() -> 2

fx2とfx3の値が違うのはそれぞれ別の関数が作られるからだよ。

こんな風に関数が状態を持つことも出来るんだ。クロージャーとよんだりするよ。

関数ハッシュを使って複数の関数を返すとこんなことも出来るよ。

var fx = function(){
  var x = 0;
  return {
    'up':function(){
      x = x+1;
      return x;
    },
    'down':function(){
      x = x-1;
      return x;
    }
  }
};
var obj = fx();
obj.up(); -> 1
obj.up(); -> 2
obj.down(); -> 1
obj.down(); -> 0

最後に一度ハッシュについてもうちょっとだけ。thisのはなし。

thisという機能があるよう。ちょっとだけつかってみるね。

var x = 0;
var add = function(n){this.x = this.x + n; return this.x};
var mul = function(n){this.x = this.x * n; return this.x};
var obj = {'x':0,'do':add};
add(1);   -> 1
add(1);   -> 2
mul(2);   -> 4
obj.do(); -> 1
obj.do(); -> 2
obj.do = mul;
obj.do(); -> 4

thisは評価された場所によって値がかわるよ。

objの中にいるときはobj.xを扱うし、グローバルにいるときはグローバルのxを扱うんだ。

http://anond.hatelabo.jp/20070620200618を改訂してみたよ。

このぶんしょうがjavascriptを覚える上の一助になるとうれしいんだ。

あとここでつかってるハッシュ伝統的な意味連想配列のことね。

突っ込みも大歓迎だよ。

2007-04-13

ASPファイル受信

'/** Requestオブジェクトから受信したデータを取り出します。
' * @return byte配列を格納した連想配列を返します。
' */
Function getRequestItem()

  If Request.TotalBytes <= 0 Then
    getRequestItem = Null
    Exit Function
  End If

  Dim data
  Dim separator
  Dim dataArray
  Dim buffer
  Dim filePath
  Dim fileName
  Dim ix
  Dim stringIndex
  Dim myCrLf
  Dim items
  Dim itemName

  myCrLf = convertAsc(vbCrLf)
  Set items = Server.CreateObject("Scripting.Dictionary")

  data = Request.BinaryRead(Request.TotalBytes)
  data = LeftB(data, UBound(data) - 3) & myCrLf

  separator = MidB(data, 1, InStrB(1, data, myCrLf) + 1)

  dataArray = SplitB(data, separator)

  For ix = 2 To UBound(dataArray) Step 1
   '1行読み込み
    buffer = MidB(dataArray(ix), 1, InStrB(1, dataArray(ix), myCrLf) - 1)

   'アイテム名の取得
    stringIndex = InStrB(1, buffer, convertAsc("name="))
    If stringIndex > 0 Then
      itemName = convertUnicode(MidB(buffer, stringIndex + 6, InStrB(stringIndex, buffer, ChrB(34)) - stringIndex))
     Else
      itemName = ""
    End If

   'ファイル名の取得
    stringIndex = InStrB(1, buffer, convertAsc("filename="))
    If stringIndex > 0 Then
      filePath = MidB(buffer, stringIndex + 10, InStrB(stringIndex, buffer, ChrB(34)) - stringIndex)
      fileName = RightB(filePath, LenB(filePath) - LastInStrB(0, filePath, convertAsc("\")))
     Else
      fileName = ""
    End If

   'データ部の取得、改行コードの切り捨て
    buffer = RightB(dataArray(ix), LenB(dataArray(ix)) - InStrB(1, dataArray(ix), myCrLf & myCrLf) - 3)
    buffer = LeftB(buffer, LenB(buffer) - 2)

    items.Item(itemName) = parseBytes(buffer)
  Next

  Set getRequestItem = items
  Set items = Nothing

End Function

'/** 文字列の最後尾から指定文字を検索します。
' * @param Start 検索する開始文字位置を指定します。
' * @param String1 検索対象の文字列を指定します。
' * @param String2 検索する文字列を指定します。
' */
Function LastInStrB(ByRef Start, ByRef String1, ByRef String2)

  Dim ix
  Dim lastIndex
  Dim searchLength

  searchLength = LenB(String2)
  lastIndex = LenB(String1) - searchLength + 1
  If Start > 0 And Start < lastIndex Then
    lastIndex = Start
  End If
  For ix = lastIndex To 1 Step -1
    If MidB(String1, ix, searchLength) = String2 Then
      LastInStrB = ix
      Exit Function
    End If
  Next

  LastInStrB = 0

End Function

'/** アスキー文字列に変換します。
' * @param String 対象の文字列を指定します。
' */
Function convertAsc(Byref String)

  Dim ix
  Dim ascii

  ascii = ""
  For ix = 1 To Len(String) Step 1
    ascii = ascii & ChrB(AscB(Mid(String, ix, 1)))
  Next

  convertAsc = ascii

End Function

'/** Unicode文字列に変換します。
' * @param AscString 対象のアスキー文字列を指定します。
' */
Function convertUnicode(Byref AscString)

  Dim ix
  Dim unicode

  unicode = ""
  For ix = 1 To LenB(AscString) Step 1
    unicode = unicode & Chr(AscB(MidB(AscString, ix, 1)))
  Next

  convertUnicode = unicode

End Function

'/** バイナリ対応版Split関数です。
' * @param String 対象の文字列を指定します。
' * @param Delimiter 区切り文字を指定します。
' */
Function SplitB(Byref String, Byref Delimiter)

  Dim ix
  Dim lastIndex
  Dim searchLength
  Dim start
  Dim datas()
  Dim dataIndex

  dataIndex = 1
  start = 1
  delimiterLength = LenB(Delimiter)
  lastIndex = LenB(String) - delimiterLength + 1

 '最初から1文字ずつ繰り返します。
  For ix = 1 To lastIndex Step 1
   'データを比較します。
    If MidB(String, ix, delimiterLength) = Delimiter Then
     'データを取り出せたら配列に格納します。
      ReDim Preserve datas(dataIndex)
      datas(dataIndex) = MidB(String, start, ix - start)
      dataIndex = dataIndex + 1
      start = ix + delimiterLength
    End If
  Next

  SplitB = datas

End Function

'/** Byte配列を返す関数です。
' * @param data 対象のデータを指定します。
' */
Function parseBytes(Byref data)

  Const adLongVarBinary = 205
  Dim recordset

  If LenB(data) <= 0 Then
    parseBytes = CByte(0)
    Exit Function
  End If

  Set recordset = Server.CreateObject("ADODB.Recordset")
  recordset.Fields.Append "UpLoadBinary", adLongVarBinary, LenB(data)
  recordset.Open
  recordset.AddNew
  recordset.Fields("UpLoadBinary").AppendChunk data
  recordset.Update

  parseBytes = recordset.Fields("UpLoadBinary").Value
  recordset.Close
  Set recordset = Nothing

End Function
ログイン ユーザー登録
ようこそ ゲスト さん