はてなキーワード: クロージャとは
そういうのはバッドノウハウとは言わないの?
関数ポインタをバッドノウハウとは言わないでしょ。C言語自体がバッドノウハウの結果だと言うなら、当たりだけど:)
手続き関数という抽象はまことに一般的な存在で(数学では汎関数というのもある)、それをC言語で直接的に表現したのが関数ポインタ。 関数的なものを オブジェクト指向言語でオブジェクトとして実装するほうがバッドノウハウだと思う。 少なくとも私は JavaのComparableインタフェースよりも C言語の heapsort/mergesort/qsort の関数引数 int (*compar)(const void *, const void *) のほうがシンプルで どちらかといえば本質をよりよく表していると思う。 なぜ関数的なものを表現するのに オブジェクトとかinterfaceとか継承とか「余計な概念」を導入する?それこそバッドノウハウでしょ。 まあでもC言語にはクロージャが無いから、関数的なものも扱いづらいことこの上ないが、Cにクロージャが無いこと自体はバッドノウハウとは言わないでしょー。
逆も然りで、オブジェクトを表現するために 関数を使ってれば そればバッドノウハウだけど、オブジェクトは関数ほど一般的な概念ではないと思う(オブジェクトなんか無くても別にいい、かも?)。
唐突にClass::Data::Inheritableのソースコードについて説明してやんよ。
使い方とかの説明はこの辺でも読んでから出直して来い、ごるぁ!
まぁとりあえずソース見てみろ、下記にはっつけてやっからよぉ!
1: package Class::Data::Inheritable; 2: 3: use strict qw(vars subs); 4: use vars qw($VERSION); 6: $VERSION = '0.06'; 7: 8: sub mk_classdata { 9: my ($declaredclass, $attribute, $data) = @_; 10: 11: if( ref $declaredclass ) { 12: require Carp; 13: Carp::croak("mk_classdata() is a class method, not an object method"); 14: } 15: 16: my $accessor = sub { 17: my $wantclass = ref($_[0]) || $_[0]; 18: 19: return $wantclass->mk_classdata($attribute)->(@_) 20: if @_>1 && $wantclass ne $declaredclass; 21: 22: $data = $_[1] if @_>1; 23: return $data; 24: }; 25: 26: my $alias = "_${attribute}_accessor"; 27: *{$declaredclass.'::'.$attribute} = $accessor; 28: *{$declaredclass.'::'.$alias} = $accessor; 29: } 30: 31: 1;
短いソースだなーこれ。でもな、なめんじゃねーぞ。短いけど色々な技術が盛り込まれてんだよコレはよぉ。
ハイ、まず3行目。
かるくstrictについて説明してやんよ。心して聞けよオマエラ。
strictっつーのはだな、つまりPerlにおける曖昧な部分をすこーしだけチェックしてくれるスグレモノなんだなコレが。
とりあえずざっくり言うと三つの機能があってだな、下記のよーに書くわけだ。
use strict 'vars'; use strict 'subs'; use strict 'refs';
varsってーのは簡単に言うとmyとかourとか宣言しろボケってやつですわ。
subsは裸体は許さんってやつですの、$とか%とかついていない裸の文字列をエラーにしてくれんだよ。
refsってのが一番やっかいな代物でな、これはムツカシイ言葉で言うとシンボリックリファレンスってんだが、要は変数名に変数を使うとエラーにしてくれるってこったよ。
で、これら全部ひっくるめてuse strict;なんだな。わかったか?オラ!
ちゅーことはだ、3行目を見ると意図的にrefsだけ外してるのがわかるよな。
つまりコレはこのコードのどこかで変数名に変数を使うってことを明示していることにもなるわけだ。けけけ。
あーもういいもういい、次だ、次。
4,5行目を見てみろよ。今時our使わずにuse vars使うなんてどんだけー。
ははは、まぁまてよ。
ourってのは明示的にグローバル変数を定義するもんなんだが、このourってやつが導入されたのがPerl5.6からなんだよ。
Perl5.5のころはourなんてなかったからグローバル変数定義すんのにこのuse varsを使っていたわけだ。
つまりこのモジュールはPerl5.5環境でも動くように配慮しているわけなんだな、ちゃんちゃん。ほほほ。
あーもう全然すすまねーよ。チクショウ、が、ま・・・・。
で、11-14行目。これはref関数使って$declaredclassがオブジェクトだったら死ぬって処理だ。
require CarpっつーのはCarpモジュールを動的にロードしてるっていうことだよぅ。
で、Carp::croak関数使ってエラー文はいて死ぬ、と。ちなみにこのCarp::croakってはまぁdie関数みたいなもんなんだ。
違いとしてはエラーの発生した原因を呼び出し元の奴のせいにして自分は悪くないんだよってアピールすることかな。まぁ実際使ってみりゃわかるよ。
さぁ、16行目。本編突入だ。長かった。長い道のりだったなお前ら。
sub {}ってのは無名サブルーチン(関数のリファレンス)ってやつだ。で、ここで注目すべき点はただひとつ!!!!!
19-23行目あたりをぼーっとみてると$declaredclass, $attribute, $dataっていう変数を使用していることがわかる。
これらの変数は9行目で受け取ったmk_classdataへの引数だ。
ここで問題が発生する。
myで宣言された変数の賞味期限はスコープの終端だ。それはわかるな?
つまり9行目で宣言された$declaredclass, $attribute, $dataといった変数どもは29行目のスコープの終端で消滅してしまうわけだ。
しかし!その消えてしまうはずの変数どもをsub {}という無名サブルーチンの中で使用してしまっている!!!
これが世間一般に語られているクロージャという仕組みなのだ!!!!!!うはははははははh!!!
本来生涯をまっとうするはずだった変数たちが別のサブルーチンの中にまぎれてしまうとその別のサブルーチンが消えてなくなるまでは死ぬことを許されなくなるのである!!!ざ・不☆老☆不☆死!
なんたる奇妙奇天烈なことであるが、この現実を受け入れることによってお前らの道が開けるんだ!!!すげーだろぉがよぉ!!
ボクはッ、キミがッ、クロージャを受け入れるまでッ、殴るのをやめないッ!
さて、肝心の16-24行目のアクセサ部分の処理の解説だけども、
引数が渡されてなければ特になんの処理もせずに$dataを返している。$dataってのは死ぬことを許されなくなったカワイソウな変数君だ。
つまり、Class::Data::Inheritableってやつはアクセサに渡された値をどこで保存してるのかというと、紛れも無いこの$data君に他ならない。
$data君がニート君になっちゃうとたちまちデータの読み書きができなくなるのであまり働かせ過ぎないように注意しよーね!
ハイ、次はアクセサに引数が渡された時の処理だけどな、20行目を見てみろ。$declaredclassに格納されてる値はmk_classdataメソッドを使用したときに格納された値になる。
package Hoge; use base qw/Class::Data::Inheritable/; Hoge->mk_classdata('hoge_accessor');
つまり上記の処理で例えると、$declaredclassには'Hoge'という文字列が入ってることになんだな。
で、この'Hoge'と$wantclassに入ってる値を比較しているわけだが、
package Hoge; use base qw/Class::Data::Inheritable/; Hoge->mk_classdata('hoge_accessor'); Hoge->hoge_accessor('aaa');
上記の処理で例えると$wantclassには$declaredclassと同じく'Hoge'が入ってくることになんだな。うっひょー。
んで、20行目のif文は$wantclassと$declaredclasが違う場合にだけ19行目の処理を実行しているわけだからこの場合はスルーするわけだぁ。ひょひょひょ。
じゃあだな、$wantclassと$declaredclasが違う場合ってどんな場合?ってことだが、下記に例を示すから目ん玉引ん剥いて網膜から直接見てみろよこのボケ野郎どもが。
package Hoge; use base qw/Class::Data::Inheritable/; Hoge->mk_classdata('hoge_accessor'); package Foo; use base qw/Hoge/; Foo->hoge_accessor('bbb');
HA!HA!HA!こういう場合だよ米ベー。$wantclass=Fooで$declaredclas=Hogeになるんで19行を実行し、Fooをベースにしてmk_classdataを呼ぶことでFooに同じ名前の新たなアクセサを提供し、元クラスHogeの値を壊さないようにするわけですなぁ。
考えた人すごいですなぁ。これがClass::Data::Inheritableが継承可能なクラス変数といわれる由縁でするまする。
で、最後の26-28行目はコレらの便利な処理をしてくれる$accessorさんをクラスに登録するというわけですよぉ。
27,28行目の*ってのは型グロブ変数ってという奴で、型グロブに対して無名サブルーチンを突っ込むと動的に関数を定義できるんだなぁコレが。
でここで、初めに俺が語った話を覚えてるか?へっ、オマエラなら覚えてないだろうなけっけ。use strictの話だよ。refsだよrefs。
ここでrefsを省いていたのが利いて来るんだ。refsって何だった?ホラ言ってミソ?
で良く見てみると型グロブ変数に対して「$declaredclass.'::'.$attribute」っていう変数を使おうとしているよね?これをしたかったからrefsだけ仲間外れにしてたわけですね。
はは。
あー、あー、あー。
これで終わりだよぅ。みんなわかったかな!?
コレ読んでもわからんやつはもう死ぬか、もしくはわからん用語について死ぬほど調べてもっかい読みなおしてみろこのド低のぅッ・・・ごふんごふん、このクサレ脳みそがぁ!!!!!!!!!!!!11
小飼さんにまでご意見いただいて恐縮です。 でもトラバ全無視で新エントリを書いちゃう。
仮に子供の頃プログラミングに目覚めたとしても、その時点では打ち込んだゲームのコードを「追う」ことは出来ても、データ構造もアルゴリズムも「理解」するのは難しいだろう。ポインターもクロージャーも、むしろ大人になってからの方が理解は早い。一度面白さに目覚めてしまえば、今や教科書だって「実習環境」だっていくらでもある。慌てる必要は全くない。むしろ「老後の愉しみ」ぐらいでちょうどいいぐらいではないだろうか。
やっぱりそういうものなんでしょうね。 学ばせるなら大学とか専門学校とかでいくらでもありますし。
プログラム言語は、子供が触れてみて面白いというものではなくなった、ということで…
PC でなにか作るなら、ほかに絵とか音楽とかブログとかなんでもありますもんね。
ブクマのコメントみたら LEGO mindstorm とかロボットというご意見もいただきました。 ちょっと敷居高そうですが、楽しそうです。
。
プログラマが枯渇云々というのは、べつに日本にしかプログラマがいないわけじゃないし、どこへでも求められますので気にしちゃいないです。
これから就職活動するバカはいないだろうけど、そういう人もいるだろうから少し書いておこう。
どちらかというと、アンチMS派なUnix技術者がWindowsだけの世界で仕事をする辛さを。
Unix技術者は、業務実績にSolaris/AIX/Linuxって書いてあってもちゃんと質問しろ。Windowsの仕事は無いですよね?って。
僕が食べるために職を手にしているこのIT業界というのは、バッドノウハウとMicroSoftとExcelで出来ている。
その為、僕が手にしたUnixの知識は、特定の仕事以外でしか役に立たないし、使わない。
viだろうが、TeXだろうが、Xの知識よりも、MFCとVBAのちょっとした知識のあるヤツが上にみられる。
ExcelとWindowsの知識があればそれだけで仕事になるからだ。
いいか、viやTeX、Xなんて捨てちまえ、Excelがあればそれでいいのだ。
MSでは、ActiveXを使ってCOMを操作し、クライアントのレジストリを操作し、IE単体でできないことをやってしまうヤツがハッカーと思われている。
VBAマクロで作ったなんちゃってツールを3時間で作れるほうが、
perlやruby/pythonで、より少ない時間で作ったツールよりも凄く思われてしまう。
そして、それができるヤツの方が、Unix技術者よりもよりハッカーであり、技術力があると思われている。
ブラウザを例にしたが、
javascriptでalert/confirmを出すよりも、vbscriptでMsgBoxの方が多くのことができるから、
javascriptでNumberの計算よりも、vbscriptでDecimalを使った方が倍密度の計算ができるから、
vbscriptを駆使できるヤツは、凄く重宝される。
いいか、javascriptで汎用的に書くのなんてナンセンスだ。javascriptなんて捨てちまえ、覚えるのはJScript実装(WSH)だ。
この業界、何が不満になるかというと、
MSの、もっというとWindowsのことしか知らないヤツが多すぎるということ。
そういうヤツらは、Windowsだったらこんなこともできるのに、なぜUnix/Linuxだとこんなこともできないのか。と言う
そういうヤツらは、Windowsの未修正バグの合間を縫いながら中途半端な実装しかしない。
だって、中途半端(もしくは大雑把)な実装で動いているものの中で動くから。それ以上に実装しようとしてもできないのだ。
いいか、win32のメッセージングの仕組を覚えるんだ。無理矢理send_keyみたいなコードを書けるようにしろ。
コマンドを連結するよりも、結果に近いコードを書くんだ。線形になろうがヤツらは気にしないだろう。
何故か。
それは、.NETで作ればお客さんの要望が実現でき、Excelと連携できるからだ。
ヤツらは、C/Sの世界でこそ役に立つ技術者だが、Webの世界に連れてきてはならない。すぐに実装がIEだけになる。
ヤツらにLLを覚えさせるのは無理だ。
クロージャなんて知らないし、高階関数やカリーなんてコードを教えてみろ。後から辛くなるのは自分だ。
ヤツらにはPHPを教えておけ、それだけで満足する。すごいヤツになった気にさせれる。
バッドノウハウ慣れしているヤツらはそれを使ってコードを書いてもらえ、rubyで書かせるよりも修正が20倍楽だ。
いいか、まとめるぞ。
今まで一生懸命Unixを勉強してきたのは無駄だ。いますぐ忘れるんだ。
Excelを今から覚えろ。VBAを覚えろ。そしてMSの動きを身に着けるんだ。
Windowsでは単位がFormだ。それが標準出力と標準入力と思え。ときどきSheetとかWorkbookになるぞ。
ストリームやファイル操作には気をつけろ。Unixの気分でいると思わぬところで抜けが出るぞ。
IRCは使うな。Jabberを使うな。メッセンジャーを使え。移行のお薦めはGaimだ。Windows版がある。
viの使用頻度を減らせ、変なコマンドを身に着ける前に、秀丸マクロを書けるようにしろ、Notepadのショートカットを覚えとけ。
BindとかApache(Httpd)の知識はいらない。IISだ。ActiveDirectoryだ。
文字コードはCp943cを何がなんでも押せ。Shift_JISっていう大雑把な伝えかたはダメだ。絶対cp943cにしろ。UTF8/UTF7との格闘で身も心もぼろぼろになるぞ。
汎用性なんて無いんだ。Windowsというプラットフォームがあれば。
ああ、心が渇いていく。
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に束縛しているっていうことを理解しておくと便利だよ。
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という機能があるよう。ちょっとだけつかってみるね。
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を覚える上の一助になるとうれしいんだ。
あとここでつかってるハッシュは伝統的な意味。連想配列のことね。
突っ込みも大歓迎だよ。
「ブロック付きメソッド呼び出し」がわからん、ということでいいのかな。この概念は是非とも解ってほしいので、今日始めて Ruby を触った俺が頑張って解説しよう、と思ったけれど、いいドキュメントを見つけたのでリンクしておくよ。
これで解らんかったらOn Lispを途中まで読みんさい(お金がないならWeb 版をどうぞ)。「ブロック付きメソッド呼び出し」は元々関数型言語の界隈で「レキシカルクロージャ」と呼ばれるもので、要するに中身は一緒なのでクロージャが解れば「ブロック付きなんたら」も解る(Ruby を触ったことのない自分が「ブロック付きなんたら」を理解しているのはこれの為)。 On Lisp は Common Lisp という言語の本なんだけど、 Ruby は言語仕様の多くの点で Common Lisp を参考にしているので、勉強するのはそれほど難しくないと思う(つまり見た目はヘンテコだけど中身は Ruby ってこと)。
元記事:
http://www.arclamp.jp/blog/archives/marulec2006_2.html
http://d.hatena.ne.jp/t_yano/20061216/1166296467
こんな感じですか?
public interface Executable <T> { void execute(T argument); } public class IterationExecutor<T> { private Collection<T> collection; public IterationExecutor(Collection<T> collection) { this.collection = collection; } public IterationExecutor(T[] array) { this.collection = new ArrayList<T>(); Collections.addAll(this.collection, array); } public void each(Executable<T> e) { for(T element: collection) { e.execute(element); } } } public class IntegerBuffer { private int value = 0; public IntegerBuffer(int value) { this.value = value; } public void add(int number) { this.value += number; } public int getValue() { return this.value; } } public class Sample { public static void main(String[] args) { final IntegerBuffer total = new IntegerBuffer(0); new IterationExecutor<Integer>(new Integer[]{1,2,3,4,5}).each( new Executable<Integer>(){ public void execute(Integer it) { total.add(it); } }); System.out.println(total.getValue()); } }
importなどは省略しました。
Sample以外は再利用可能なので……いやどうだろう