アニメシンデレラガールズは考察を追うのも楽しいということ

http://hetablo.hatenablog.com/

毎週感想を楽しませて貰っている。それはそれとして春香と卯月のことについてだけ個人的に思うところがあったので、それをまとめてみる(読解力がないだけかもしれないけど)。僕は普段感想文を書く人ではないので、箇条書き。

  • アイマスプロデューサーの権能はアイドルに夢をかなえる力を与えることであって、夢そのものを与える力を持っているわけではない。
  • その力を持ったプロデューサーが卯月の元へ恐怖とともにやってきたのはなぜか? -> 卯月はアイドルになりたいという夢を本当は叶えたくなかったから、と考えるしかない。
  • 夢をかなえる力の代償は、職業としてのアイドルになることである。が、22話に至るまで卯月からプロ意識を感じられる描写は全く存在しない。プロ意識の塊である杏やみくが同じ部署にいるにも関わらず。
  • この問題が難しいのは、プロ意識があればそれで良いというものではないからだ。夢を与えられる存在であるためには、自分自身も何かしら夢を見る必要がある。これはアイドルにとって絶対的に必要な素質であると同時に、現実との矛盾をはらむ。
  • またこれは武内Pと美城常務のスタンスの違いにも表れている。常務は夢の形まで細かに指定してアイドルを夢の中に閉じ込めようとするが、CPにおいては夢の形は自由である。その代わりCPは優しくない。自己責任で夢を見なくてはならないからだ。
  • 実のところ卯月との相性はプロデューサーより常務の方がよいように思われる(常務の好みではないだろうが)
  • アイマスのアイドルは基本的に職業としてのアイドル性と自分自身の夢にズレがあることで自己のバランスを保っている。例えば天性の才能を持つ美希でさえ、その目的はキラキラ輝く存在になりたいということであって、アイドルはそのための手段にすぎない。アイマスとはアイドルになりたい女の子の話ではなく、アイドルになることで自己実現を果たす女の子の話である。
  • ここに例外がいる。アイドルになることそのものが夢である、天海春香と島村卯月である。
  • アイドルになりたいという夢は、自分自身がすでにアイドルになってしまったという現実と矛盾する。春香の場合、これは夢の喪失という形で現れ、夢をかなえられる存在でしかないプロデューサーも無力となってしまった。夢の再発見は結局当人が自分で何とかするしかないからだ。
  • 卯月は逆に現実が破綻した。夢を与える存在=アイドルとしての自分を支えられなくなってしまい、現場を(形はともかく)追い出されてしまう。何故なら、夢をかなえる代償に職業としてのアイドルを求めるプロデューサーとの契約が不履行となったからである。繰り返すがCPは優しくない。本当に優しいのは(しかしそれ故つまらないのは)常務の方だ。
  • ところで、卯月の採用理由は笑顔である。卯月の笑顔とは何なのかといえば、それは夢のかたまりのようなものだろうといえる。春香や卯月が天性のアイドルである理由は、二人が強大な夢の持ち主だからである。
  • 卯月の才能を最大限に発揮するには、この持って生まれた強大な夢を、現実的な能力として使いこなすしかない。
  • 卯月はプロデューサーとの出会いがもたらした恐怖をまだ乗り越えていないと考えるべきである。ということは、その解決は自分の中にある恐怖の正体を見極めてそれを乗り越える事によってしか得られない。
  • 探すべきは12時過ぎの魔法である。それは自分の靴で進む勇気である。ガラスの靴は助けてくれないはずだ。
  • 王子服といえば少女革命ウテナである。卯月は世界を革命するしかない。
  • 卯月にプロ意識は本当にないのだろうか?そんなわけはない。頑張るとはプロ意識の最も原初的な形であるはずだ。
  • 卯月がこれまでひたすら頑張ると言い続けてきたのは、それが破綻を企図した空っぽさの表れであると同時に、卯月の本質であると考える。
  • 卯月に足りないのは、自分の頑張りが本物であると信じる勇気である。勇気の問題であるならば、プロデューサーの権能の及ぶところであるはずだ。そしてこれは美城常務が与えてくれないことでもある。何故なら常務の価値観は結果が全てであって頑張りは評価の対象にならないからだ。また常務のプロデュース方針はアイドルたちに勇気を与えてくれるものではなかった。
  • 以上を持って、卯月の物語とは自分にとってのプロ意識の形を求める少女革命の物語ではないかと考える。これは春香が徹底してアマチュアリズムを貫くことと対照的である。

