拙作 Smoker Insight! is Out

f:id:setminami:20160627002853p:plain

拙作アプリがローンチされました。

?  にあるように、CoreDataとデータ構造に興味を持って設計を起こしたため、ベースがcoreData(実態はsqlite3)の割にはデータ量が少々蓄積してもリアルタイム演算は割と速い方だと思います。

以前にもネタにしましたが、ミソはパーセンタイルと4分位数あとKVS少々。逐次検索を一旦KVS構造に読み替えるシステムなんで、一旦準備が整えばフラグ調整や、新たな喫煙データが加わることで全データの平均値が変わって各データのプロパティ変更が必要になっても1,2,3qをピボットにして高速追従します。

これを軸にシステム全体のデータハンドリングが組まれているため、よくあるユーザに見えてるアイテムについてだけの実行ではなく、毎回データ全舐めをして、全データ系内で整合性を保ちながらも十分な速度で動きます。

 このアプリ、任意のタイミングでの全データグラフ化機能を搭載しているので、これは外せない必要仕様なわけです。

遅延評価であれもこれも必要になったとき計算、ではいざというときユーザーを待たせてしまいますから。

 IAPに至っては、かなりJBに対して挑戦的な実装になっていて、途中で仕様が複雑になりすぎたため、ここだけは真面目にUMLを書いて設計しました。

これも機会があればまた詳しくネタとして展開します。

ただ、今の実装はappleidのログインダイアログがしつこく出るので、少し仕様変更入れるかも。

 

とりあえず、いまはまだまだ全然宣伝できてないので、向こう2週間は個人開発者向け宣伝サイトに順次レビューを寄稿しつつ、AdMob, iTCとにらめっこの予定です。

 

swift解体新書 その2 Enum RawValue

Swift.org - Documentationを読んでて、swiftEnumJavaなみに変態仕様になっていたのはチェック済みだったんだけど、いろいろ隠れ仕様があるみたいなのでそのメモ。

 

まずは手始めにこんなのを書いてみた

 

気になっていたのが、The Swift Programming Language (Swift 2.2): Enumerationsの次の記述。

Implicitly Assigned Raw Values

When you’re working with enumerations that store integer or string raw values, you don’t have to explicitly assign a raw value for each case. When you don’t, Swift will automatically assign the values for you.

 

 つまり、明確な型を指定しなくても、swiftcで良きに計らってなにかしらのサインとして、型付けするよと。

これの何が嬉しいかというと、Planet_whoAreYouみたいにrawValueほったらかしていても、switch-case文書くときには何も気にしなくていい。

とにかく.MercuryはPlanet_whoAreYouのMercuryというサインでしかないと。

これは、全てをオブジェクトで表現しようというOOP上正しい。

 

そもそも、K&R時代のenum値でアーキテクチャのWORD型を割り当てていたのは、Cという抽象表現から機械語に翻訳する上でそうするほか無かったからだという解釈もできるわけで。

本当なら、コーダーはenum値が具体的にIntで1だろうが、Stringで"Mercury"だろうがそんなな気にせず、Planet_whoAreYou.Mercuryという抽象的なモノとして扱えと。

こうしておくことで、具体値が何かの都合で突如IntになろうがStringになろうがDoubleになろうが.Mercuryは.Mercuryという概念でしかなくなり、概念上のシステムバウンダリから向こう側はそっから先で勝手に考えてくれれば、それで必十条件を満たすので、みんな幸せということにできる。

とはいえ具体値は気になるということで、それを確認しているのが各for内の

    print(x)

    // -> Earth

    let strA = String(x)

    print(strA)

    // -> Mercury

    print("\(x) = \(x.rawValue)")

    // -> Jupiter = 5

 

のあたり。どうやら2.2時点ではrawValue関係なく、Stringで内部に持っているらしい。正確には、String型でインスタンスを生成できるといったところか。

 

ただし、Planet_...で次のようにはできない。

