東方弾幕防衛が面白かった日記(と少しの攻略)

にじいろひつじさんの『東方弾幕防衛』がクッソ面白くて寝不足になったので日記
http://www.ac.auone-net.jp/~d-hituzi/

まずは紹介

これは東方の二次創作ゲームで,ジャンルはタワーディフェンス(TD),こちらからどこかに冒険に行ったり攻め込んだりするのではなく,こちらの陣地に敵が攻めてくるのを防衛するというゲームです.

有名ドコロだと『勇者のくせになまいきだ。』とか『ぱちゅコン!』とかですね.『東方弾幕防衛』の場合は,迷路のようになっているステージの入り口から,『魂』(宝みたいなもの)を奪いに東方キャラのゴーレムがやってくるので,こちらも東方キャラのゴーレムを配置して,魂を持ったまま入り口に返さないことが目的となります.

RPGでダンジョンを攻略しながら,「俺だったらこんな罠を作るのにな……」と思ったことのあるあなたにおすすめのゲーム.不用意に侵入してきた雑魚どもを隘路に誘い込み,ゆうかりんのマスタースパークで薙ぎ払ってあげましょう.上のサイトから体験版が落とせます.そしてそれにハマったらとらのあなに買いに走りなさい……走るのです……

内容について

ステージについて

どうにもならなそうなステージと楽勝なステージの差はかなりあるという印象.ステージの形が苦手なパターンだとすごい勢いで詰まりますが,楽勝ステージはらくらく一発突破です.そのバランスがな……逆にハマってしまうんだよな…….

ちょっと工夫するととたんに楽勝になるステージは脳汁出ますね.守矢神社(最終ステージ)の3とか4とかね……いやあまさかアレをああすればこんなに楽になるなんて思いませんでしたよ.

ただどうしたら楽になるのかさっぱり分からんステージもちらほら.誰か攻略まとめてくれー.

ユニットレビュー
  • 魔理沙……基本にして要のユニット一号.まず近距離連射型は魔理沙しかいないので,大型の硬い敵は魔理沙じゃないとどうにもならないと思う.そして,より重要なのは通路を封鎖するお仕事.敵をうまく誘導できると難易度が桁違いに違う.技の一号である.
  • 霊夢……要のユニット二号.厄介な結界を無理やり剥がすのが仕事である.力の二号.大型早苗さんに立ち向かえるのは君しかいない!
  • アリス……いらない子.前線から離れたところで通路封鎖しつつ,射程を活かして攻撃もするみたいな立ち位置で時々役に立つことがあり,その瞬間は輝くのだが,それ以外の使い道がさっぱり分からん.足の早いユニット相手には攻撃を避けられている気がするんだがこいつ大丈夫か.
  • 咲夜……このゲーム1の重要ユニットだと思いますよ僕は.咲夜さんをどこに置くかがすべてのゲームですね.
  • パチュリー……そこそこ強い範囲攻撃キャラ.攻撃した弾幕が消えずに貫通し,密集している敵にまとめてダメージを与えられるので,基本的には狭い通路に立たせる事になる.通路の左右に立って炎を吐く姿は完全に狛犬.後半,若干息切れしていた.
  • 幽香……このゲームの華一号.地形とか関係ない射程無限のマスパで敵を蹂躙する.長い通路が出てくるステージはゆうかりんの庭である.この極太レーザービームをなんと呼ぶかで世代がわかる気がしますね.僕は超電磁式榴散弾重砲でした.
  • フランドール……このゲームの華二号.レーヴァテインをぐるんぐるん回すだけの簡単なお仕事です.ゆうかりんに比べれば地味だけど活躍の度合いはこっちが上という気がします.
  • にとり……? 知らない子ですね

攻略のコツ

やってみて分かった攻略のコツを並べてみます.誰が見なくてもまたプレイする時見返すこともあるでしょう.

  • これは手筋を決めておいて淡々と実行するゲーム.敵の出現パターンは決まってるっぽいのでいつ何をやるか決めておくこと.
  • ユニット配置に必要なPOWERは限られているので,ひとつのユニットになるべく複数の仕事をさせること.
  • 必勝パターンは,咲夜とパチュリー,咲夜とフラン,あるいは幽香をまとめて投入したキルゾーンを作っておいて,どんな敵にもそこを通らせるパターン.マーダーホレスを作れってことだな? と思ったあなたはおっさん.
  • フランはコの字型通路に,幽香は長い通路に置いた時最大の効果を発揮する.どちらでもない通路の場合はパチェでいいんじゃないかな.必ず咲夜さんを併設すること.
  • 封鎖のコストはたいていキルゾーンを2つ作るより安いと思う.
  • キルゾーンを軸に考えると,ユニットは大きく分けて3種類.キルゾーンの往路で死ぬ雑魚,往復か往復+αでようやく殺せる大型か速いユニット,そして大型や速いユニットにキルゾーン突破能力を与える早苗さん.
  • キルゾーンをどこにするか,そこで殺しきれないタイプの敵をどう殺すか,というのが各ステージごとの戦術になる.
  • 誘導がほぼ不可能なステージの攻略法,コレガワカラナイ.
  • 他のユニットに結界を張る緑のクソ野郎に対向するには,キルゾーンの攻撃力自体を上げてすり潰すか,出てきたときにすぐ結界を剥ぐかどっちかだと思う.
  • 金を貯めるには,キルゾーンを作り,雑魚が来ている間は新ユニットを配置せず,強化もせず,放置するとよい.キルゾーンを突破されそうになった時にだけ強化する.

汚部屋の片付けを考える

1. 動機

私はずぼらなので部屋が片付かない.
心構えを変えれば片付くよという記事があったが,私はずぼらなので心構えを変えないし,変えてもつづかない.


汚部屋に戻らないための心がけ
http://anond.hatelabo.jp/20140711114927


なにかがうまくいっていないとき,とにかく気合で解決しようというのは立派な人である.
私は立派ではないので,ぼんやりやっていても自然とそうならない仕組みが悪いんだと考える.クズである.
そんなクズの私が,部屋を汚部屋にしないために必要な仕組みについて考えてみた.


なお私の部屋はとても汚いので,今適当に考えたこの片付け理論が参考になるかどうかは不明である.

2. 片付いている,という状態についての考察

2.1 片付いている,という状態を静的にとらえるのはよくない

片付いている,ということを,ものがあるべき位置に収まっている,という静的な見方でとらえるのはよくないと思う.
なぜなら,ものは移動するからである.


たとえば,服ならクローゼットやタンスと,洗濯前のかご,洗濯機の中,洗い終わった後のかご,物干し竿,
という順に場所を移動して,最後にクローゼットやタンスに戻ってくる.
食器は食器棚から食卓に,食卓から流しに,流しから乾燥かごに,乾燥かごから食器棚に移動する.


服がクローゼットやタンスにある状態,食器が食器棚にある状態だけを「片付いている」とみなすと,
服や食器がそれ以外の状態にある時,いちいち片付いている状態に戻すまでの手順を考えなくてはならなくなる.
考えるのは面倒である.だから放置してしまう.


これを防ぐためには,前もってどうすればいいか決めておいて,考えなくても動かせるようにしておく必要がある.

2.2 「フロー」と「バッファ」

片付いている,ということは,「フロー」と「バッファ」で考えたらいいと思う.
先に定義してしまうと,あるものが片付いているというとき,それは,その物が定められた片付けフロー上のバッファの中にあることだ.


なんだか大げさなようだが,服の片付けで考えるとすぐわかる.
服が部屋の中を移動する流れ,それが『フロー』だ.


服がクローゼットかタンス,洗濯前のかご,洗濯機の中,洗い終わった後のかご,物干し竿,という順に部屋の中を移動するとする.
それ以外のところにおかれることはないだろうし,おかれていたら拾ってどこかに(たぶん,洗濯前のかごに)入れるだろう.
この服が部屋の中を移動する経路が『服のフロー』だ.