特にオチはなし。ストーリーの先読みとかは苦手だからしない。

悲しいが、リズム天国ベスト+は駄作である

リズム天国シリーズはGBA版からやってるから、ちょっと言わずにはおれない。

このゲームはなにをどう間違ったんだろう。ストーリ要素の不要性とかもあるが、とにかくプレイのテンポが全体的に悪すぎる。各所の仕様が完全に改悪であると思う。

個々のゲームは悪くない。そこはしっかりリズム天国であるからいいのだが、ゲーム全体をどう見せるかというレイアウトの劣化がどうしても気になるのだ。例えば「あのゲームはどこにあったかな」と思ったとき、これまでであればゲームすべてが平面上に配列されているからアイコンを見れば一目瞭然なのだが、ベスト+ではまずステージ選択を行わなければならない。しかもステージ間の接続は基本的に縦並びだから、行ったり来たりで目的のステージを見つけるまでの手順が恐ろしく面倒になっている。
多分開発者もそこに問題があるのを薄々分かっていて、パーフェクトキャンペーンに関してはわざわざショートカットを設けて飛べるようにしているのだが、それって本末転倒だろう。宮本茂のいう「アイデアとは複数の問題を一度に解決するものだ」という定義に照らしても問題を先送りにしてるだけなのは一目瞭然だ。
しかもストーリー要素だかなんだか知らないが、一々キャラクターが余計なセリフを喋ってくれるわけである。こちらとしては調子よくパーフェクトキャンペーンに挑みたいのに、受付の手続きで待たされるわけで、ノリ感を崩すこと甚だしい。
先ズレ、後ズレ表示や、キメ星もそう。あれは見せるべきではなかったと思うし、見せるにしても主張するようなやり方は良くない。しかも設定でオフにできる辺り、やっぱり開発側の迷いが感じられるように思う。

正直なところを言うと、Wii版もちょっと怪しいなという気がしていたのだ。ただ、あれはプラットフォームが家庭用機だったし、多人数路線を推していたから少し違うのは仕方ないと思っていた。初代が100点、ゴールドが(ペン操作でやや曖昧な点を引いて)95点とすれば、Wii版も80点ぐらいはあげられると思うし、そこまで駄目だとは思っていない。でもベスト+はいいところがない。残念だけども50点ぐらいの駄作だと思う。

恐らくなのだけれども、ゴールドの「社長が聞く」を読む限り、初代・ゴールドディレクターの大澤和義という人がWii版以降ディレクターを外れたことが相当大きいのではないだろうか。初期のリズム天国は、あのインタビューから受ける印象そのままの、恐ろしくストイックで余計なものが一切なく、それでいて裏設定やミニゲームなどやたら愛情あふれた無言の語りに満ちている作品だった。だから何回プレイしても飽きが来ないし、何か極めたくなるような魅力があった。しかも「極めなければ」と思わせる脅迫感はなくて、「僕はこれがいいと思うんだけどどうですか?」というような一歩引いた感じで、あのキッサ店のマスターのごとく大人なゲームだったわけだ。Wii版はまだなんとか持ちこたえていた感じがあるのだけど、ベスト+はもうボロボロである(謎の怪ゲー「ヤギ育成」だけはぶっ飛び過ぎてて好きだけど。もうリズム何も関係ないw)

あるいはつんくが本調子であればまた違ったのかもしれないけど、そこは本業でもない人間に求めることではないし、任天堂が面倒見るべきところであるからいうべきではないだろう。返す返すも全く残念である。

C++

いまさら僕が言うまでもないが、C++という言語は混沌としていると思う。

この混沌はどこから来るのだろうと考えてみるに、どうも悪しき還元主義にあるんじゃないかなぁという気がする。

C++の仕様の議論でしばしば見られる現象として「機能○○は既存のxxによって実現可能であるから言語コアに不要」というのがある。これはライブラリ機能ではなくてメタ文法に属する水準の話だが、テンプレートという変態機能があるおかげで、C++は色々なことがad-hocに実現できてしまうわけだ。

もちろん言語コアはできるだけコンパクトに纏めるべきだし、予約語も少ないに越したことはない。・・・のだが、仮に「ifとgotoがあればすべてのフローはシミュレートできるからforもwhileも不要」といったらまるで「本物のプログラマ」の話みたいでうげっと思うだろう。僕の感じるC++の混沌はそれに類似したことなのではないだろうか。

