Just $ A sandbox

プログラミングと計算機科学とかわいさ

【宣伝】C92で「簡約!? λカ娘 10」が出ます

ほーむ - 参照透明な海を守る会

http://www.paraiso-lang.org/ikmsm/images/c92-cover.jpg

C92にてサークル「参照透明な海を守る会」より「簡約!? λカ娘 10」が出ます。 1日目 金曜日 東た11b だそうです。

今回私も初めて参加させていただきました。


簡約!? λカ娘 10 - 参照透明な海を守る会

モナドとひも」というタイトルで、String Diagramの気持ちをなんとなく理解してもらえればいいな〜という感じの文章を書いたつもりです。Haskell一応書けます、ぐらいの人が対象です。

予備知識なしでString Diagramの計算とか色々見れたらよいなと思っていたのですが、まーそうそう上手く行く例があまり見つからず、あとTikZで図を書くのが死ぬほどつらかったので易きに流れた結果内容が薄くなりました。

一応ウェッブサイトにてサンプル版が見れるので(他の人のもちょこっと読めます)興味あれば見てみてください。

こういう文章書くのはあんまり経験ないんですけど割と楽しかったです。もし次があるならもう少し構成とか諸々考えておくべきだなと思いました。

宣伝おわり。

foldって難しくないですか

foldって難しい、というか書きづらくて読みづらくないですか、と思った話を書きます(そういう感じの話題でTLが盛り上がってたので)

以下、foldをrecursion schemeの観点から理解していることは前提とします

foldをかく

foldを書きたい

foldを書こうと思う場合は自分の場合は明白で、可換monoid積で畳むときは必ずfoldで書きます

sum = foldl (+) 0

これは入力と出力の型が一致していて細かいことを考えなくて良いし、何しているのか見れば分かるし、畳み込み感があっていい感じだし文句はない

ただこういうのは得てしてすでに欲しい関数が定義されてたりする

foldを書きたい??

これがアキュムレータ付き再帰をしたい場合になると若干微妙で、

f xs = go xs 0 where
  go [] acc = acc
  go (x:xs) acc = go xs (x @@ acc)

-- ↑これが
-- ↓こうなる

f = foldl (@@) 0

上が下になるわけですが、下の方が見やすくていいやんけ、と思うじゃないですか? しかし下をパッと書くためには困ったことにfoldlの第一引数の型を暗記している必要があるので私はググるコンパイラがある場面でしか自信を持って下は書けません。

もちろん上より下の方が「良い」コードであることは明白ですが、悪いコードの方が圧倒的に自信を持って書けるのなんかおかしくないかって思いますね。

foldを書きたくない

畳み込み関数がより複雑になる可能性のあるケースでは自分はもうfoldは使いません。それが永久に修正されない、あるいは計算量を保証したいとかでなければもう再帰でいいと思っています。

そもそもこれ以上複雑なケースの場合は自分は一旦再帰の形を経由してしかfoldの処理を理解できないので、わざわざそれを頭の中でやるくらいならおとなしくコードに書けばよいのではないかという気持ちになるからです。

f = go where
  go [] = a0
  go (x:xs) = (なんかめっちゃ複雑な式) $ go xs

-- ↑元の形
-- ↓アキュムレータで書き直す

f xs = go xs b0 where
  go [] acc = acc
  go (x:xs) acc = go xs (さらに複雑な式 x acc)

-- ↓foldで書き直す

f = foldl (さらに複雑な式) b0

例えば一般に何も考えていない時は一番上の書き方をしていて、それをfoldに変えようとすると一旦アキュムレータを使う形に変える必要があります

というわけで真ん中の再帰の形を経由してから一番下に変えるわけですが、一番上から一番下には自分は脳内だけでは変換できません。(脳内ワーキングメモリー足りない人類なので)

そもそも一番下のコードを読むときにも脳内で真ん中の形に展開しないと自分は読めないので、常に真ん中経由なら真ん中でよくないか?ってなるし、計算量とか気にしないならむしろ一番上でいいでしょって思うわけです

この辺は好みや脳内ワーキングメモリーとの相談になりますがプログラムを書く際に極限まで脳みそを使いたくない自分のような人間はこういうケースではまずfoldを使いません。