『フロー』上にはその物を置いておくための場所がある.これを『バッファ』と呼ぶことにしよう.
クローゼットやタンス,かご,洗濯機,物干し竿が『バッファ』だ.
ここから服があふれていたら,『あふれた服はバッファの中にない』.
あふれた服はその辺におかれることになる.片付いていない.

2.3 フローとバッファで片付けを考える

部屋を片付けたい,といったとき,その最終目標は,すべてのものにフローが定義されており(すなわち,使った後の行き場所がはっきりしており),
すべてのものが片付けフロー中のバッファに収められている(すなわち,すべてのものがカゴなり棚なりに入っている)ということだろう.
そして部屋を綺麗に維持したいといったときは,物がフローに乗ってバッファからバッファへ移動していく時,
バッファが溢れないように調整したい,ということになろうかと思う.


こう考えることの利点は,上の方でも言っているが,ものというのは動くということを取り入れている点である.


服はクローゼットの中に,食器は食器棚の中に,本は本棚の中になければ片付いていないというより,
服が洗濯カゴの中に,食器が流しに,本が机の上に積まれていたとしても,
それはクローゼットや食器棚や本棚へのフローの途上にあるバッファ中にあるから,いずれそこへと片付けられる.
だから片付いていると言ってもいい.そう考えたほうが部屋をつかう上では実際的だろう.


3. 実際の片付け

3.1 バッファが溢れる理由とその対策.

物がバッファの中から逃げ出すには4つの場合がある.


1つめはバッファそのものがない場合だ.
今手にあるものをどこに戻せばいいのかわからない場合,その辺(机の上とか)に放り投げられがちである.


これを防ぐ方法は,どんなものにも片付けのフローを用意してやることである.


2つめはバッファからバッファへの移動が滞り,バッファがいっぱいになる場合だ.
洗濯物でいっぱいのかご,ぎゅうぎゅうになった洗濯機,もう干す場所がない物干し竿,これらはいっぱいになったバッファだ.
これは,次にどうしたらいいかが決まっていなかったり,次のバッファに移すための作業をサボっていた場合に発生する.


これを防ぐ方法は,次になにをすればいいかを予め決めておいて,そして嫌になっても諦めてそれに従うことだ.
それ以外の方法は特にない.と思う.


3つめはバッファに入れるのが面倒な場合だ.
洗濯かごがないので脱いだ服をその辺にほかしておくとか,
ゴミ箱がないのでとりあえず机の隅に積んでおくとかいう状態がこれにあたる.


これを防ぐ方法は,ものが動く場所にかならずバッファを置いておくことである.
ゴミが出るところには必ずゴミ箱を置く.服を脱ぐ場所には洗濯カゴを置いておく.


4つ目はそもそも物がバッファに入りきらなかったり,バッファからバッファへの移動を阻害するほど多い場合だ.


これを防ぐ方法は物を減らすか,バッファをふやすことである.


部屋の中のバッファの容量が100であったとき,部屋の中に100のものを置くことはできない.
なぜなら,ものを移動させることがむずかしくなるからである.
クローゼットとタンスに服がぎゅうぎゅうで,カゴにも洗濯物にも服がいっぱい,
物干し竿は常時稼働率100%では,たぶん服の移動はすさまじくめんどうである.


ギリギリまで増やすのも良くない.そのギリギリに詰め込むのは苦労なので,必ずサボるからである.
物はなるべく増やさず,バッファには常に余裕があるようにしたい.


バッファがどうしてもキツイなら,バッファを広げることを考えよう.
大きい洗濯かごを買ったり,たくさん洗濯機を買ったり,物干し竿を買ったりしよう.
ネックになるバッファを飛ばすというのも手である.
物干し竿は狭い割に長く服を置いておかなくてはならない狭いバッファなので,
これをショートカットするために乾燥機付き洗濯機を買うという手がある.


3.2 部屋を片付ける手順

上記の考えにもとづいて部屋を片付け,片付いた状態を維持することを考える.


管理すべきものは少ないほうがいいし,バッファに余裕を持たせたい.
物を捨てよう.どんどん捨てよう.
いつか要るかもしれないものは,多分一生要らないものだ.
本は電子化しよう.


次に,床や机の上に放り投げられがちなものから順に,そのものが乗るべきフローを考えよう.
私の場合,服と食器と本と小物,小さなゴミがこれにあたる.
うーんだいたい部屋にある全てだな.こいつクズだぜ.


フローを考えるときはなるべく手順を簡略化することを考えよう.
見た目に綺麗でも平日の自分は全くあてに出来ない.
なるべく楽ができた方がいい.


小物なんかは一つの箱に突っ込んでしまうのも手だ.
小物がなくなったとき困るのは,何処を探せばでてくるかわからなくなる時である.
少なくとも箱のなかにあるのだと確信できれば,混乱はだいぶマシになるはずだ.


そして,バッファが溢れないように物をフローに流すには,
どのタイミングでなにをすればいいかを考えよう.

机の上に積んだ本は一日の終りに必ず本棚に入れるとか,
食器は一日の終りに必ず洗って乾燥かごにいれるとかいうことを決めておく.


最後に覚悟を決めて,決めた時間には必ず決めたことをするようにしよう.
人間はゴミとウンコと憎しみしか作り出せない悲しい生命体である.
そのため生きている限り,自分で出したゴミは自分でどうにかせねばならない.
ゴミを片付けたらウンコは下水に,憎しみは水に流そう.


4. 余談

この片付けについての話をトラックと倉庫でやると物流の話っぽいよね.

だれか私の部屋の片付けを数理的にモデル化して最適化してくれ.


5. 最後に

こんな書いたとおりに部屋が片付いたら誰も苦労しねえんだよ!

自殺する人間の強弱に関わらず弱さは罪ではない.だが責任は取らされる.

こういう話題が出ると,事実より信念のぶつかり合いといった様相になるなと思った.

「自殺」に関して〈個人〉と〈社会〉の両側面から考えたい
http://yamayoshi.hatenablog.com/entry/2014/05/10/000117

自殺する人は弱い
http://grshb.hatenablog.com/entry/20140510/1399711443


これに絡む信念について書く.



私は弱い人間で,自殺したいと思ったことがあるし,今も自殺することを時々考える.


弱さとひとくちに言っても色々あるが,私の弱さというのは自分の意見や好みを主張できない弱さだ.
私は自分の意見や好みが他人に受け入れられるとあまり信じていないし,
自分の意見を主張することで軋轢が生まれるくらいなら,主張を引っ込めたいと思っている.
自分が正しいと思ったことでも,責任をとってまでそれを主張するかと言われれば間違いなくNOである.
なるべくなら,他人の求めに応じるだけの機械になれればいいと思っているし,
私よりちゃんと求めに応じて働ける存在がいたら,私の代わりにそいつが生きるべきなんだと思う.


ただ,従っているのは苦しい.なので能率は上がらず,それが劣等感を加速させる.
従っているのは苦しいのだが,かといって自分の意見を出すのも苦しい.
ジレンマである.そうしたジレンマに直面するのはつらいし,
ジレンマのどっちを選んでも,つまり,言うなりになっても,意見を言ってもつらいので,私は人間嫌いだ.


人間嫌いなのだが,一方で寂しいとも思う.
便利であるかぎりに必要とされるのではなく,無条件に必要とされたい,とも思う.
だが,そういう気持ちを押し出して引かれるのはつらい.これもジレンマである.
取りうる行動は3つで,

1つ目,感じのよい人間として振る舞ってストレスを貯め,
やっぱり便利であるかぎりにおいて必要なだけの人間なんだという気持ちを強化する

2つ目,距離を詰めて引かれる

3つ目,他人から離れる

どれもつらい.


こうしたジレンマ状況をどうにかするのは,理屈で言えば簡単で,
失敗してもいいから意見を出していけばよいはずである.
日本に人間は1億人いるわけで,その何人かから永遠に嫌われたとしても,
そこで得た教訓を次に活かすとか,気の合わない人は諦めるとかしていけば,
そのうち自分を出すこともできるようになるだろうし,
損得抜きで必要としてくれる人に出会うこともあるだろう.
これは確率の問題でしかない.コインの表が一回でも出ればいい確率の問題だ.