ある機能がad-hocであることのコストは、それが認識の中で弱い前景化になりがちであることと、同じことだが不格好な実装になりがちなことで、C++の場合「結局どれを組み合わせれば正しい書き方なのか言語全体を知っていないと判断できない」という形で現れているように思う。

swift: SEGVに遭遇

swiftは言語的には素晴らしくて気に入っている。でも開発環境はまだまだヘロヘロでHeaderDoc的なのがまだないし、ソース補完は相変わらずクラッシュしまくりである。

そしてついにコンパイラがSEGV。IR生成までいってるので文法的には正しいコードを書いてると思うのだが・・・。

While emitting IR SIL function XXX for 'f' at yyy.swift
  swift                    0x000000010b311b68 llvm::sys::PrintStackTrace(__sFILE*) + 40
1  swift                    0x000000010b312054 SignalHandler(int) + 452
2  libsystem_platform.dylib 0x00007fff8b52ff1a _sigtramp + 26
3  libsystem_platform.dylib 0xffffffff00000001 _sigtramp + 1957495041
4  swift                    0x000000010b197715 llvm::ConstantUniqueMap<llvm::ExprMapKeyType, llvm::ExprMapKeyType const&, llvm::Type, llvm::ConstantExpr, false>::Create(llvm::Type*, llvm::ExprMapKeyType const&, std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::__value_type<std::__1::pair<llvm::Type*, llvm::ExprMapKeyType>, llvm::ConstantExpr*>, std::__1::__tree_node<std::__1::__value_type<std::__1::pair<llvm::Type*, llvm::ExprMapKeyType>, llvm::ConstantExpr*>, void*>*, long> >) + 53
5  swift                    0x000000010b194bb2 llvm::ConstantUniqueMap<llvm::ExprMapKeyType, llvm::ExprMapKeyType const&, llvm::Type, llvm::ConstantExpr, false>::getOrCreate(llvm::Type*, llvm::ExprMapKeyType const&) + 514
6  swift                    0x000000010b1919c6 llvm::ConstantExpr::getBitCast(llvm::Constant*, llvm::Type*) + 182
7  swift                    0x000000010b3177d5 llvm::IRBuilder<true, llvm::ConstantFolder, llvm::IRBuilderDefaultInserter >::CreateCast(llvm::Instruction::CastOps, llvm::Value*, llvm::Type*, llvm::Twine const&) + 133
8  swift                    0x000000010a6aef02 swift::irgen::emitDynamicTypeOfOpaqueHeapObject(swift::irgen::IRGenFunction&, llvm::Value*) + 66
9  swift                    0x000000010a6de423 swift::irgen::emitClassProtocolMethodValue(swift::irgen::IRGenFunction&, swift::irgen::Explosion&, swift::SILType, swift::SILDeclRef, swift::irgen::Explosion&) + 531
10 swift                    0x000000010a71da96 swift::SILVisitor<(anonymous namespace)::IRGenSILFunction, void>::visit(swift::ValueBase*) + 28950
11 swift                    0x000000010a715d3b swift::irgen::IRGenModule::emitSILFunction(swift::SILFunction*) + 9179
12 swift                    0x000000010a68f210 swift::irgen::IRGenModule::emitGlobalTopLevel() + 208
13 swift                    0x000000010a702a6c performIRGeneration(swift::IRGenOptions&, swift::Module*, swift::SILModule*, llvm::StringRef, llvm::LLVMContext&, swift::SourceFile*, unsigned int) + 1900
14 swift                    0x000000010a703473 swift::performIRGeneration(swift::IRGenOptions&, swift::SourceFile&, swift::SILModule*, llvm::StringRef, llvm::LLVMContext&, unsigned int) + 51
15 swift                    0x000000010a6586f4 frontend_main(llvm::ArrayRef, char const*, void*) + 5444
16 swift                    0x000000010a655a6d main + 1677
17 libdyld.dylib            0x00007fff949fc5c9 start + 1

swift: array for-each

Swiftにはfor-eachがあるから古典for文のようにindexで回すよりはずっといいが、せっかくなのでRubyよろしくeachでブロックを渡すような文法もあればいいのにと思ったりする。

そこで演算子オーバーロードだ。

infix operator / { associativity left precedence 95 }
func / (lhs:Array<T>, rhs: T->Void) -> Array<T> {
    for i in lhs
    {
        rhs(i)
    }
    return lhs
}

やりたいことはeachだから値を返す必要はないが、連鎖的に処理できると面白そうなので元の配列を返すことにしてみた。つまり、配列に保持されたオブジェクトに対して副作用を次々加えていくスタイルである。