また、一番上から真ん中は比較的簡単に修正可能ですが、再帰部を色々いじる可能性がある場合も一番下の式で再帰部の形をごりっと書き換えたりするのはかなり大変なので、あとで修正が入りそうな関数も敢えてfoldつかわなかったりします

foldは何がしんどいか

個人的にはこういうことだと思ってる。

foldのつらみを緩和

さて上の3つの問題は例えばこんなふうに書けると割と楽だと思います。

fold {nil: g0} {cons(x): \acc -> gn x acc} xs

まぁ当然タイプ数が増えるわけですが、しかしfoldの引数とnil,consのデータコンストラクタとの対応が見えやすいのでわかりやすいよね? しかもこれなら一般の代数的データ型に対していい感じのsyntaxが提供できるし!(その場合foldという名前はいかがなものかと思うけれど)

しかし当然Haskellではこんなsyntaxはないのでどうしようもないけれど…じゃあ妥協してこういうのでどうかな

instance TaglessList b where
  nil = acc0
  cons = \x acc -> gn x acc

おっ…これならまだ…ってお前tagless finalやんけ!!!

こんな面倒なことするくらいなら素直にfoldでよくないか

ここまで読んだ上でなお「こんな面倒なことするくらいなら素直にfoldでよくないか」と言えるのは多分脳内ワーキングメモリーが有り余ってる人なのでメモリー分けて欲しい

まとめ

  • fold難しい
  • 避けたいし再帰で書きたいけどパフォーマンス上の問題やらを引き起こす可能性が高まるかもしれないことは常に意識しておくべき
  • Haskell(に限らず大体どの言語もだけど)のsyntaxが悪い説もまぁある
  • tagless finalは便利(ただしこれだけだとリストリテラルで表現されたデータは手に入らないので使うときは注意)
  • 頑張って慣れていきましょう

ゲーム進捗報告会その5

更新忘れたわけでもプロジェクトが崩壊したわけでもないぞ

進捗報告

  • スタイルシート: デザインに関するあれこれは全部リソースファイルで記述できるようにしたい、と思ったので、画面上のWidgetにidを割り振り、対応するidに関連するコードをファイルから読み込んでrendererを書き換えるということをやっていた。ひとまずそれっぽいところまできた。
  • Widget: タブビューとスクロールビューの実装とかをした
  • 調合システム: 調合システム少し手直ししたり色々やった気がするけど忘れた()
  • 戦闘システム: 戦闘システムちょっとあれこれやって状態異常の実装とか攻撃ポーション使えるようにしたりしてた
  • 実績関連: 実績解除したらバルーンが出る、みたいなのができるようにした
  • 主要キャラクターの名前を決めた

スタイルシートについて

message-layer >> message-writer {
  margin: 20, 10
}

maintown {
  _ {
    margin: 0, 10
  }

  script-engine > message-layer {
    position: 0, 450
  }
}

例えばこんな感じでidを指定してmarginとかpositionとかを設定できる。CSSっぽいsyntaxだけど入れ子が出来たりする。雑にパーサーをかいたらこういう形に落ち着いた。

margin, positionぐらいしかまだ対応してないけど

開発ツール

いくつか新しく開発ツールを導入した

  • hpack: stack連携のあるhpackを使うことにした。 package.yaml.cabal より色々楽に書けていい感じ。
  • just: Rust製のalternative makeみたいなやつ。Makefile一から書いたことないしまぁ何でも良かったんだけどなんかよさげだったので。

その他

  • 早くBGMとSEを導入したい(はやくしろ)
  • スプライト画像読み込んでアニメーションみたいなのもそろそろ対応しつつ素材探したりするべきか
  • あとモンスターのパラメータ生成とかどうにかしたい いい感じにアレして自動生成とかでいいんだろうか
  • https://matome.naver.jp/odai/2134066735815545701 自動生成ツール関連
  • 酒場で簡単なお使い系クエストを受けて報酬をもらう、みたいなやつ入れたいなぁって思ったけど思っただけ(工程増やすのは一旦後回し)
  • 戦闘関連は地道にパラメータ調整頑張るしかないかなぁ この辺のゲームバランス調整難しすぎるからどうにか上手く勧める基準とかコツが知りたい所

TODO

TODOリストは110/175(消化率62%) やっと6割か(作業的には4割くらいか?)