あるいは,「自分は必要ない人間なんだ」という思い込みが強いのが問題なので,
精神科なりカウンセリングなりに行って,まともな(一般的な)思考ができるように
薬なり訓練なりを重ねていけばいい.それでも自殺したくなった方はメンタルへ!


と,こんな感じで対策がはっきりしていても,私はそれを実行に移せないくらい弱い.


今,私はそれほど滅茶苦茶に深刻な悩みを持っているわけではない.それなりに暮らせている.
だから,今の生活よりもっとつらいことをしたくない,と思う.
ゆでガエルの論法なのだが,実際つらいのだからやりたくない.
人に合うと頭が働かなくなるとか,外にでる気力が湧かなくなるとか,
無理やり人と関わると吐かずにはいられなくなるとか,
そんな風に現状維持が現状を変えることよりつらくなったら何かやり始めるだろう.


精神科とかなら何もつらいことないじゃないかと思うかもしれないが,
「精神科とかカウンセリングを必要とするくらい,自分はダメな人間なんだ」
ということを認めるのがつらい.
私は弱い人間だが,どーにかこーにかここまでやってきた.
そのことをそれなりに誇っている.つらい思いをしてきたんだ,と思っている.
それを変えなくてはいけないとなったら,私は,
一時的にせよ,何も誇れるものがなくなってしまう.
つらい思いとかいうのは全部無駄だったんだという気持ちになってしまう.
それがつらい.



私はそんな感じで弱い人間で,もう生きているのが面倒で死にたいと思ったことがあるが,
死ぬのが怖かったのと,死んだあとの後処理をする家族のことを考えると死ねなかった.
別に家族が好きとかではなくて,家族の中で自分が果たしている役割を自殺という形で放り出した時に,
家族が受けるだろう精神的ショックを考えると,
「そんな精神的ショックを与える人間なんて居ないほうがよかった.必要としたのが間違いだった」
ということになりはしないか,と思えてつらかった.
打算からでも必要としてくれる人間を裏切るのはつらい.


なので,私は自殺というより,「自分が生まれてこなかったことにできないものか」とよく考えた.
「私より上手くやれて,そのことに疑問を感じない人に後を引き継いでほしい」ともよく考えた.



そんな感じで私は虚弱的な意味でも,意志薄弱で怠惰という意味でも弱い.
そのことでずいぶん自分を責めたのだが,そのときにこう考えた.
果たしてそれは罪だろうか.弱いことは悪いことだろうか.


罪ではない,と私は思う.
それはそのように生まれついたとか,そのように育たざるを得なかったから罪でないとか,
単に怠惰だから罪でないとか,そういう理由ではない.
ここでいう罪とは,神なり永遠普遍の善なりというものに照らしての罪,
宗教的な意味での,許されざる罪かということだ.
罪ではない,と私は思う.
神なり永遠普遍の善なりというものが存在しない,と思っているからだ.
私はただ弱いだけで,そこに宗教的な価値の上下はない.
だから,「弱いことは悪いことだ」という主張は,滅茶苦茶だと私は思う.そう言うお前は神じゃない,と思う.


ただ,責任があるかと言われれば,あるだろうと思う.
責任があるとかないとか,責任を取る取らないというのは,人間同士の関係のことだ.
そして,自分がやったことには自分が責任を持たなくてはならない,という漠然とした約束の上に
今の社会は成り立っている.そこに逆らうことは,社会に生きる人全員の心を変えない限り出来ない.
つまり,たぶん私が死ぬまでくらいは,私は自分のやったこと,やらなかったことに責任を取らなくてはならない.

永遠の基準にもとづくものではない責任は,場合や時代,雰囲気により重くなったり軽くなったりする.
どの程度の重さが適当であるか,ということについては,私は分からない.
ただ,責任を取らなくてはいけないだろうということははっきりしている.


責任をたくさん取らされるとき,しばしば「それが悪いから」と理由付けされるように思う.
しかし,それは,「都合が悪い」という意味だろう.「倫理的に悪い」わけではない.
みんなに都合が悪いから,お前が責任を取れと言われる.そういうことだろうと思う.


自殺する人を助けるべきかとか,自殺しそうな人を救うべきかという問題も,
やっぱり生きている人間の都合で決められるのだと思う.
そして,どの辺を落とし所にするかには議論の余地があり,
落とし所が良ければ救われる人がいるだろう,と思う.

自説を通したいと思えば,なるべく多くの人を自説に賛成させる必要がある.
そのはずなのだが,「お前の気持ちは分からない.私はこう考える」といって,
自分の感じるところだけを書くのは,自説を通したいんじゃなく,議論をしたいんじゃなく,
ただ言いっぱなしに言いたいだけなのだろうなと思った.


弱さは罪ではない.
だが責任は取らされる.
責任の重さは人が決めるものだが,
自分の信じる善悪だけを言いっぱなしにして,
交渉とか妥協の方に持っていくことがないのは残念だ.
言いっぱなしに言っておく.

英語嫌いと認知の歪み

          し!     _  -── ‐-   、  , -─-、 -‐─_ノ
  学 和    // ̄> ´  ̄    ̄  `ヽ  Y  ,  ´     )   和 え
  部 文    L_ /                /        ヽ  文  |
  生 が    / '                '           i  !? マ
  ま 許    /                 /           く    ジ
  で さ    l           ,ィ/!    /    /l/!,l     /厶,
  だ れ   i   ,.lrH‐|'|     /‐!-Lハ_  l    /-!'|/l   /`'メ、_iヽ
  よ る   l  | |_|_|_|/|    / /__!__ |/!トi   i/-- 、 レ!/   / ,-- レ、⌒Y⌒ヽ
  ね の   _ゝ|/'/⌒ヽ ヽト、|/ '/ ̄`ヾ 、ヽト、N'/⌒ヾ      ,イ ̄`ヾ,ノ!
   l は  「  l ′ 「1       /てヽ′| | |  「L!     ' i'ひ}   リ
        ヽ  | ヽ__U,      、ヽ シノ ノ! ! |ヽ_、ソ,      ヾシ _ノ _ノ
-┐    ,√   !            ̄   リ l   !  ̄        ̄   7/
  レ'⌒ヽ/ !    |   〈       _人__人ノ_  i  く            //!
人_,、ノL_,iノ!  /! ヽ   r─‐- 、   「      L_ヽ   r─‐- 、   u  ノ/
      /  / lト、 \ ヽ, -‐┤  ノ  キ    了\  ヽ, -‐┤     //