class C {
    var val: Int
    init(v: Int)
    {
        val = v
    }
}

let l = [C(v:1), C(v:2), C(v:3)]
l / { o in o.val *= 2 } / { o in o.val += 3 }
l // => [(val 5) (val 7) (val 9)

これ、/は適当かな?SwiftはUnicodeも演算子にできるから、「♪」とか定義してもいいのだが。

swift: protocolとGenerics関数

次のコードは違法。

protocol Metric {
   var width: Double { get }
   var height: Double { get }
}

func area<T:Metric>(obj:T) -> Double 
{
   return obj.width * obj.height
}

class X: Metric {
   var width = 0.0
   var height = 0.0
}

let obj: Metric = X() // protocolで型制約
area(obj) // !Invalid!

この時「error: generic parameter ‘T’ cannot be bound to non-@objc protocol type ‘Metric’」という周りくどいエラーが出るので困る。

要はGenericsの関数はコンパイル時に実体を確定する必要があるため、型をprotocolで多態させている変数に適用することができない。swiftの場合、Array<Metric>などは普通に作れるので、うっかり使うとハマる。

次のようにすれば動的解決してくれて合法になる。パフォーマンスのオーバーヘッドは多少あるが、それならprotocolで多態するなという話だろう。

@objc protocol Metric {
   var width: Double { get }
   var height: Double { get }
}

なお、@objcがつくと自動的に: classのプロトコルになるので、struct等を対象にこの方法は使えない。

swift: class-onlyプロトコルとsetter

以下の単純なプロトコルがあったとして、これをGenerics関数と組み合わせた時valの値を変更する関数を書くことは文法違反となる。

protocol P {
   var val:Int { get set }
}
func updateVal<T:P>(item:T, v:Int)
{
   item.val = v // 代入は違反
}

なぜならプロトコルはstructにも適用可能であり、structではプロパティの値を変更する関数がmutating属性を必要とするからである。

しかしPの中にmutating属性の関数を定義するのは恐らくうまくない。それをやると今度はclassがmutating関数を持てないので、構造体にしか適用できないプロトコルができてしまう。またsetter関数が特定のプロパティの変更に対応することを文法的に保証できないので、設計的にもエレガントとはいえないだろう。

値の代入が必要な場合、諦めるならstructである。class-onlyプロトコルならば代入を行う関数を書くことができる

protocol P:class {
   var val: Int { get set }
}

Swift; init! と init? の使い分け

最近Swiftのコーディングを始めている。

Appleのドキュメントでは「失敗することのあるinitializerはinit!かinit?なんだ」というざっくりとした説明があるのだが、それらを具体的にどう使い分けるのかちゃんとした記載がなく、検索しても意外と触れている人間が見つからなかった。ということで個人メモ。ちゃんと調べてないので、オレオレ && 二番煎じの可能性あり。

前提としてOptionalの話。Optionalについては http://qiita.com/cotrpepe/items/518c4476ca957a42f5f1 にまとまっているが、要はSwiftでは通常の型の範囲に無効値であるnilを含むことが許されないため、無効値がありうる場合は[普通の型 または nil ]という派生型にパックして扱うという話である。つまり関数型でいうところのMaybeにあたる。

で、これらは基本的に変数に対する修飾として機能するのだが、nilを生み出す源として、インスタンス生成の失敗というものがでてくる。例えば存在しないファイルパスからNSDataをつくろうとすれば、当然インスタンスはnilを返さざるをえない。このような失敗するイニシャライザは!また?によって自分がパラメータによって失敗しうることを示す必要がある。問題はinit!とinit?はどう違うかということである。

上記のリンクで整理されていることを設計思想的に解釈すると、!はnullptr方式で「nilが入っていてもいいが、nilの時に使ってはいけない」ということであり、一方?はobjc由来のnilにメッセージを送る方式で「nilに対して何をしてもnilである」ことを示していると思われる。

例えばこんなクラスを考えてみる。

class Test {
    init!(f:Bool)
    {
        if !f {
            return nil
        }
    }
    
    init?(g: Bool)
    {
        if !g {
            return nil
        }
    }

    func show()
    {
        println("bow!")
    }
}

パラメタfの方のイニシャライザは!指定であるから、アンラップは暗黙で行われる。「ヌルポで呼ぶなよ?」というやつで、そのため

let t = Test(f: false)
f.show() // runtime error

というのは文法的には合法だが、実行時エラーを発生する。もしこれを回避したければif let受けするのがSwift的にはスタンダードなんじゃないかと思う。