swtch x {

    case "Mercury" : .....

"型と値はちっげーよ、勘違いすんなカス"と怒られる。

 

あと、

let y = x as! String

とかで強制キャストすると、Warningで"それ...やってもいいけどダメだと思うよ。その型xと関係ねーし。わけわからん!"といわれる。

で、実際実行してみるとInstructionエラー。機械語に展開したときに変なオペコードでも吐いてるのか?これはx86だからかも知れない。ARMでやったらnilスルーするのかも。 

 

でも、

swtch String(x) {

    case "Mercury" : .....

はPlanet_...全てで通るし、想定通りに実行される。

 

Stringの生成に関して、ニーモニック上は手前で実装されたmethodの呼び出しに続いて、"__...NSString_class"なんていかにもバックコンパチな外部シンボルを呼んでいるにもかかわらず、"...fixStringForCoreDataP11objc_object"なんて、名前を見る限り不穏なローカルルーチンに何度も何度もオフセットかけて正気の沙汰とは思えないくらいぐっるぐる飛び倒す。これ...CStringの残党引っ張りだすためなんじゃね?とすら思える。

ちなみにこの解析アプローチは、レジスタの書き換えを肉眼では追い切れず、もはや人間の読むフローではないので、あきらめた。

 

 

じゃぁということで、ひとつ疑問が生じる。"enum Planet_String: String..."って結局内部保持してるから二重に宣言するだけ無駄じゃね?Stringじゃないほうがお得かもって。。。

なんとなくここから次のソリューションをひらめく人もいるかもしれない。

例えば、 rawValueはIntだろうが、DoubleだろうがswitchのネタをString()インスタンスにしてしまえば、IntとString両方の値が持てるのでは?

つまり、caseラベルは"Mercury"などenum値を文字列表現したもの、rawValueはInt型だとすると、

func next_prev(x:Planet_Int, isNext:Bool) -> Planet_Int! {

    switch String(x) {

    case "Jupiter":

        if isNext {

            return Planet_Int(rawValue: (x.rawValue + 1))

        } else {

            return Planet_Int(rawValue: (x.rawValue-1))

        }

    // something else ....

    default:

        return Planet_Int(rawValue: 1)

    }

}

なんて、このコードは例としては良くないが、要はStringでcase分岐かけつつ、Int値演算で次or前のenum値を得ることができる。そして実際実行可能だ。

enumにclassを内包させたりして結構苦労する.Neptuneの次は.Mercuryみたいに、循環enumも割と簡単にできてしまうし、なんなら、regexやら遅延評価で超技巧的なこともできそうだ。

 

しかし、これ実はOSやコンパイラ配布側(つまりApple)からすると相当筋の悪いソリューションで、思いついて"自分天才ですから"なんて言ってちゃいけない(と思う、個人的には)。

理由は上記で出てきた、

...Planet_whoAreYou.Mercuryという抽象的なモノとして扱え ...

 というのがその全て。

これ、抽象的に扱うというのは、具象的な扱いは全て配布側で握るよ、気が向いたら内部表現なんて全然違うモノにするよん。といっている(ようにも聞こえる)。

つまり、ある日突然xをStringでインスタンス化したコードは全く想定外の動きを示し、実は人間の読めないハッシュ値になることすら否定はできない。例えば、swift3ではenumが直で階層構造持てるようになって、.Jupiter.NaturalSAT.Ganimede, .Mercury.None, .Earth.MTSAT.HimawariVIIIとか(妄想)。

今の仕様で、これをやろうとすると結構めんどくさい。主張点がぼけるからかもしれないが、Appleが出しているトランプの例示が、別々に宣言したenumの組み合わせでしかできないことを示しているのかと。確かにpropertyは委譲できないけど、関数やらstruct/class宣言は内包できるんで、がんばればできるのかとは思う。

 

なんかいろいろ議論が混線しちゃったけど、言いたいことは、enumにこんな特徴見つけたが、たとえお客から数時間後に緊急納品ねじ込まれたとしても、よい子はマネしちゃだめということ。

いや、たいていはチーム内の誰かがアーキの意見を聞かず、色気を出して技巧に走り、自分の守備範囲ではないEnum値をIntで順序ありきとかに勝手に書き換えてシステム前提ぶっ壊す。あげく褒め殺してアピールをしてくるというのが現実なんだけど...

だからJavaなんかは、禁則コードが描きやすくなってるんだよね。Appleはその辺あんまり興味ないみたいだけど。

"やられて困ることは、はなからできなくしろ" これ鉄則。

マーフィーの法則の失敗する余地を残したシステムは、いつか誰かが必ずやらかすってやつ。

Swift解体新書 その1 ニーモニック確認環境

通常ならswift de classDrivenの続きのはずだが、一旦中断してクロスコンパイル環境から解体することにした。

 

なぜなら、WWDC2015 - 408にむっちゃ恐ろしいことが発表されていたから...

忙しさに負けて、チェック抜け...間抜け

 

詳しい解説はネット上和訳が出回っているのでここでは割愛するが、これによるとAppleは(少なくともswiftのPLであろうabraham氏によれば)、

継承構造は継承と言いつつ資源配分問題になると、

途端に厄介な問題を抱え込むので、そこは正式にはサポらんよ。

Protocol-Orientedで書いてね、てへぺろ

 と、いろいろ誤解を招く訳しかたをしたが、言いたいことはclass/struct/enumで継承使って親クラスの資源を無考で使えると思うなよ、そもそもabstractみたいな概念自体わしゃ好かん。

そんな細かいこというやつは、protocol+extension(これは明言してないけどObjCならprotocol+Category)で記述しろと。。。

 

これつまり、Appleはダブルチェックロッキングとかめんどくさいこと考えたくないから、独自路線を行きます。JavaやらC++と同じ概念で継承を考えると細かい所で違うものになるから痛い目見るよと(独自解釈)。

Alan Kay先輩とBjarne Stroustrup先輩の視点差に関して、ObjCでは業界トレンドに任せてごまかしていたことをついに認めたコメントなんだと思う(独自解釈)。

 

 

ということで、基本に立ち返る。

言われてみれば、swiftcも1.1時代にobjCとたいして変わらんことしか確認せず、まだ具体的には触ったことがなかったのでちょっと触ってみた。

最終Goalはニーモニックでクラス階層構造を確認すること。

この記事のGoalはとりあえず.o, .sファイル相当の内容確認ができるところまで。

 

---

閑話休題。では、以下本日のゴールに向かって順番に。

まずはswiftc  これがないとはじまらない。

これはxcodeなり、swift.orgからなりtoolchain取ってくれば入っているはず。

$ swiftc -v

Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)

Target: x86_64-apple-macosx10.9

 

 素知らぬ顔して、clangがLLVMのフロントにあることを告白している。

前に見たときこんなの出力してたっけ? そんななわかりきってるので、今日は触れない。

 

次、binutils これは、swiftc -SなりxcodeのDebug Workflow > Dissassembly でも代用はできるけど、サマるんだったら入れておいて損はない。

macならbrewで入れればまず問題は無いだろう。EL-CapitanのSIP問題に引っかかったら、ググって自己解決してください。

Linuxならapt-get

ここではgobjdumpが使えれば必要十分。

 

解析ツールはそろったので、次それぞれの使い方に進みます。

swiftc

これは、コンパイルするのに使う。

とりあえずの試料としてざざっとLoopテストを書いてみた。

asの確認をするのであれば平でfuncを書いた方が分かりやすいが、とりあえず。。。あとprobe効果なんかもほぼ考慮していないので、試料内容に関する突っ込みはご容赦いただきたい。

もちろん配列がL2に入るかなんて、とりあえず数回して誤差詰めとけ位にしか考えていない。

 

 

 Playgroundで書き出したが、XcodeのREPLの起こし方がアホで、編集の度にインタプリタ実行をかけ直そうとする。Loop試験のような何百万もループ回すコードだとさすがに固まるため、途中でコピペしてAtomでやった。

たぶんなんらかXcode設定はあるかと思うが、どちらにせよREPLだとs/n比低すぎてLoopの試験にはならんので、結局コンパイルする予定だからあまりこの場では深く考えないものとする。

あくまでGoalはニーモニック確認

 

 で、この試料をswiftcで実行ファイルにビルドする。

$ swiftc -O -sdk $(xcrun --show-sdk-path --sdk macosx) -o aStruct.out for-inExp-struct.swift

どうやらエントリポイント(main)はfoundationにリンクすることで、バイパスしているらしい。普通はこれで走るわけはないので注意。

 

今どきはアルゴリズムが成熟しているため、-Oを付けた方がより実測値に近づくがこれはお好み。 

--sdkはiphoneos(全小文字)にすればiOS向けにクロスできるけど、objdumpが欲しいだけなので、x86でいいだろう。

試料でUIKitではなくFoundationをimportしているのはそういう理由。

 

$ aStruct.out

でターミナルなんかから直接実行されるファイルができあがる。

もろもろ面倒なので、script書いた

 

OUTPUTはこんな感じ

StructTrials

opened for : av = 59.826675, total = 5982.667542

closed for : av = 26.785226, total = 2678.522583

for-in : av = 27.640303, total = 2764.030273

zipIndexing : av = 61.732862, total = 6173.286194

while : av = 59.986423, total = 5998.642334

zip、whileはなんとなく想像ついたけど、開区間(...) 閉区間(..<)でこんな違うんだ。

zip同様Loopごとに余計な命令群でも挟まってるんだろう。この辺は、dumpoファイルを元に解析予定なので後述(たぶん)

Pythonがrange使ってる理由がなんか解る気がする。

 

AppleDevにはfor-inで書けとしか書いてなかったんで、swiftでは実質高速列挙なんだろうな。ObjCではenumerate...blocksと挙動が違ったような覚えあるけど。

ほんとはfor(;;)も比較対象に入れたかったけど、syntaxに無かったようなので断念。

この辺の考察も想像の羽を広げてる場合ではないので、後述。

 

gobjdump

gnuなのでgas, gcc同様、頭にgが付く。

gobjdump -S -d aStruct.out > objStruct.txt

こんな短いコードだけど、FPテクを使ってるからか全貌はかなりの長さ。

以下、callqで飛んでいる興味のあるところだけだらだらコピペする

0000000100002090 <__TFV7aStruct7runTest11letUsLoopinfT_T_>:
100002090: push %rbp
100002091: mov %rsp,%rbp
100002094: push %r15
100002096: push %r14
100002098: push %rbx
100002099: push %rax
10000209a: mov %rdi,%rbx

// reset()
10000209d: movq $0x0,0x4b10(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
1000020a4: 00 00 00 00

// self.myFunc() のはず
1000020a8: callq 100005314 <__ZL20fixStringForCoreDataP11objc_object+0xc9>
....

__ZL20fixStringForCoreDataP11objc_objectは0x10000524bとあるので、offset0x9cで

0x100005314へ。

100005314: jmpq   *0xdee(%rip)        # 100006108 <__ZL17_load_method_type+0x267> 

 

0000000100006108 <__DATA.__la_symbol_ptr>:
100006108: in (%dx),%al 

...

うーーん、anonymous関数ポインタのハンドリング...で、これ以上読み進める自信が無いので、すこしFP の手を緩める。

FPでは値も処理も区別しないため、何段階かアプローチはあるのだけれど、MAXひよってこうした。

 

もはや、関数ポインタを即値で代入しているだけで、何がしたいのかよくわからないが、おかげでdumpファイルが100K近く小さくなってくれた。

つまり固定値ラベルを追えばいいだけに。

 

いや、目的を考えたら、かっこつけずに初手からこうすべき話なのは解っていたのだが。。。FP+OOP脳に邪魔された。

 

stack操作はノイズだとして、各こういった対応になる

closed for -> av = 26.785226

for i in 0 ..< testValues.count {
        total += testValues[i]

 

0000000100002200 <__TF7aStruct6test01FT_T_>:

....

....

10000220b: mov 0x10(%rax),%rcx

10000220f: test %rcx,%rcx

100002212: je 100002235 <__TF7aStruct6test01FT_T_+0x35>

100002214: mov 0x499d(%rip),%rdx # 100006bb8 <__Tv7aStruct5totalSi>

10000221b: add $0x20,%rax

10000221f: nop

100002220: add (%rax),%rdx

100002223: mov %rdx,0x498e(%rip) # 100006bb8 <__Tv7aStruct5totalSi>

10000222a: jo 100002237 <__TF7aStruct6test01FT_T_+0x37>

10000222c: add $0x8,%rax

100002230: dec %rcx

100002233: jne 100002220 <__TF7aStruct6test01FT_T_+0x20>

100002235: pop %rbp

100002236: retq 

100002237: ud2 

100002239: nopl 0x0(%rax)

 オーバーフローでオペコード例外とか。

インデクスはrcxに最大値取ってからdecしてるけど、一般的な条件分岐jmpらしい。全てがレジスタで完結しているし、妥当な最適化がされているから速い。

 

open for ->  av = 59.826675

let max = testValues.count - 1
for i in 0...max {
        total += testValues[i]
}

0000000100002240 <__TF7aStruct6test02FT_T_>:
....
100002244: mov 0x4965(%rip),%rax # 100006bb0 <__Tv7aStruct10testValuesGSaSi_>
10000224b: mov 0x10(%rax),%rcx
10000224f: test %rcx,%rcx
100002252: je 100002287 <__TF7aStruct6test02FT_T_+0x47>
100002254: dec %rcx
100002257: xor %edx,%edx
100002259: nopl 0x0(%rax)
100002260: mov 0x4951(%rip),%rsi # 100006bb8 <__Tv7aStruct5totalSi>
100002267: add 0x20(%rax,%rdx,8),%rsi
10000226c: mov %rsi,0x4945(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
100002273: jo 100002283 <__TF7aStruct6test02FT_T_+0x43>
100002275: cmp %rdx,%rcx
100002278: je 100002285 <__TF7aStruct6test02FT_T_+0x45>
10000227a: inc %rdx
10000227d: cmp 0x10(%rax),%rdx
100002281: jb 100002260 <__TF7aStruct6test02FT_T_+0x20>
100002283: ud2
100002285: pop %rbp
100002286: retq
100002287: ud2
100002289: nopl 0x0(%rax)

このレベルだと、letによる参照透過性の違いは見られない。 

そして、cmp %rdx, rcxあたり怪しすぎる。rcxとrdx二本ふりまわしてdec,inc繰り返してるのか。

retqの前にud2が来てるのもなんかおかしい気もするが、こういうものだっけ?

-Oつけてもこれだと言うことは、..<はなんらか展開しくってるように思える。

実行速度も出てないし、開区間指定はまだこなれていないのかもしれない。

そもそも、for文は昔から0からはじまる閉区間なので、需要があるまではあまり力入れてないのかも

 

for-in  -> av = 27.640303

for v in testValues {
        total += v
}

0000000100002290 <__TF7aStruct6test03FT_T_>:
....
100002294: mov 0x4915(%rip),%rax # 100006bb0 <__Tv7aStruct10testValuesGSaSi_>
10000229b: mov 0x10(%rax),%rcx
10000229f: test %rcx,%rcx
1000022a2: je 1000022c5 <__TF7aStruct6test03FT_T_+0x35>
1000022a4: mov 0x490d(%rip),%rdx # 100006bb8 <__Tv7aStruct5totalSi>
1000022ab: add $0x20,%rax
1000022af: nop
1000022b0: add (%rax),%rdx
1000022b3: mov %rdx,0x48fe(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
1000022ba: jo 1000022c7 <__TF7aStruct6test03FT_T_+0x37>
1000022bc: add $0x8,%rax
1000022c0: dec %rcx
1000022c3: jne 1000022b0 <__TF7aStruct6test03FT_T_+0x20>
1000022c5: pop %rbp
1000022c6: retq
1000022c7: ud2
1000022c9: nopl 0x0(%rax)

あーなるほど。testValuesの次アイテムを取り出すのに、raxをアドレスぶんオフセットかけてるのか。 closed forと極めて似ている。処理時間も近似している。

 

while -> av = 59.986423
var test = testValues.count - 1
while test > 0 {
        total += testValues[test]
        test -= 1

<__TF7aStruct6test04FT_T_>:
1000022d0: push %rbp
1000022d1: mov %rsp,%rbp
1000022d4: mov 0x48d5(%rip),%rax # 100006bb0 <__Tv7aStruct10testValuesGSaSi_>
1000022db: mov 0x10(%rax),%rcx
1000022df: cmp $0x2,%rcx
1000022e3: jb 100002317 <__TF7aStruct6test04FT_T_+0x47>
1000022e5: test %rcx,%rcx
1000022e8: je 100002315 <__TF7aStruct6test04FT_T_+0x45>
1000022ea: dec %rcx
1000022ed: nopl (%rax)
1000022f0: mov 0x48c1(%rip),%rdx # 100006bb8 <__Tv7aStruct5totalSi>
1000022f7: add 0x20(%rax,%rcx,8),%rdx
1000022fc: mov %rdx,0x48b5(%rip) # 100006bb8 <__Tv7aStruct5totalSi>
100002303: jo 100002315 <__TF7aStruct6test04FT_T_+0x45>
100002305: dec %rcx
100002308: jo 100002315 <__TF7aStruct6test04FT_T_+0x45>
10000230a: test %rcx,%rcx
10000230d: jle 100002317 <__TF7aStruct6test04FT_T_+0x47>
10000230f: cmp 0x10(%rax),%rcx
100002313: jb 1000022f0 <__TF7aStruct6test04FT_T_+0x20>
100002315: ud2
100002317: pop %rbp
100002318: retq
100002319: nopl 0x0(%rax)
100002320: push %rbp
100002321: mov %rsp,%rbp
100002324: push %r14
100002326: push %rbx
100002327: mov 0x45d2(%rip),%rbx # 100006900 <__ZL12demangleLock+0x40>
10000232e: test %rbx,%rbx
100002331: jne 1000023b9 <__TF7aStruct6test04FT_T_+0xe9>
100002337: mov $0x20,%edi
10000233c: mov $0x7,%esi
100002341: callq 1000054b8 <__ZL20fixStringForCoreDataP11objc_object+0x26d>
100002346: mov %rax,%rbx
100002349: mov 0x3cd8(%rip),%rax # 100006028 <__ZL17_load_method_type+0x187>
100002350: mov %rax,(%rbx)
100002353: mov 0x4496(%rip),%rax # 1000067f0 <__TMLFT_T_>
10000235a: test %rax,%rax
10000235d: jne 10000237e <__TF7aStruct6test04FT_T_+0xae>
10000235f: mov 0x3cda(%rip),%rsi # 100006040 <__ZL17_load_method_type+0x19f>
100002366: add $0x8,%rsi
10000236a: mov $0x1,%edi
10000236f: mov %rsi,%rdx
100002372: callq 100005488 <__ZL20fixStringForCoreDataP11objc_object+0x23d>
100002377: mov %rax,0x4472(%rip) # 1000067f0 <__TMLFT_T_>
10000237e: mov %rax,0x8(%rbx)
100002382: movq 0x3ca6(%rip),%xmm0 # 100006030 <__ZL17_load_method_type+0x18f>
100002389: pshufd $0x44,%xmm0,%xmm0
10000238f: movdqu %xmm0,0x10(%rbx)
100002394: xor %eax,%eax
100002396: lock cmpxchg %rbx,0x4561(%rip) # 100006900 <__ZL12demangleLock+0x40>
10000239d: mov %rax,%r14
1000023a2: je 1000023b9 <__TF7aStruct6test04FT_T_+0xe9>
1000023a4: mov $0x20,%esi
1000023a9: mov $0x7,%edx
1000023ae: mov %rbx,%rdi
1000023b1: callq 1000054be <__ZL20fixStringForCoreDataP11objc_object+0x273>
1000023b6: mov %r14,%rbx
1000023b9: mov %rbx,%rax
1000023bc: pop %rbx
1000023bd: pop %r14
1000023bf: pop %rbp
1000023c0: retq

やめてくれ!もはや見る気がしない。zipはfuncに立てるのを忘れたが、なんか皆察しの通りかと。

 

objdumpからニーモニック人力評価のための環境構築というGoalは満たせたし、

長くなりすぎて何がしたいのかが不明瞭になってきたので、今日はこの辺にしておこう。

 

 気が向いたら次は今回大幅に日和ったラムダ式の追い方か、いきなり核心を突いて継承とprotocol+extesionの違いを見てみよう。

swift de classDriven その1 virtual関数と具象実装

どんなにFPを極めようと、結局中でやってることはblocksなので、FP恩恵の一部しか受けられない、FPの何が嬉しいかが解ってればまずObjC + blocksで書き下せる、Cocoaが結局ObjC目線なので、エンジニアは何かしらCocoa + ObjCとのコンパチで軋轢を被る、おかげでかっこつけて実装した参照透過性すらだいなし。なにやってんだか。
とはいえApple様はもうアランケイってだれ?状態で、罪をNextとObjCなんならPARCにもかぶせて捨て去りたくて仕方ない、そして最も厄介なことにswiftは教育含め、特にwanabe相手に金になる。
 
そんな、言語仕様界のあばれはっちゃくswiftに時間を見つけて着手していこうかと。
ObjCのときは、OOPなら当然実装されているべき仕様が、変な小技使わないとできなかったり、結構はまりポイントだったので、その辺から。
 
いやはや、FPを知ってればOOPの代わりになると勘違いしてる輩が多いけど、ScalaやらOcamlやら、今OOPとFP組み合わせのパラダイムシフト中だから。
OOP極めてないと、いくらFP学んでも何の恩恵も得られないよ?というメッセージをこめつつ、とりあえず、初回はFBで先行で出した記事2件のコピペ

 

2016/4/16 晴れ

swiftで継承構造とabstractメソッド的なことやってみた。

例えばクソほどボタンが必要なときに、実装さえあるなしに応じて1メソッドで分岐する。

あれば、具象実装末端まで落ちていく

なければ、default実装へ。

default実装が存在するので、厳密にはstroustrupのvirtualなんだけど。

CallerClassでassert(sender.action())かかるよう、UITestで全叩きさせるとリリースストッパにできていいかも。
なお、Javaがなぜファイルで明確にひとつずつの処理単位(actor)を分けられるようになっていて、それの何が嬉しいとして言語設計されているのかよくわかんない人にはお勧めしない。

多段継承したとき、ObjCみたいにKVSで自前管理しなくていいぶん素直かな?
結局実体はKVS管理なんだとは思うけど。。。

 

 

 

2016/4/17 曇り

swiftで...その2
多少ググってswift流ならどうやるか調べてみた版。
こっちの方が、abstっぽくはなったが。。。
ちがいは、まだ構築中で未実装関数にPCが当たったときに、@noreturn関数内で完全停止するか、何らかdefault処理が入るか。
言うなれば、その1はvirtual(C++)、その2がabstract(Java a.k.a C++ pure virtual)といったところ。

好みと言うだけだけど、出荷後にやっちまったことが発覚したときのFS性から考えるとvirtualの方だろうなぁ。

 

Releaseで関数ごと入れ換えればいいだけなので、リリース、テスト、デバッグの各コードを疎に保てる。

 

テスト中に失敗検出のため、わざと落とすのと、デリバリ後にユーザの手元で落っこちるのとでは目的が全然違うからなぁ。

僕のインチキ設計哲学では、目的に合わせて極小影響粒度でオブジェクトやら処理やらを入れ換えるというのが好み。

abstは実装段階で忘れず全部梱包しろよってことだと解釈してるので。

 

個人的には、virtualでもabstractでもあまり興味は無く、言語設計つまりOSとの親和性として、どちらがCocoa設計に即しているかだけなんだけど。

 

swift blogみてもそういう所は出てこないんだよな。

 

 

Cocoaで統計処理 四分位数実装編

Objective-C, 仕様検討, 個人的見解, 思案中

元OS屋から見て普通に考えるとCocoaでは演算高速化のため、NSExpressionによる統計関数を用意している(のだと思う)

なぜなら、開発サイドから見ればそうすることにより

  • 開発者が独自で変な実装しだす確率を下げる (テストしてないところをがんがんほじくり返されるから。何が起きるか解らないため、提供者としてはむっちゃいや。特に演算処理は高速化を容赦なく求められるところなので、開発者は1命令でも削りたくてうずうずしている)
  • 自分たちの都合に合わせて、実装を自由に変えてリリースできる (当時ああは言ったけど、経営者の気が変わったときとか、頭のおかしいエンジニアが新製品や論文の売り文句に騙されて昨日と全く違った実装をこっそり入れてリリースしたくなったとか。)
  • なんかややこしいH/W仕様を使いたくなったらベンダー最適化し放題  (リリース時は時間に追われ、仕様もよくわかんなかったけど、よくよく見たらむっちゃ便利機能が見つかった。よくある。本当に。)

なんて戦略が取れるから。

しかしググった感じ、iOSではNSExpression#expressionForFunction@”median:”は使えない?なんてレポートもちらほら…いや、中央値出せなくてpercentile出すのはめんどくさい。なにより統計コードの天敵は計算ミス。

 

中央値算出くらいなら奇数サイズなら配列の真ん中、偶数サイズなら中央2項の算術平均取るだけなので、簡単でバグも入りにくいから自分で書いてもいいけど、sciPyが使えれば演算速度以外のほぼ全ての問題は解決するので、iOSへのPython組み込みとのProsVSConsに関しては非常に悩ましい&#12290 ;

ちなみに、自分の間抜けさを熟知しているプログラマほど自分で書くことを避ける。なにより、世の中で既に誰かが書いてて、公開されてて、かつ動いてるコードを僕がスクラッチから起こす必要はどこにもない。

 

とも思いつつ、だめもとで

NSExpression#expressionForFunction@”median:” argumentsSmilie: :(NSArray*)…

使ってみた。

—– [XXX:XXX] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[theObject compare:]: unrecognized selector sent to instance 0xXXXXXX’

いやいやいや…自分の間抜けさを実感させられただけでした。このログはtheObjectを直比較してるので、どの属性で比較したいか解らんと怒られてます。

それだけ?使えんじゃん!

 

ちなみにiOS8.4 SDKのヘッダにはこうコメントされている

+ (NSExpression *)expressionForFunctionSmilie: :(NSString *)name argumentsSmilie: :(NSArray *)parameters;
// Expression that invokes one of the predefined functions. Will throw immediately if the selector is bad; will throw at runtime if the parameters are incorrect.
// Predefined functions are:
// name parameter array contents returns
//————————————————————————————————————————————-
// sum: NSExpression instances representing numbers NSNumber
// count: NSExpression instances representing numbers NSNumber
// min: NSExpression instances representing numbers NSNumber
// max: NSExpression instances representing numbers NSNumber
// average: NSExpression instances representing numbers NSNumber
// median: NSExpression instances representing numbers NSNumber
// mode: NSExpression instances representing numbers NSArray (returned array will contain all occurrences of the mode)
// stddev: NSExpression instances representing numbers NSNumber
………. 

 つまり、公式に記述のあるOSX 10.5 laterにくわえ少なくともiOS8.4では最頻値まで使えるんじゃないか?

インチキ英語しか身につけてないので読み飛ばしただけかもしれないけど、だったらそう書け…

 

アルゴリズムとしては

  1. CoreDataから全アイテムをソートした配列にして取り出す
  2. 全配列から中央値出す -> 2Q
  3. lastObject -> 4Q
  4. 全配列前後を再帰させる -> 前1Q、後3Q

以上。ヒンジならこれで必要十分なはず、あとはこれをネタ元に配列が更新されたら四分位数再設定

sciPyのscoreatpercentileみたいにするなら四分位数からバイナリ探索して何percentileか出す。

レコード数が大きいから毎回Zone評価してリンク組み替えよりは算術探索の方が速い(はず…Smilie: ;) 。ステップ数は測ってないけど、NSSetのcontainsObject:は意外と遅いので…内部でループ回してるんじゃなかろうかと思うくらい。

テスト用に組み込んであるPythonの検算コードとも値は一致したので、とりあえず一件落着?

pyObjC組み込んだはいいけど、全然速度が出ないなんていう事態が怖くていまいち腰の重い僕でした。



from WordPress
How to Struggle Against S/W
http://ift.tt/1Ns1dKL

久々の投稿

長らく離れていたが、リンクをたどるとどうやら拙作のMarkdownエンジンAoBaneのDL数を稼いでいるもととなっているのはこのサイトらしい(仮)説が浮上してきたので、後日談と近況報告を兼ねて投稿。

AoBaneのDL数はおかげさまで今日現在24,767。割と稼いでる方だとは思う(全くお金にはなってないけど)。 ただ、たぶん以前の記事にもあると思うけど、このAoBaneの開発は止めてある。 理由はコストに見合わないから。AoBaneはRubyで書かれたBlueFeatherというエンジンがBaseになっていて、そこを部分的に間借りして<span>なんかを使って文字に色を付けたり、マーカーを入れたり、記号変換したり...してるわけだが、これが結構しんどい。

PHP MDExtraみたいな複雑な記述法を改変したり追加しようとしたら、仕様1つにつき基になっている記述を理解して、改修箇所を見極めてたら、改修とテストを含め一日仕事になる。そこで目を付けたのがScalaというFP言語。

試しにAoBaneの仕様を吸い上げてAkkaとか外部フレームワークも使わずフルスクラッチで書いたら1日で動き出した。しかもコード量も圧倒的に少なく、メンテナンスもしやすい(メンテナンス性は設計(表現法)に大きくよるので、表現力以外はScalaがすごいわけではないけど)。

 

ということで、SourceFourgeを主戦場に移して公開してるのがBlackQuillというScala製MarkDownエンジン。こっちの方が忠実にExtraの記法を実装してあり、追加機能も豊富、Markdownとしても表現力が高いので自分の書き物はもうこっちしか使ってない。

ただ、sbtとか環境を導入するのが面倒なためか、AoBaneよりはるかにパワフルなのにこっちは全くの閑古鳥。まぁ世の中そんなもん。AoBaneはgem installするだけだから...

 

一時期はMac AppstoreにGUIエディタ込みでBlackQuillを出そうともしてたけど、全然審査が通らない。2,3日でしびれを切らせて担当レビュアーに"じゃぁどうすりゃ通るのよ!"となかばプチ切れの英語で問い合わせたらjreをアプリにバンドルしないと通んないよだと。それもうJVMの利点を見事に殺してる...つまりMacアプリとしてJVMを使えば、アプリの数だけJVMがメモリ上に居座る。

まぁ、当時はAppleJavaのシステムサポート切るよとオフィシャルで言った直後だったし、ちょっとした入れ違いもあったとはいえ、みるみるやる気をなくして未だitunesConnectにアップロード待ちのステータスで放置されている状態。

 

しかし、最近になってまずいことが起こった。(つまりここからがこの投稿の本題。)

FireFoxでも数式が見れない! 長いこと数式を使うような書き物がなかったため気づかなかったが、MathMLになにか変更が加わったのか、インポートしてる外部ライブラリに何かあったのか...。もう2年も前に描いたコードなので、そのレベルで見る気も起こらず、数式ジェネレータ部分は作り直すことに事にしました。

この投稿はその決意表明みたいなもの(ここまで読んで失望したした人ごめんなさい)です。ここ2日ほど下調べした感じ、2年前当時と違うのは幸か不幸か割とTeXを画像変換かつ多言語を埋め込めるようなWebサービスが出てきているところ。別サービスだけどついでにグラフ描画なんかもできるところもあったりする。

これならTeXを書いておいて実は内部的には<img>で画像埋め込みとかできるんじゃないか?というのが今回の改修の基本アプローチ。

本当はWolfram αが使えると1サイトで完結するからいいんだけど、叩いてみたらProにしろ(つまり金払え)ときたので今回はたぶん見送り。これに関しては利害関係が一致しないの一言につきる。あと、ユーザにMathematica構文を強要するのも雑だし、過去に書いたものを再変換するときにここの構文が変わってしまうといけてないので、コンバータが必要になるというジレンマ。ただ、数式だけならTeX構文をそのまま読み込むような機能もWolframにはあるのでやろうと思えばなんとかできそう。

 

何にせよ、MathMLはあまりに複雑でHTML5に変換されたときなに言ってるかさっぱりわかんなくなるので、シンプルな方に大きく倒すことにしたというお話。

あとはMarxicoみたいにMathJaxを使うアプローチにするか...少なくともMathMLを使っちゃったのが失敗だったというのは設計者として素直に認めるところではある。

ただ、できることならTeXも読んでて直感的になに書いたかわかりにくいから離れたいんだよなぁ。

OSX でSynapse2.0を使用する際のコツ的なもの

Razerのクソドライバ Synapse2.0http://www.razerzone.com/jp-jp/synapse2 の出来の悪さに僕の中の大事な何かがブチ切れて、アイコン制作を一時中断、持ってるマウス全部引っ張り出して、徹夜でいろいろ試して見たけど、やっぱりそれぞれ譲れない問題がある。
悔しいけど、やっぱりRazerのマウスが一番しっくりくるので、いろんなこと妥協して、Synapseを使いこなすことに方針を定めた。
 
同じ実験や同じ検索は二度しない主義なので、雑時間集めてSynapseの飼い慣らし方(ここではOSX10.9.3 Ouroborosを主な対象とした)をまとめてみた。
 
ちなみに、「Razer,Synapse,mac,osx,不具合」 など適当なキーワードでググった限り、ネットの情報はほとんど文句のはけ口ばかりで、使いこなしているらしき記事は皆無だった。
本当に困ってんなら何の利得もない文句でアフィってないで実験して考察を公開してくれませんか?
まぁ、無料(アフィられてるけど)の情報なんて過度の期待はしちゃいけないということ。
 
本件で最も腹立たしいのはRazerが不具合情報はおろか、FAQすら用意していないところなのだが・・・。
Macで全ボタンアサインして使えるようならSteelseriesあたりに乗り換えてやろうか。
頑固な会社なので、未来永劫ワイヤレスは出しそうにないけど。
 
Tip #1: Synapse2.0に最初に認識させるには、ワイヤードモードでなくてはいけない
Tip #2: ひとつのアカウントに設定が沢山(僕の場合は、Ouroboros以外にNaga HEX mobaとNaga 2014の設定とMacroが山のように入っていた)入っていると、認識に時間がかかったり、うまく反映されないことがあったり。結局これが不具合の根っこの部分かもしれない。
→ 別のメアドを使ってOuroboros用に別アカウントを用意した。これだと、マウスを変えると都度ユーザーを切り替えて使用しないといけないが、それほど頻度の高いシチュエーションでもないので、その点における利便性は諦めた。
Tip #3: 無設定の状態だと、ポインタが飛びまくるので、Performanceから設定していく
Tip #4: Show Application (^ + ↓)がMacroでもShortcutsでも設定が効かない・・・これはOuroboros だけかもしれない。
→Macroで適当にdelayを入れて'↓'を'^'で挟むように設定したらうまくいった
Tip #5: 認識してない状態で、Synapse2.0の左下にOuroborosの画が出てる場合は、それをクリック。
Tip #6: ここまでやっておいてワイヤレスに切り替えると、Performanceが異様なほどセンシティブにかってに書き換えられていたので、ワイヤレスモードに移ってから再度設定しなおさなければならない。これ以外の設定は書き換えられてないので、心配する必要はない。
Tip #7: 念のため作った設定を適当なローカルファイルにexportしておく。
 
うーん、Synapse2.0もアカウント別に用意すれば、割と素直でいい子なのかもしれない。