ハ キ  {  /   ヽ,ト、ヽ/!`hノ  )  モ    |/! 「ヽ, `ー /)   _ ‐'
ハ ャ   ヽ/   r-、‐' // / |-‐ く    |     > / / `'//-‐、    /
ハ ハ    > /\\// / /ヽ_  !   イ    (  / / //  / `ァ-‐ '
ハ ハ   / /!   ヽ    レ'/ ノ        >  ' ∠  -‐  ̄ノヽ   /
       {  i l    !    /  フ       /     -‐ / ̄/〉 〈 \ /!

英語が嫌いなんですよ.

読書猫『A rude awaking / 英単語の勉強法について 』
http://readingcat.hatenablog.com/entry/2014/03/23/040748

こういう記事を見て,なるほど英語は大切なんだなーと思うほどに,だんだん気分が沈んできて,英語が苦手で嫌いですとか言ってる自分には何の価値もないように思えてきて,英語の勉強サボってる俺は何の仕事もできないんだ,将来ワープアになるんだ,誰も幸せにできず誰からも疎ましく思われながら生きるしかないんだ,孤独なまま死ぬんだ,でも迷惑でしかない存在はでしゃばるより孤独な方がいいんだ……などと思いつめたあげくそんなつらすぎる未来図がつらすぎて,「おふとんだけは裏切らない」と言い出し,ふて寝してしまう,それくらい嫌いなんですよ.おふとんすら受け入れてくれるだけで助けてはくれないし,私じゃなくても受け入れるんですよ.まったく同じ顔で.それでも僕はおふとんを責めたりしない.僕にはおふとんしかないからです.だから責めたりしない.だが絶対に逃さない.死ぬまでだ.

メンヘラなんですよ.

つれーなーとか思ってたら認知の歪みについての匿名ダイアリーを見たんですよ
http://anond.hatelabo.jp/20140325112503

『認知の歪み』って言葉自体は知ってたんですが,別に日々反省はしてないんですよ.でも,英語を勉強しましょう!!!! とかいう量産型意識高い系記事よんでいちいち死にたくなるなんてなんて非生産的なんだ,でも実行に移さない俺はなんてクズなんだ.矯正されねばならない.っていうネガティブなやる気を出して考えてみたんですよ.

認知の歪みとは何でしょう.はい,困ったときのグーグルせんせーい

心理学COCOROの法則
http://rzt.sakura.ne.jp/shinri/001050/001125/

自動思考〜認知の歪み
http://sinrigaku.com/?page_id=1856

レポートだったら赤点ですね.人生に関わる思考を審議確かならぬネットの情報に基づいて行うってどんな気持ち? などの考えはさっくりと無視していきます.いずれその適当な生き方によって死ぬ.

えーまー第一歩の『英語ができないと仕事がない』とかいう考えにしてからが,二分割思考,過度の一般化,選択的抽出,占い,破局視,縮小視,情緒的理由付けなどがあてはまりそうですね.英語ができるできないというのにも幅があり,仕事にも幅があります.英語ができないと話にならない仕事,重大なビハインドになる仕事もあれば,必要とあれば通訳が付く仕事や,英語とかまるでさっぱり使わない仕事もあるでしょう.色いろある中から,英語ができないと不利な仕事ばかりを抜き出すのは論理的ではありませんね.英語ができないと仕事がないとは断言できないし,仕事は多分あるでしょう.未来を決めつけてはいけません.自分はダメだからきっと未来もダメになるとかそういう考えは正しくない.海外旅行に行った時,英語圏じゃなければ英語は通じるか微妙な感じでした.でもその人達は働いていました.

貧乏になって孤独に暮らして誰も幸せにできないんだーみたいな未来図も,肯定的な側面の否定,読心,占い,破局視,縮小視ですね.将来はどうなるか分かんないです.英語ができたらそりゃ有利ですが,英語以外にもおちんぎんに影響する要因はあるのに無視するのは良くない.他人を幸せにできるかどうかも,労働してればまずは立派に社会貢献ですし,関わる人をみなことごとく不幸にし,友達からは絶縁状をたたきつけられ,リプライを返せばブロックされ,オフ会に出たら二度と開催されなくなった,みたいな事実はなく,大盛り上がりはしないにせよ平和裏に続いている人間関係もあるので,それはそれで評価すべきです.

英語はそれだけでただちに未来を決定したりしないし,まして人間的価値を決めたりはしません.ただ,英語ができると色々捗るのは間違いないので,1個でも2個でも単語を覚え,1つでも2つでも英語の情報を仕入れられたら素敵やん? ってお話なだけです.意識高い系エントリを読むたび人生に絶望する必要はないし,必ずしも英語を勉強しなくてもいい.大英帝国を壊滅させて英語を世界から駆逐するという未来を選んだっていい.ロンドン橋は落とせ.歌のように.

そんなことを考えました.

いちいちそんなことを考えないと意識高い系エントリを読むたび落ち込んで,ありとあらゆる勉強が,生きているだけで社会の害悪であるゴミクズの自分をどうにか多少マシなクズにするための義務,みたいに感じられて,そんなに苦労するならもう何もしたくない,マイナスでしかない存在ならば死にたい,ゼロになりたい,でもゴミカスが死んだら迷惑だよなぁ,苦しんで生きるしかない,とか考えだすんですよ.

メンヘラなんですよ.

trivial-gray-streams についてのきわめて瑣末な記事

Gray Stream の提案と,その実装であるところの trivial-gray-streams について訳したよ.

まずはGray Streamsについて.参考サイトは以下のとおり.
http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html

FAILED Issue STREAM-DEFINITION-BY-USER ("Gray Streams")/ユーザ定義ストリーム(Gray Streams)

Issue STREAM-DEFINITION-BY-USER
参照 CLtL pages 329-332, 378-381, and 384-385.
関係する Issue STREAM-INFO, CLOSED-STREAM-FUNCTIONS, STREAM-ACCESS, STREAM-CAPABILITIES
Category ADDITION
Edit history Version 1, 22-Mar-89 by David N. Gray
Status の結果,標準には含まれないことになりました.

問題

Common LISP はユーザが,標準のI/O関数と使えるような独自のストリームを定義するための標準の方法を提供していない.このことは,Common LISPの標準のウィンドウシステムを開発するのを邪魔すると思われる.というのも,Common LISPには標準のI/Oがあり,また標準ウィンドウシステムの端緒があるが,それらを接続してポータブルなウィンドウシステムを作るためのポータブルな方法がないからである.

さらに,多くのアプリケーションでユーザは独自のフィルタストリーム,たとえばプリンタデバイスコントロール,レポート整形,文字コード変換,暗号化/解読とのためのストリームを定義したいと思われる.

提案 STREAM-DEFINITION-BY-USER:GENERIC-FUNCTIONS

概要

I/Oを処理するための総称関数の集合を定義する.それらの総称関数はストリームの引数によって特定されるメソッドを持っていて,既存のI/O関数もそれを使うようにする.ユーザは自分が定義したストリームがサポートされるように,総称関数にメソッドを追加することができる.

デフォルトのメソッドを書くために,ストリームのスーパークラスとなるべきクラスの集合も定義する.

クラス

下記のクラスはユーザ定義ストリームのスーパークラスとなるべきクラスである.これらのクラスは直接インスタンス化されることを想定しておらず,デフォルトのメソッドを提供するために使われる.

FUNDAMENTAL-STREAMがトップのクラス.これを直接に継承する FUNDAMENTAL-INPUT/OUTPUT-STREAM と, FUNDAMENTAL-CHARACTER/BINARY-STREAM の4つのクラスがあり,4つのクラスのうち2つを同時に継承する FUNDAMENTAL-INPUT/OUTPUT-CHARACTER/BINARY-STREAM という4つのクラスがある.

FUNDAMENTAL-STREAM FUNDAMENTAL-INPUT-STREAM FUNDAMENTAL-OUTPUT-STREAM
FUNDAMENTAL-CHARACTER-STREAM FUNDAMENTAL-CHARACTER-INPUT-STREAM FUNDAMENTAL-CHARACTER-OUTPUT-STREAM
FUNDAMENTAL-BINARY-STREAM FUNDAMENTAL-BINARY-INPUT-STREAM FUNDAMENTAL-BINARY-OUTPUT-STREAM
FUNDAMENTAL-STREAM [Class]

このクラスは STREAM および STANDARD-OBJECT のサブクラスである.STREAMP はこのクラスを継承しているインスタンスに対して真を返す.(他のものにも true を返すことがありえる)

FUNDAMENTAL-INPUT-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.これを継承していると INPUT-STREAM-P が真を返す.

FUNDAMENTAL-OUTPUT-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.これを継承していると OUTPUT-STREAM-P が真を返す.双方向ストリームは FUNDAMENTAL-INPUT-STREAM と FUNDAMENTAL-OUTPUT-STREAM の両方を継承することになる.

FUNDAMENTAL-CHARACTER-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.STREAM-ELEMENT-TYPE のためのメソッドを提供し,そのメソッドは結果として CHARACTER を返す.

FUNDAMENTAL-BINARY-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.このクラスを含めたインスタンス化可能なクラスは,STREAM-ELEMENT-TYPE メソッドを定義する必要がある.

FUNDAMENTAL-CHARACTER-INPUT-STREAM		[Class]

FUNDAMENTAL-INPUT-STREAM と fundamental-CHARACTER-STREAM を継承する.文字の入力のための総称関数のデフォルトの実装を提供する.

FUNDAMENTAL-CHARACTER-OUTPUT-STREAM		[Class]

FUNDAMENTAL-OUTPUT-STREAM と FUNDAMENTAL-CHARACTER-STREAM を継承する.文字の出力のための総称関数のデフォルトの実装を提供する.

FUNDAMENTAL-BINARY-INPUT-STREAM		[Class]

FUNDAMENTAL-INPUT-STREAM と FUNDAMENTAL-BINARY-STREAM を継承している.

FUNDAMENTAL-BINARY-OUTPUT-STREAM		[Class]

FUNDAMENTAL-OUTPUT-STREAM と FUNDAMENTAL-BINARY-STREAM を継承している.

文字入力

文字入力ストリームは, FUNDAMENTAL-CHARACTER-INPUT-STREAM を継承したクラスを定義し,以下の総称関数にメソッドを追加することで実装できる.

STREAM-READ-CHAR  stream			[Generic Function]

ストリームから1つの文字を読む.この関数は character オブジェクトか,ファイル終端に達したらシンボル :EOF を返す.FUNDAMENTAL-CHARACTER-INPUT-STREAM のサブクラスは必ずこの総称関数のためのメソッドを定義しなくてはならない.

すべての総称関数について, stream はストリームオブジェクトでなければならず,T や NIL は使えないことに注意.

STREAM-UNREAD-CHAR  stream  character		[Generic Function]

最後の STREAM-READ-CHAR の呼び出しを UNREAD-CHAR のようになかったことにする.NILを返す.FUNDAMENTAL-CHARACTER-INPUT-STREAM のサブクラスは必ずこの総称関数のためのメソッドを定義しなくてはならない.

STREAM-READ-CHAR-NO-HANG  stream		[Generic Function]

この関数は READ-CHAR-NO-HANG を実装するために使える.この関数は文字, NIL, あるいは :EOF を返す. NIL が返されるのは,入力が使えなくなった時である.:EOF を返すのは,ファイルの終端に達した時である.FUNDAMENTAL-CHARACTER-INPUT-STREAM によって提供されるデフォルトのメソッドは,単純に STREAM-READ-CHAR を呼びだす.このことはファイルストリームにおいては十分だが,インタラクティブなストリームは再定義をする必要がある.

STREAM-PEEK-CHAR  stream			[Generic Function]

peek-type 引数が nil であるような PEEK-CHAR を実装するために使われる.

この関数は CHARACTER あるいは :EOF を返す.デフォルトのメソッドは STREAM-READ-CHAR を呼び, STREAM-UNREAD-CHAR を呼ぶ.

STREAM-LISTEN  stream				[Generic Function]

LISTEN によって使われる.真あるいは偽を返す.デフォルトのメソッドは STREAM-READ-CHAR-NO-HANG を使い, STREAM-UNREAD-CHAR を使う.ほとんどのストリームは独自のメソッドを用意する必要がある.というのも,それは通常些細な事で,常にデフォルトのメソッドより効率的と思われるからである.

STREAM-READ-LINE  stream			[Generic Function]

READ-LINE によって使われる.文字列が第一の返り値として返される.第二の返り値は,文字列が行終端文字で終わっていれば真,ファイル終端に達した場合は偽を返す.デフォルトのメソッドは STREAM-READ-CHAR を繰り返し呼んでいる.

  STREAM-CLEAR-INPUT  stream			[Generic Function]

CLEAR-INPUT を実装する.NILを返す.デフォルトのメソッドはなにもしない.

文字列出力

文字列出力ストリームは, FUNDAMENTAL-CHARACTER-OUTPUT-STREAM を継承したクラスと,下記の総称関数のためのメソッドを定義することで作成できる.

STREAM-WRITE-CHAR  stream character		[Generic Function]

文字をストリームに書き出し,その文字を返す.FUNDAMENTAL-CHARACTER-OUTPUT-STREAM のサブクラスは,この総称関数のためにメソッドを定義する必要がある.

STREAM-LINE-COLUMN  stream			[Generic Function]

この関数は次の文字が書き込まれるカラム数を返す.このストリームにおいて意味を成さない場合,NILを返す.行の始めのカラムは0である.この関数は PPRINT および FORMAT の ~T 指示子によって使われる.文字出力ストリームを定義する時はこの総称関数のためのメソッドを定義する必要があるが,それは常に nil を返すものであってもよい.

STREAM-START-LINE-P  stream			[Generic Function]

この関数は述語であり,ストリームが行の初めにあるときにT,そうでないときNILを返す.このこの関数のためのメソッドは,いつでもNILを返すものであってもよい.この関数は FRESH-LINE の実装に使われる.注意すべきなのは, STREAM-LINE-COLUMN が0を返す場合もまた行の始まりを意味するが,STREAM-START-LINE-P が意味を持つのに, STREAM-LINE-COLUMN はそうでない場合がありうるということである.たとえば,可変幅の文字を使うウィンドウなどでは,行の桁の意味ははっきりしないが,行の始まりについては明白である.FUNDAMENTAL-CHARACTER-OUTPUT-STREAM による STREAM-START-LINE-P のデフォルトの実装ではSTREAM-LINE-COLUMN を使っている.そのため,もし STREAM-LINE-COLUMN が NIL を返すように定義されているのならば,STREAM-START-LINE-P か STREAM-FRESH-LINE のいずれかのためのメソッドが提供されるべきである.

STREAM-WRITE-STRING stream string &optional start end [Generic Function]

この関数は WRITE-STRING によって使われる.この関数は,文字列をストリームに書き出す.start と end がしていされていれば,文字列からその範囲が切りだされる.そのデフォルトの値は 0 および NIL である.文字列引数が返り値となる.FUNDAMENTAL-CHARACTER-OUTPUT-STREAM によって提供されるデフォルトのメソッドは,STREAM-WRITE-CHAR を繰り返し呼び出す.

STREAM-TERPRI  stream				[Generic Function]

TREPRI 同様に行集端子を書き出し,NILを返す.デフォルトのメソッドは (STREAM-WRITE-CHAR stream #\NEWLINE) を呼び出す.

STREAM-FRESH-LINE  stream			[Generic Function]

FRESH-LINEによって使われる.デフォルトの実装はSTREAM-START-LINE-P と STREAM-TERPRI を呼び出す.

STREAM-FINISH-OUTPUT  stream			[Generic Function]

FINISH-OUTPUT を実装する.デフォルトメソッドは何もしない.

STREAM-FORCE-OUTPUT  stream			[Generic Function]

FORCE-OUTPUT を実装する.デフォルトメソッドは何もしない.

STREAM-CLEAR-OUTPUT  stream			[Generic Function]

CLEAR-OUTPUT を実装する.デフォルトメソッドは何もしない.

STREAM-ADVANCE-TO-COLUMN  stream column	[Generic Function]

次の文字が特定のカラムに書き込まれるように,十分な空白を書き込む.成功した場合は真を返し,サポートされていない場合は NIL を返す.PPRINT および FORMAT 関数の ~T によって使われる.デフォルトメソッドは, STREAM-LINE-COLUMN と, #\Space を引数としたSTREAM-WRITE-CHAR の繰り返しの呼び出しによって実現され,STREAM-LINE-COLUMN が NIL を返した場合は NIL を返す.

その他の関数.
CLOSE  stream &key abort			[Generic Function]

現在の CLOSE 関数は総称関数によって再定義されるが,振る舞いは同じである.FUNDAMENTAL-STREAM によって実装されているメソッドは, OPEN-STREAM-P のためのフラグを立てる. CLOSE の値は Issue CLOSED-STREAM-OPERATIONS によって決定される.

OPEN-STREAM-P stream				[Generic Function]

この(提案 STREAM-ACCESS から取り入れた)関数は総称的に定義される.デフォルトメソッドは FUNDAMENTAL-STREAM によって提供され,CLOSE が呼ばれていないストリームであれば true を返す.

STREAMP  object				[Generic Function]
INPUT-STREAM-P  stream			[Generic Function]
OUTPUT-STREAM-P  stream			[Generic Function]

ユーザに STANDARD-OBJECT のサブクラスでないストリームを定義することを可能にしたい処理系は,これらのすでに存在する述語を,総称関数として定義することもできる.通常, FUNDAMENTAL-INPUT-STREAM および FUNDAMENTAL-OUTPUT-STREAM によって提供されるデフォルトメソッドで十分である.ここで,たとえば, (INPUT-STREAM-P x) は (TYPEP x 'FUNDAMENTAL-INPUT-STREAM) は等しくないことに注意すべきである.というのも,もしこれらの関数が総称関数にする以外の方法で処理系はストリームを定義するための定義するための独自の方法を提供しているかもしれないからだ.

STREAM-ELEMENT-TYPE  stream			[Generic Function]

現在の関数は総称関数によって再定義されるが,振る舞いは変わらない.FUNDAMENTAL-CHARACTER-STREAM が提供するデフォルトメソッドは CHARACTER を返す.

PATHNAME と TRUENAME はいずれも総称関数として定義しても良い.デフォルトメソッドは存在しない.すべてのストリームに対する適当な実装が考えられないためである.

バイナリストリーム.

バイナリストリームは FUNDAMENTAL-BINARY-INPUT-STREAM と FUNDAMENTAL-BINARY-OUTPUT-STREAM のどちらか,あるいは両方を継承したクラスであり,総称関数 STREAM-ELEMENT-TYPE のためのメソッドと,次の総称関数のうちどちらか,あるいは両方のためのメソッドを提供している.

STREAM-READ-BYTE  stream			[Generic Function]

READ-BYTE によって使われ,整数値を返す.ファイル終端に達したときは :EOF を返す.

STREAM-WRITE-BYTE stream integer		[Generic Function]

WRITE-BYTE を実装する. integer をストリームに書き込むとともに,結果として返す.

論理的根拠

すでにある I/O 関数は総称的にすることができない.というのも,ほとんどのケースで,ストリーム引数はオプショナルであり,そのためストリームによってメソッドを特定することができないからである.そのため,すでにある関数によって使われるより低レベルな総称関数を定義する必要がある.また, PRINT-OBJECT の第二引数によってメソッドを特定するのも適切ではない.というのも,それは高レベルな関数だからである.この関数はその第一引数が文字であれ文字列であれ, *PRINT-ESCAPE* に従ってフォーマットする必要がある.

その意味するところを明確にするために,総称関数の名前は,総称関数でない関数の名前の前に, "STREAM-" というプレフィックスをつけたものになっている.

eof-error-p と eof-value 引数を持つ高レベルな関数とともにファイルの終わりで :EO を返す総称関数を持つことは,総称関数のインタフェースを単純にし,引数を引き渡す必要をなくすことで効率的にする.この慣習に従うストリームは文字あるいは整数のみを返すことに注意すべきである.そのため, :EOF が返されることに曖昧さはない.

関数 STREAM-LINE-COLUMN, STREAM-START-LINE-P, および STREAM-ADVANCE-TO-COLUMN は捨てられた提案である STREAM-INFO が蘇ったようにみえるかもしれないが,ここでのモチベーションは異なっている.このインタフェースはユーザ定義ストリームが PPRINT や FORMAT ~T に渡せる時に必要であり,それは,これらの関数がシステム定義ストリーム上でこれらを呼べるかとは別の問題である.


こっから trivial-gray-streams

Gray Streamでした.これは提案されたものの仕様には含まれていません.でも便利そうだから色んな実装が同じものを提供しています.ただ,実装依存だからそれぞれ微妙に違っていて,ポータブルにするのは地味に面倒です.

で,それをまとめたのが trivial-gray-streamsです.quicklispで使えます.市民,幸福ですね?

そのREADMEを訳したっぽいですよ.参考サイトはこちら.
http://common-lisp.net/project/trivial-gray-streams/

trivial-gray-streams

Gray streams は ANSI CL に含めるために David N.によって,『ユーザ定義ストリーム』 STREAM-DEFINITION-BY-USER (http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html)の中で提案されました.

この提案は ANSI CL には取り入れられませんでしたが,多くの有名なCL実装がこれを実装しています.

このシステム(trivial-gray-streams)は,gray streamsのための,ごく薄い互換性のレイヤーを提供します.

これは完全なパッケージというには*とても*小さいものかもしれません.でも,もう十分なくらいたくさんのプロジェクトに同じコードをコピペしたんで,僕はこれをまとめて取り出して,もう*二度と*触りたくないです!

つかいかた

  • gray streamのシンボルを取得するのに,貴方が使わなくてはならない実装依存のパッケージの代わりに,TRIVIAL-GRAY-STREAMS パッケージを使うようにしてください.
  • STREAM-READ-SEQUENCE と STREAM-WRITE-SEQUENCE について言っておくと,2つの必須引数とキーワード引数を使うことができます.なのでどちらのメソッドを定義するときも,ラムダリストは次のようになります
(stream sequence start end &key)

かくちょう

Generic function STREAM-READ-SEQUENCE (stream sequence start end &key)
Generic function STREAM-WRITE-SEQUENCE (stream sequence start end &key)

上を見てください

Generic function STREAM-FILE-POSITION (stream) => file position
Generic function (SETF STREAM-FILE-POSITION) (position-spec stream) => successp

これらは ABCL, ACL, Lispworks, CCL, CLISP, SBCL, そして MOCL でのみ呼ぶことができます.

中身について補足(訳者)

このシステムは,各実装が提供している gray-streams 関連パッケージから,以下に挙げるクラス・関数をインポートし,たいていはそのままエクスポートしているだけっぽい.

だから,各実装の gray-streams 関連ドキュメントを読んだあと,実際に使う時は実装依存のパッケージではなく,このシステムを使うようにすればいいっぽい.

ただ,このパッケージからシンボルを取ってきてねといっても,どんなシンボルがエクスポートされてるかが分かんないとどうにもなんないですね.はい,次がそのシンボルでーす.実際に使う場合はちゃんとpackageの定義も読みませう.

システムが提供しているクラス
  • fundamental-stream
  • fundamental-input-stream
  • fundamental-output-stream
  • fundamental-character-stream
  • fundamental-binary-stream
  • fundamental-character-input-stream
  • fundamental-character-output-stream
  • fundamental-binary-input-stream
  • fundamental-binary-output-stream
システムが提供している関数
  • stream-read-char
  • stream-unread-char
  • stream-read-char-no-hang
  • stream-peek-char
  • stream-listen
  • stream-read-line
  • stream-clear-input
  • stream-write-char
  • stream-line-column
  • stream-start-line-p
  • stream-write-string
  • stream-terpri
  • stream-fresh-line
  • stream-finish-output
  • stream-force-output
  • stream-clear-output
  • stream-advance-to-column
  • stream-read-byte
  • stream-write-byte
実装依存のパッケージにはない, trivial-gray-streams 独自の総称関数
  • stream-read-sequence
  • stream-write-sequence
  • stream-file-position

CLOSとExpression Problem

                  ,,,,,,,,,,,,,,,,,,,,,,,,,,,,
            ,,--―'''""`ヽ'         ̄`ヽ、
           /        ヾ  /       ~`ヽ
         /           ヽ;:  /"""ヾ   ヽ
        /        ;:;;:::''''   l /;:;;:::'''  \   i
      /        /;:;;:::'''           ヽ  ヽ
      |         |               ヽ  |
      /        ;/                ヽ ヽ
     /        ;:;:ヽ            ,,,,;;::'''''ヽ  |
     i          /  ,,,,;;:::::::::::::::       __ ヽ ヽ
     |          |  "   __ ::::  '"ゞ'-' |  |
     |          |.    - '"-ゞ'-' ::::::..      |. |
     |         ;:|           :::::::       | :|
      |         ヽ.         ( ,-、 ,:‐、   | | 
      |       /ヾ..                  | |
      |          |         __,-'ニニニヽ .  |  |
..       |        `、ヽ        ヾニ二ン"  /  |
        |         ヽ\             /  |
        |          l  `ー-::、_       ,,..'|ヽ./ 
        ヽ.        :人      `ー――'''''  / ヽ
        /;:;:;:;;:;:;: _/  `ー-、          ,.-'"   \ー-、
           ,.-'"  \:      \      .,.-''"     |
         /.     \        ~>、,.-''"      |
    ,,..-‐'''""        ヾ    ,.-''"|    /――――、/

            オブジェクト指向は操作を追加するのが
            関数型言語は型コンストラクタを変更するのが
           (後からだと)難しい

らしいんですよ.そんでこれをExpression Problemというらしい.最近知りました.
http://d.hatena.ne.jp/osiire/20090516

これ,CLOSみたいなオブジェクトシステムの場合はどうなるんやろうなーと思いました.あっ,LISPは静的型言語じゃないだろとか言わないで,石を投げないで.

CLOS

クラスは一般に,メンバーの一覧とスーパークラスの一覧の定義からなります.メンバーにはフィールドとメソッドがあります.Common LISPでは,フィールドはスロットと呼ばれます.構造体のスロットからの流用でしょう.
クラスを定義してみます.

;;; <sample>クラスの定義
(defclass <sample> ()
  (
   ;; スロット一覧
   (slot-a :initarg :slot-a)
   (slot-b :initarg :slot-b)
   )
  )

;;; <sample>のサブクラスを定義
(defclass <sample-sub> (<sample>) ;;スーパークラスの一覧
  (
   ;; 新たなスロット
   (slot-c :initarg :slot-c)
   )
  )

インスタンスを生成するには,make-instanceを使います.スロット定義に :initarg をつけておくことで,生成時にスロットを初期化することができます.

;; <sample>クラスをインスタンス化
(make-instance '<sample>)

;; <sample-sub>クラスをインスタンス化
(make-instance '<sample-sub>)

;; スロットに初期値を与えて初期化
(make-instance '<sample-sub> :slot-a 1 :slot-b 2 :slot-c 3)

スロットにアクセスするためのもっとも基本的な方法は, slot-value を使うことです.

(slot-value (make-instance '<sample-sub> :slot-a 1 :slot-b 2 :slot-c 3) 'slot-a) ;=> 1

メソッドがありませんね.CLOSの際立った特徴は,メソッドがクラスに属さないことです.
ほとんどのオブジェクト指向言語において,オブジェクトはメッセージを受け取り,それに応えるという形で処理を進めます.ですから,オブジェクトがどのようなメッセージに反応できるのか,それはクラスに属するというのは自然な考え方です.
ところが,CLOSにおいて,クラスはデータ構造を表すだけで,どのようなメッセージに応えるかは定義しません.CLOSは,メッセージパッシングによるオブジェクト指向というモデルを採用していないからです.
ではCLOSにおけるメソッドとはなにかといえば,それは単なる関数です.単なる関数なので,高階関数にも渡すことができます.ただし,この関数は型によって動作を変えます.ポリモーフィズムはこれで表現可能です.
それでは継承が表現できないじゃないか,スーパークラスのメソッドを呼び出したいときにはどうするんだ,という話については,実際のコードを見てみましょう.

;; 総称関数の定義
(defgeneric sample-method (obj)
  (:documentation "自分のクラスを印字する")
  )

;; <sample>を受け取ったときの実装
(defmethod sample-method ((obj <sample>))
  (print "this is a sample")
  NIl)
; "this is a sample"
; => NIL

;; <sample-sub>を受け取ったときの実装
(defmethod sample-method ((obj <sample-sub>))
  (print "this is a sample-sub")
  ;; スーパークラスのメソッドを呼び出す
  (call-next-method)
  )
; "this is a sample-sub"
; "this is a sample"
; => NIL

メソッド定義の中で call-next-method を呼び出すことで,スーパークラスのメソッドを呼び出すことができます.ここでいう next とは,そのオブジェクトの型の階層を見た時,次の型,1つ上のクラスを指しているわけです.これで,オブジェクト指向で表現できることは表現できることになります.
なお,CLOSが多重継承を許すことや,2つ以上の引数の型に応じて呼び出されるメソッドが決まることなどから,next-method が何であるかは微妙な問題になるのですが,ここでは省略します.
さらに,メソッドが呼び出される前,呼び出された後に呼ばれる処理の指定といったわけのわからない独特なシステムが続くのですが,これも割愛します.

操作と型コンストラクタを後から追加する

cardクラスがあったとします.そんでこれのサブクラスとしてnum-card, jack, queen, kingがあり,それに対するメソッド,num, plusがあります.numはカードの数を返すメソッド.plusはカードの数を足し合わせるメソッドです.

(defpackage :card
  (:use :cl)
  (:export
   :<card>
   :<num-card>
   :<jack>
   :<queen>
   :<king>
   :num
   :plus
   ))

(in-package :card)

;; 抽象クラスとして使われる<card>
(defclass <card> ()
  ())

;; カードの種類を表すサブクラスを定義
;; 1から10までのカード.値を意味する value スロットを持つ.ついでにアクセサを定義しておく.
(defclass <num-card> (<card>)
  ((value :initarg :value :reader value-of)))

;; 絵札.スロットを持たない.
(defclass <jack> (<card>)
  ())

(defclass <queen> (<card>)
  ())

(defclass <king> (<card>)
  ())

;; カードの数字を得るためのメソッド
;; 総称関数を定義して
(defgeneric num (card))

;; 型によって場合分けを追加していく
(defmethod num ((card <num-card>))
  (value-of card))

(defmethod num ((card <jack>))
  11)

(defmethod num ((card <queen>))
  12)

(defmethod num ((card <king>))
  13)

;; カードの数字による足し算
(defgeneric plus (card1 card2)
  )

(defmethod plus ((card1 <card>) (card2 <card>))
  (+ (num card1) (num card2)))

別のパッケージに,このカードにjokerを足したものを作ります.カードの定義のほとんどは元のものを流用しますが,cardのサブクラスとしてjokerを追加し,さらにcardクラスに対して使える新たなメソッドも定義してみます.

(defpackage :card/joker
  (:use :cl :card)
  (:export
   :<card>
   :<num-card>
   :<jack>
   :<queen>
   :<king>
   :<joker> ; 新たなサブ型が追加されます
   :num
   :plus
   :score ; 新たなメソッドが追加されます
   )
  )

(in-package :card/joker)

;; cardを継承してjokerクラスを作れる
(defclass <joker> (<card>)
  ())

;; 総称関数の新たな場合を追加する
(defmethod num ((card <joker>))
  0)

;; 新たな操作も追加してみる
(defgeneric score (card)
  )

(defmethod score ((card <num-card>))
  (if (= (num card) 1)
      11
      (num card)))

(defmethod score ((card <jack>))
  10
  )

(defmethod score ((card <queen>))
  10
  )

(defmethod score ((card <king>))
  10
  )

(defmethod score ((card <joker>))
  10
  )

これが別の言語,たとえばJavaでは,メソッドを追加することは難しいことになります.メソッドがクラスに属すので,cardクラスに対して呼べる新たなメソッドを追加するためには,cardクラスを再定義する必要があるわけです.またOCamlなどでは,cardを再定義せずとも関数を追加することで操作を追加することができるのですが,サブクラスを定義するのが難しい.card型の新たなコンストラクタを作れないわけです.

しかし,CLOSなら簡単に後からメンバーと操作が追加できてしまいます.やったね.

わからないこと

CLOSがExpression Problemを解決しているのか,これが分からない.
型の継承関係と,総称関数としてのメソッドがあれば,静的型言語でも同じ事ができるんちゃうの,てかOCamlの多相バリアントってそういうもんちゃうの,と思うのですが,よくわかりません.

結論

ISLISPを使うべきでないたった1つの理由

突如としてライフハック風なわけだが

          ∧_∧
    ∧_∧  (´<_`  )  風味だけだ
   ( ´_ゝ`) /   ⌒i   中身はないぞ
   /   \     | |
  /    / ̄ ̄ ̄ ̄/ |
__(__ニつ/  FMV  / .| .|____
    \/____/ (u ⊃

LISPマクロについての今更感あふれる話(知ってる人は飛ばしておk)

LISPと同図像性

LISPマクロは他の言語に類を見ない極めて協力な機能を備えている,LISPはマクロによって他の言語と決定的に違った言語である,と言われます.
LISPのマクロとは何がそんなに特別なのでしょうか.それは,「カッコ大杉.修正されるね」としばしばネタになる,LISPの構文と深い関係があります.

LISPは同図像性を持つ言語です.これは,LISPソースコードが,その言語で操作可能なデータとして書かれるということを意味しています.LISPは,関数呼び出しは,関数を第一要素,引数を残りの要素とするリストとして書かれます.
関数が第一要素のリストというのは,Rubyでたとえれば,

puts("hello")

というコードが,

(puts "hello")

こう書かれるということです.さらに,これをRubyのデータ構造で表して,

[:puts, "hello"]

という風に書かれることになります.
関数呼び出しの引数が別の関数呼び出しであってもよいように,これはネストにすることができます.

[:puts, [:+, 1, 1]]

これがLISPの文法です.異質に感じられるかもしれませんが,大変便利なものです.

コードと評価

LISPソースコードは,まず「読み取り器(リーダ)」にシンボルのリストとして読まれ,そのシンボルのリストが「評価器」によって評価され,何らかの値を返す,という形で実行されていきます.

(fun a b)

というプログラムは,

(list 'fun 'a 'b)
; あるいは '(fun a b)

というデータとして読まれたあと,このリストが関数呼び出しを表していると評価器によって判断されて,関数が呼び出され,評価されます.
Rubyであれば,irbを想像してみてください.

'[:puts, "hello", :world]'

という文字列が読み込まれて,

[:puts, "hello", :world]

というオブジェクトが返されます.Rubyの評価はこれで終わりですが,LISPにおいては,先述の通り,このオブジェクトがコードです.

"hello"という文字列は評価されると再び "hello" という文字列を返します.:worldというシンボルは,LISPの考え方からすると,変数です.シンボルに対応する値として評価されます.最後に,:putsがメソッドとして評価され,そのメソッドに"hello"と:worldに対応した値が渡されます.

つまるところ,このコードはこのような意味になるわけです.

puts("hello", world)

これは大変ややこしい話のようですが,普通のプログラミング言語も,まず文字列が意味のある単位(トークン)に分割され,トークンの列が木構造のデータ(構文木)に整理されます.そしてプログラムが実行されたりネイティブコードを吐き出すというのは,この構文木を辿りながら行われるわけです.

LISPというのは構文木を表現するのにリストを使い,その構文木をつくる前段階,プログラムのソースすら,リストリテラルを使って,特別な記法を使わない,という,大変シンプルな言語です.

LISPマクロはなぜすごい

LISPのプログラムは,LISPのデータです.であれば,LISPのプログラムを使って,LISPのプログラムを書くことができるはずです.要するに,Arrayを出力するようなコードを書けば,それがそのままコードになります.

これは,たとえば「Ruby正規表現を大変便利に使える言語だから,Rubyプログラムを生成するRubyプログラムを書けるはずだ」という話と少し似ていて,少し違っています.

短い記法,プログラムのためではなく文書化のための記法,あるいはより自然に見える記法から,プログラムを生成しようという発想は同じです.

しかし,LISPが操作するのは文字列ではなく,LISPのデータ,リストです.Rubyで例えると,これは「ArrayとHashで擬似コードを書いて,それをevalしてデータ構造を得たあと,それを操作してプログラムを書き出す」というようなことです.文字列を操作するよりずっと複雑なことができます.
RubyがArrayやHashにたくさんの便利な機能を備えているように,LISPはリスト処理のための関数を山ほど揃えていますから,たいていのマクロは簡単に書くことができますし,自分で定義した関数でプログラムを生成してやってもよいので,大変パワフルです.

さらに,マクロによるコードの生成は,コンパイル時に行われます.リーダが読んだリストを,マクロで展開して,正しいLISPコードになった所で,さらに機械語バイトコードコンパイルすることができます.つまり,(事前にコンパイルしておけば)マクロでの変換は実行時にコストが掛からないということです.*1

一番いいことには,ArrayやHashの記法と違って,LISPにはカンマがいりません (^-^ )
このような理由から,LISPマクロはスゴイと言われるわけです.

本題:ISLISPを使うべきでないたった1つの理由

ISLISP is 何

ISLISPというLISPがあります.ISOのLISPです.
ANSI Common LISPがそれまでに乱立していたLISPの最小公倍数的な仕様であるのに対し,ISLISPは最大公約数的なLISPです.その仕様は,わずか100ページほどです.しかしその中身は,CLOS相当のオブジェクト指向機能とコンディションシステム(エラー処理システム)を備えた,たいへん意欲的なものです.

Schemeと同じく小さな仕様を持つLISP.しかしSchemeとはまた方向性の違うLISP

しかし,このLISPには一つ,大きな悲しみがあるのです.

『8. マクロ』

マクロは,実行準備時に展開される,いかなる実行時情報も使えない.

仕様にあるこの一文が悲しみです.「実行時情報」が使えないということは,つまり,自分で定義した関数によるマクロの展開は行えない,ということです.なぜなら,関数が定義されるのは,実行時だからです.
ユーザ定義の関数が使えないのでは,いくらLISPがリスト操作を得意とするといっても,マクロを書くのは大変だし,メンテナンスも難しくなります.

たとえば,こんなコードがあったとします.

(defun hello-extr (arg)
  `(format t "Hello ~A!" ,arg))

(defmacro hello (arg)
  (hello-extr (arg)))


これは,Common Lispではコンパイルを通りません.なぜなら,helloマクロが使っているhello-extr関数が,マクロが展開されるコンパイル時,使用できないからです.

これを回避する2つの方法があります.一つはインタープリターにこのコードを実行させることです.S式を1つずつ読んで評価していけば,すでに定義してある関数はマクロの展開に使うことができます.

あるいは,hello-extrがコンパイル時に評価されるようにするという手もあります.

(compile-toplevel (:compile-toplevel)
  (defun hello-extr (arg)
    `(format t "Hello ~A!" ,arg))
  )

(defmacro hello (arg)
  (hello-extr (arg)))

hello-extrがコンパイル時に使えれば,マクロの展開にも困りません.

ISLISPでは,どちらの方法も使えません.規格によって否定されているからです.これがISLISPを使うべきでないたった1つの理由であり,もしあなたがLISPを書きたいなら,おそらくそれはLISPマクロを書きたいということでしょうから,Common LISPを使うべきです.

真実

そうか.じゃあCommon LISPを使おう
ライフハックを即座に実行
流石だな俺
          ∧_∧
    ∧_∧  (´<_`  ) まあ,そもそもISLISP処理系が全然ないから,使いたくても使えないんだがな
   ( ´_ゝ`) /   ⌒i  
   /   \     | |   さらに見つけた処理系は普通にユーザ定義の関数が使えた
  /    / ̄ ̄ ̄ ̄/ |     要するにこの記事は全文無意味とほぼ同じだ
__(__ニつ/  FMV  / .| .|____
    \/____/ (u ⊃


     \\\
   (⌒\  ∧_∧
    \ ヽヽ( ´_ゝ`)
     (mJ     ⌒\
      ノ ∩兄 / /
     (  | .|∧_∧  OK,兄者,正直すまんかった
  /\丿 | (    ) 5000円で仕様書(JISから出ている翻訳版)を買ったことに免じて許せ
 (___へ_ノ ゝ__ノ



馬鹿なことに金を使ったものだな
どうするんだその仕様書
落書き帳にでもするのか
          ∧_∧
    ∧_∧  (´<_`  ) SchemeじゃないLISPを実装するときにでも
   ( ´_ゝ`) /   ⌒i  参考にするよ.多分
   /   \     | |
  /    / ̄ ̄ ̄ ̄/ |
__(__ニつ/  FMV  / .| .|____
    \/____/ (u ⊃

*1:コンパイルと聞いて疑問に思った方もおられるかもしれませんが,Common LISPのコードはコンパイルして実行することもできますし,インタープリターで実行させることもできます.