if let t = Test(f: false) {
    t.show()
}
else
{
    // tがnilの時の処理
}

一方パラメタgの時のイニシャライザは?指定なので、アンラップはプログラマの仕事である。よって

let t2 = Test(g: false)
t2.show()

というコードはそもそも文法的に許されず、

t2!.show() // runtime error
t2?.show() // nil

のどちらかを書くことになる。

コーディングの見た目としては、おそらく明示的にアンラップしなければ使えない後者の方が安全なので、init!をどうしても使わなくてはならない理由はどうも思い当たらない。なにせinit!にしたところで、

let t = Test(f: false)
t?.show() // nil

と明示的に?で外せばinit?と同じことになるし、逆にinit?にしてもif letで受けた場合は!/?を省略できる点を考慮すると、生成失敗のnilチェックを厳密にする手間は同じであるように思える。

ちなみにプロパティについては、!/?の適材適所がそれぞれあると見た。
?修飾のプロパティは、objcでよくあるnil許容の値に使うべきで、nilチェインを認めないなら使わないほうが無難だろう。

!修飾は、個人的にはhas-aで補助インスンタンスを持つタイプのクラスが、init!/?で変数初期化を保証するために使うといいのではないかと思う。

というのは、init中で必須の補助インスタンス生成が失敗したらさっさとnilを戻して脱出して欲しいのだが、Swiftの文法はインスタンス変数未初期化のままでinitのnil脱出を認めないということになっている。そのため、とりあえずnilで初期化しておいて、実際はnilのままでは使えない!修飾にしておけば、「他のメソッドは変数は正しく設定されている前提で動きますからね!」というような主張になる。まあ、多分に好みの問題ではあるのだが・・・。

Nikon Dfのイケてないところ

当ブログ主は趣味のNikon使いである。そして先日来Nikon Dfを使用している。
Nikon Dfはデザインの印象に反してボディがデカいのと、ペンタ部の仕様が半端で突き抜けてないのが少々気に入らないのだが、性能面については満足している。D4センサーはいい。「センサーがD800の方なら・・・」とかいってるのは、単なるスペック厨で写真なんか撮らない奴だ。

そのNikon Dfも個人的に設計で一点納得いかない部分がある。Mモード+ISO感度自動制御の挙動である。

普通カメラマンがMモードを使う状況というのは、AE任せにすると作画意図に反する露出が出る or 露出補正では却って制御しづらい時に、露出そのものを手でエイヤと決めてしまう場合ではないかと思う。
デジタルの場合はこれにISO感度設定が加わるが、Dfの場合はISO感度にも専用の物理ダイヤルが付くから、100なら100を基準にいわゆる「感度分の16」法則+経験に従ってシャッタースピードを当てていくことで対処することになるはずである。

ところが、うっかりISO感度の自動制御がOnになっていると、絞りとシャッタースピードを手で決めて、なおかつISO感度設定のダイヤルが固定値を示しているにも関わらず(<-ここ重要)、感度シフトが起こって露出をカメラが勝手に決定してしまうのである。なんじゃそりゃ、マニュアルじゃないじゃん!直観に反しとる!

思うのだが、シャッタースピードにAEモード指標があるように、感度ダイヤルにもAEモード指標を付けるべきだったんじゃないだろうか。マニュアルレンズの絞りとシャッタースピードは設定値のマニュアルとオートをちゃんと手で物理的に切り替えできるのに、ISO感度だけはメニューを掘るか専用のファンクション設定しないとダメなのはなんだか片手落ちである。もちろんこんな事が気になるのはDfが物理ダイヤルを堂々と備えているからで、他のカメラでは至極普通の仕様といえるのだが・・・。

ISO感度の自動制御自体は普段非常に便利なだけに、デフォルトでオンにしておきたい機構である。せめてMにした時だけ、自動制御をオフにするような設定が付けられないものであろうか。GRとかならファームアップで何とかなるのだが、そんなことをするのはリコーだけかなぁ。

気がついたらiMovieが使い物にならなくなっていた

iMovie 10でファイル出力しようとしたらビットレートの設定がなくなってるでゴザル。。

ニコニコ用の動画は一ファイル当たり最大で100MBと決まっているため、ちょっと長めの動画を作っている私はビットレート調整でサイズをギリギリになるよう抑え込んでいた。ファイルサイズ合わせの出力ができないということは、iMovieを使うなという事と同義である。バージョンアップで機能が減るのは何時ものAppleだから今更期待はしていないが・・・。