Minecraft 1.14サーバーを運用してみた
Minecraft 1.14 "Village and Pillage"は、サブタイトルの通り村人と略奪者をテーマにしたアップデートだ。
主な楽しみ方
村人の取引システムが一新され、以前よりもバリエーションに富み、かつリーズナブルな取引ができるようになった。余ったアイテムを換金したり、有益なアイテムを入手できるようになるだろう。 ランタン、焚火などの新たな光源や、壁や階段の変種、さらには鐘なども追加され、建築の楽しみも大きく増した。だが、良いことばかりではない――新たなイリジャー(邪悪な村人)、ピリジャーが出現するようになったのだ。条件を満たすと発生する襲撃から村を守る死闘、そして安全な拠点づくりという課題が生まれた。これを乗り越えれば、村の英雄としての賞賛が待っている。
注目のアイテム
砥石
装備につけられたエンチャントを剥がし、経験値として回収することができる。今まで、中途半端なエンチャントのついたアイテムはゴミ扱いだったが、これがあればエンチャントが気にくわなくても再利用できる。
石切台
今までは階段を4つ作るのに6ブロックが必要だったが、石切台を使えば1:1の比率でクラフトできる。模様付きの石レンガなども原料から一発で得られるのも嬉しい。
コンポスター
植物関係のアイテムを、わずかではあるが骨粉に変換できる。余りがちな種子や木の葉などを処分するのに便利だ。
クロスボウ
エンチャントを考慮すると弓よりも攻撃力は低いが、速射・拡散のエンチャントを与えれば高いDPSを叩き込める新たな武器。花火の玉をガン積みしたロケット花火を打ち出すことで恐ろしいダメージが出せる。
足場
竹と糸でクラフトできる新たなブロック。好きなだけ高く積み上げることができ、自在に上り下り可能で、一番下を壊せばすべて回収できるという、建築に非常に便利なブロック。
主な設備
自宅
3LDKの比較的簡素な住宅。住民がほとんどの資材をここに置いているため、実質的にここがメインの拠点となっている。
フォーラム
名目上はギルドの本拠地。村人たちが働く場所で、かつては交易の拠点として賑わっていたが今は最小限の村人しか通っていない。武器や弾薬などが格納されている。
昆布・竹自動栽培機
昆布も竹も、ピストンで押し出せば刈り取れるので収穫の自動化が容易だ。この装置のポイントは燃料の供給にある。
昆布をかまどで焼くと乾燥昆布になり、それを9つまとめると昆布ブロックになる。昆布ブロックは20アイテム分を焼くことができる燃料になり、これは溶岩バケツ、石炭ブロックに次ぐ効率で *1、再生可能資源としては最高である。街に響くガシャコンという作動音と共に、世界のエネルギーをまかなっている。
自動釣り堀
最凶クラスの装置。右クリックを押しっぱなしにすることで、以下のようなサイクルによって釣りを繰り返す。
- 感圧版により、釣り竿を使用している間鉄のトラップドアが開く
- 音符ブロックにカーソルを合わせている間、釣りを維持する
- 獲物が引っ掛かると感圧版が解除され、鉄のトラップドアが閉じる
- 鉄のトラップドアを右クリックしても何も起こらないため、釣り竿のアクションが優先されて釣り上げる
魚やゴミが大量に釣れるだけでなく、Fishing – Official Minecraft Wiki に書かれている通り、強力なエンチャントを伴った弓や本なども得られ、その質はエンチャントテーブルによるエンチャントを上回る。パワー4耐久3が付いた弓はザラで、通常のエンチャントでは得られない《束縛の呪い》や《修繕》も入手できる。 一晩放置すれば40くらいまでレベルアップするだけでなく、使い道に困るほどの量のエンチャント本が得られるだろう。食料、経験値、エンチャントを無限に供給できる設備としては、あまりにも簡易かつ低コストすぎる。不要なものを砥石で削れば、さらに莫大な経験値と本などの資源が回収できる。
栽培プラットフォーム
汎用作物収穫プラットフォーム「エミュー壱号」のおかげで安定した収入が得られるようになった pic.twitter.com/xIMNU3YesM
— ふみ (@fumieval) May 15, 2019
ピストンとオブザーバーによるフライングマシンを往復させ、サトウキビなどを自動で収穫する。地下にはホッパー付きトロッコが走っており、刈り取ったアイテムが収穫される。竹やカボチャ、スイカにも使える。
天空TT
虹色の超高層ビルの最上階には、ゾンビ、スケルトン、クリーパー、ウィッチを対象としたトラップタワーが存在する。いわゆる24-32式のクラシックな構成だが効率は申し分ない。村人を何人か住まわせており、アイテムをすぐに交換できるようになっている。
羊毛工場
土を循環させることで効率よく草を再生させる羊毛工場 pic.twitter.com/8ESc8APEGc
— ふみ (@fumieval) 2019年6月14日
ハサミを入れたディスペンサーによって羊毛を刈り取り、ピストンで一辺11ブロックの土を循環させる。隣接する草ブロックを増やすことで草の再生を加速させ、羊毛を効率よく取り出せる。機構はレッドストーンリピータとトーチを用いた簡単なもので、土が来ると通電(?)してピストンで押し出される。 注意点として、この機構がチャンクをまたいでいると、一部だけが読み込まれておかしな状態になることがあるので、1チャンクに収まるような場所に設置すべきである。
丸石工場
溶岩流と水没した階段から生成された丸石をピストンで押し出し、複製したTNTで破壊する。Minecraft 1.14から、TNTで破壊されたブロックは100%ドロップするようになったため、極めて効率がよい。
全自動丸石製造機 pic.twitter.com/UTNpWW9TU7
— ふみ (@fumieval) August 15, 2019
精錬・集積・取引所
以上の設備で生産したアイテムを地下水路に流し、精錬可能なものは精錬しつつ仕分ける。そしてアイテムをその場で村人に売却することで莫大な利益を得る。忘れられがちだが、自動化してもかまどには経験値が溜まるため、時々かまどのレバーを下げてアイテムを取り出すことでレベル上げもできる。
サーバーの構成
レイヤー低い順に以下の通り。
当初はRAMは2GBだったが、かなりパフォーマンス面に難があったためスケールアップした。5人ほどのプレイヤーがいてもそれなりに快適に動作する。
起動スクリプト
#!/bin/sh java -Xms2G -Xmx2G -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=100 -XX:+DisableExplicitGC -XX:TargetSurvivorRatio=90 -XX:G1NewSizePercent=50 -XX:G1MaxNewSizePercent=80 -XX:G1MixedGCLiveThresholdPercent=35 -XX:+AlwaysPreTouch -XX:+ParallelRefProcEnabled -Dusing.aikars.flags=mcflags.emc.gs -jar paperclip-163.jar
所感
村人関係の機能が非常に充実したため、より多くのエメラルドを稼ぐという目的でもなかなかやりがいがあり、Villager Trade Generator (Java Edition 1.14)などでコマンドを使えば、独自の取引メニューを持った村人も作れる。トライデントがあまりにも入手困難だったため、エメラルド64個で販売する村人を作った。
装飾関係のブロックが数多く追加されたため建築もはかどり、足場ブロックがそれを後押しする形となった。今のところほとんど遊びつくした感はあるが、Minecraftは次々と新しい要素を追加しているため、次のバージョンでも楽しめると期待している。
Traversable API
与えられたConnection
を通じて、指定したKey
に対応するByteString
を取り出すような、シンプルなKey-ValueストアのAPIを考えてみよう。
type Key = ByteString fetchOne :: Connection -> Key -> IO ByteString
ネットワーク越しにたくさんのデータを取得したいとき、何度もこれを呼び出していては効率が悪い。一度にまとめて取り出せるように拡張するなら、このように書ける。
fetchMany :: Connection -> [Key] -> IO [ByteString]
悪くはないが、この型はたとえば「["foo", "bar"]
を要求したのに返ってきたのは[]
」のような振る舞いを許してしまうため、使い手に不必要なパターンマッチを強いる。だが、リスト[]
にちょっとした一般化を施すだけでそれを防ぐことが可能だ。
fetch :: Traversable t => Connection -> t Key -> IO (t ByteString)
リストが任意のTraversableになっているのがミソだ。TraversableはFunctorとFoldableのサブクラスで、各要素に対して作用を伴って関数適用し、元の構造を保ったまま返すような関数、traverse
を持つ。パラメトリシティのおかげで、勝手に要素を追加したり減らしたりするような振る舞いは許されない*1。Identity、Maybe、[]のような型はもちろん、data V3 a = V3 a a a
のような固定長のコンテナもインスタンスになる。
class (Functor t, Foldable t) => Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) ...
これだけでもfetchOneとfetchManyの一般化になるが、最近流行りのHKD(higher kinded datatype)とのコンボでさらなる力を発揮する。HKDは、パラメータとして与えられた型で各フィールドを包むことによって、同じデータ型に複数の役割を与えられるような手法である。例えばConst String
で包んで各フィールドの名前を表したり、Parser
で各フィールド用のパーサーを定義することができ、Identity
なら当然通常のデータ型と同型になる。
data User h = User { userId :: h Int , userName :: h Text } userFields :: User (Const Key) userFields = User (Const "userId") (Const "userName")
barbiesのData.Barbie.Containerは、このパラメータがConst
な場合にTraversable
として使えるようにする。
newtype Container b a = Container { getContainer :: b (Const a) }
userFields
をContainer
で包めばfetch
に渡せて、形を保ったままByteString
のレコードが返ってくるので、あとは煮るなり焼くなり好きにすればいい。
Container result :: Container User ByteString <- fetch conn $ Container userFields let user :: Maybe (User Identity) user = btraverseC @FromJSON (fmap Identity . decode . getConst) result
リクエストと同じ形のレスポンスが得られるという性質はGraphQLにも通じる。このような関手ライクなインターフェイスは、今後のAPI設計の鍵を握っているかもしれない。
*1:厳密には順番を入れ替えることは可能だが、そのような使い方はまずありえない
楽園へ行きたい
楽園へ行きたい。
森と平原に囲まれた、街のはずれの小屋に住みたい。
朝は、小鳥たちのさえずりと窓から射し込む陽の光で目覚めたい。
昼は、コーヒーと焼き菓子を用意して一服したい。
夜は、天の河の向こうに思いを馳せながら眠りたい。
月曜日は大学に行き、エルフの先生の下で言語学を学びたい。
火曜日は研究室にこもり、ドラゴンの教授と研究に没頭したい。
水、木曜日は道具鍛治と修繕の仕事をしたい。
金曜日は都に向かい、品を売って食材と情報を仕入れたい。
土曜日は酒場に集まり、仲間たちと杯を交わしたい。
日曜日は使い魔を連れ、公園をゆったり散歩したい。
春は、花々を眺めながら、渡り鳥たちにしばしの別れを告げたい。
夏は、祭りの企画に参加し、市民を楽しませたい。
秋は、旬のものでご馳走をたくさん作りたい。
冬は、客を呼んで鍋をつつきながら新年を迎えたい。
そんな楽園で私は暮らしたい。
特級シリアライズライブラリ、winery 1.0解禁
あれから9ヶ月…wineryのバージョン1.0をついにリリースした。
前回までのあらすじ
データの保存や通信に直列化は不可欠の概念である。 binaryなどの直列化ライブラリは、レコードのフィールド名などの情報が欠けており、構造が変わると互換性を持たせることができない。 一方、JSONやCBORなどのフォーマットで愚直にフィールド名などを残すと極めて冗長になり、時間・空間効率が悪い。 コード生成が前提のProtobufなどはHaskellの既存のデータ構造との相性がよくない。 そんな現状に殴り込みをかけたのがwineryだ。値を「スキーマ」と「データ」に分割して保存することによって、冗長性を避けつつ、メタデータを保持させることができる。wineryは最強のライブラリとなりうるか…?
特徴と特長
JSON, MessagePack, CBORなど、多くのフォーマットでは値にフィールド名などの情報を付属させる。
[{"id": 0, "name": "Alice"}, {"id": 1, "name": "Bob"}]
wineryが違うのは、それらメタデータをデータ本体から分離し、一箇所にまとめて保存することにある。これにより、冗長性はなくなり、しかも要素がwell-typedであることを保証する。
0402 0402 0269 6410 046e 616d 6514 [{ id :: Integer, name :: Text }] 0200 0541 6c69 6365 0103 426f 62 [(0, "Alice"), (1, "Bob")]
メタデータのおかげでデシリアライザに互換性を持たせることも可能となる。もちろん、目的に応じてメタデータを省き、binaryやcerealと同じように使うこともできる。 整数のエンコードにはVLQを採用しているため、binaryやcerealよりも短くなりやすい。
使い方
まずSerialise
のインスタンスを定義する。DerivingViaを使って簡単にインスタンスを導出できる。この導出機構は再帰的なデータ型にも対応している。
{-# LANGUAGE DerivingVia, DeriveGeneric, OverloadedStrings, ApplicativeDo #-} import Control.Applicative import Data.Winery import Data.Text (Text) import qualified Data.Text as T import GHC.Generics (Generic) data User = User { first_name :: !Text , last_name :: !Text , email :: !Text } deriving (Show, Generic) deriving Serialise via WineryRecord User
WineryRecord
はどのようなインスタンスにするか選ぶためのラッパーだ。目的に応じてWineryProduct
(フィールド名なし), WineryVariant
(コンストラクタ名あり)と使い分けよう。
あとはserialise :: Serialise a => a -> ByteString
とdeserialise :: Serialise a => ByteString -> Either WineryException a
で自由にシリアライズ・デシリアライズができる。
> serialise (User "Fumiaki" "Kinoshita" "fumiexcel@gmail.com") "\EOT\EOT\ETX\nfirst_name\NAK\tlast_name\NAK\ENQemail\NAK\aFumiaki\tKinoshita\DC3fumiexcel@gmail.com" > deserialise @User "\EOT\EOT\ETX\nfirst_name\NAK\tlast_name\NAK\ENQemail\NAK\aFumiaki\tKinoshita\DC3fumiexcel@gmail.com" Right (User {first_name = "Fumiaki", last_name = "Kinoshita", email = "fumiexcel@gmail.com"})
互換性
レコードにフィールドを追加したときや、バリアントからコンストラクタを削除したときなどに古いデータとの互換性が失われる。そんな場合のための処理をコンポーザブルに記述できる仕組みがwineryには備わっている。
UserにRoleというフィールドを追加したい場合を考えよう。
data Role = Admin | Moderator | Member deriving (Show, Generic) deriving Serialise via WineryVariant Role data User = User { first_name :: !Text , last_name :: !Text , email :: !Text , role :: !Role } deriving (Show, Generic)
データにroleが欠けている場合の振る舞いも、ApplicativeDo記法を用いてカスタマイズができる。なんとメールアドレスがexample.com
で終わっていれば自動で昇格するといった芸当も可能だ。
instance Serialise User where bundleSerialise = bundleRecord $ const $ buildExtractor $ do f <- extractField "first_name" l <- extractField "last_name" e <- extractField "email" r <- const <$> extractField "role" <|> pure (\x -> if T.isSuffixOf "example.com" x then Moderator else Member) return $ User f l e (r e)
RoleからModeratorを削除した場合も簡単に対応できる。
instance Serialise Role where bundleSerialise = bundleVia WineryVariant extractor = buildVariantExtractor $ HM.fromList [ ("Admin", pure Rice) , ("Moderator", pure Member) , ("Member", pure Member) ]
パフォーマンス
どんなに便利でも遅くては仕方がない。広く使われているbinary
, cereal
, aeson
, serialise
と比較するためのテイスティング・セッションを行った。
課題となるのは以下のデータ型だ。それぞれの方法でインスタンスを導出し、1000要素のリストのシリアライズ・デシリアライズをする。
data Gender = Male | Female deriving (Show, Generic) data TestRec = TestRec { id_ :: !Int , first_name :: !Text , last_name :: !Text , email :: !Text , gender :: !Gender , num :: !Int , latitude :: !Double , longitude :: !Double } deriving (Show, Generic) {- 1,Shane,Plett,splett0@free.fr,Male,-222,53.3928271,18.3836801 2,Mata,Snead,msnead1@biblegateway.com,Male,-816,51.5141668,-0.1331854 3,Levon,Sammes,lsammes2@woothemes.com,Male,485,51.6561,35.9314 ... -}
結果は以下の通りだ。wineryがダントツで速いだけでなく、生成されるバイト列も最も短い。
encode 1 | encode 1000 | decode | length | |
---|---|---|---|---|
winery | 0.28 μs | 0.26 ms | 0.81 ms | 58662 |
cereal | 0.82 μs | 0.78 ms | 0.90 ms | 91709 |
binary | 1.7 μs | 1.7 ms | 2.0 ms | 125709 |
serialise | 0.61 μs | 0.50 ms | 1.4 ms | 65437 |
aeson | 9.9 μs | 9.7 ms | 17 ms | 160558 |
総評
互換性と拡張性、あらゆるデータ型に対応できる柔軟な導出メカニズム、そして卓越したパフォーマンスと簡潔な表現を提供するwinery 1.0は、ここ数年で最高の出来栄えと言えるだろう。Hackageへ急げ!
報告はGitHubもしくはHaskell-jp Slackまで。
旅のチェックリスト
筆者が旅に出る際に確認する項目をまとめた。
事前の準備
- 渡航ビザ: 必要な場合もあるので事前に確かめよう。
- ESTA(アメリカの場合): どんな理由であれUSに入国する場合申請する必要がある。大抵すぐ承認されるが、遅くとも出発の72時間前に済ませるべきである。
- 宿: 好みに応じてホテルでもAirBnBなどで民泊を予約しても。後者はキッチンが用意されているところもある。
- 交通手段: 電車は大抵の場合当日で大丈夫だが、もちろん船舶や航空機の場合は予約が必須である。
- 冷蔵庫の整理: 日持ちしないものは消費してしまおう。
携帯するもの
- 財布: 財布は現金やカードを収納する。リスク回避の観点から財布は省きセキュリティポーチで代用することもできる。
- 鍵: 自宅を施錠、および帰りに解錠するのに必要である。こちらもリスク回避のため、自宅の鍵のみを持っていくという選択もある。
- 交通系ICカード: 空港や港に向かったり、コンビニで買い物したりする際に便利である。
- 保険証(国内の場合): 怪我や病気などになった場合、ないと損する。
- クレジットカード: 紛失した場合無効化できる。
- 腕時計 時間を気にするならあったほうがよいだろう。
- 装飾品 首掛けバードコールやブレスレットなど。省略可
一般
汎用性が高いため、これらはまとめて一つの袋などに入れておくと有用である。
- ビニール袋: ゴミや細々とした物をまとめるために、2枚程度は用意したい。
- マイクロファイバークロス: レンズやメガネなどの光学機器を使う場合必須である。
- 非常食 質量・体積あたりのカロリー密度が高いものを100kcal*日数くらい用意しておくと、いざという時の体力の回復に役立つ。
- インスタント食品 カップ麺やスープなどは熱湯を要求するが、食味とカロリー密度において優れている。特に海外に行く際、一つは用意しておくと心も暖まる。宿泊場所にキッチンがあるがスーパーは遠いといった場合、袋麺なども選択肢になり、荷物の圧縮につながる。
国内で入手しやすいものの質量とカロリーを比較すると以下のようになる。
名前 | 質量(g) | カロリー(kcal) | 質量比 |
---|---|---|---|
inゼリー スーパーエネルギー | 120 | 200 | 1.67 |
大粒ラムネ | 41 | 153 | 3.73 |
カップヌードル カレー | 87 | 422 | 4.85 |
カロリーメイト | 80 | 400 | 5.0 |
ゼリーは液体として扱われ、国際線には手荷物として持ち込めないので注意が必要である。
- 酔い止め: 乗り物酔いする体質の場合
- サプリメント: 旅先でバランスのいい食事ができるとは限らないのでマルチビタミンは確保したい。また、疲労対策としてアミノ酸のサプリメントもあるとよい。小分けにする場合は怪しまれないよう気を付けたい。
- ボディソープ、シャンプー、コンディショナー、洗顔料など 機内に持ち込めるよう、小さい容器に入れ密封可能な袋にまとめておく。もし宿泊先にあるという確証がある場合は省いても良い。
- 固体石鹸: もし上記のいずれかを切らした場合のバックアップになる。液体枠を圧迫しないため持っておいて損はない。
- 剃刀: 除去すべき体毛がある場合用意すべきである。
- ネックライト: 街灯がなく真っ暗闇になるところもあり、安全を確保する上で重要だ。
- 爪切り: 爪が伸びすぎると危険かつ不衛生である。1週間を超える滞在の場合は爪切りは必須である。
- 日焼け止め: 低緯度地域に向かう場合、紫外線から体を守るために用意したい。
- ポケットティッシュ
- 布: 袋でもいいしハンカチでもいい。
- 絆創膏: 大きめのものが3枚程度あると安心だ。
- 櫛・ヘアブラシ
- 新聞紙などの薄い紙: 靴が濡れた際に乾かすのに使える。
衣類
行き先の気候に合わせたものを用意する。
- 下着
- 靴下
- シャツ
- 手袋
- 洗濯可能な衣類ケース
電子・電気製品
電気製品は一つの袋にまとめておくと、手荷物検査を迅速に進められる。
- イヤホン・ヘッドホン: 移動中に音楽を楽しみたい場合。音漏れしにくく、遮音性が高いものを選びたい。国際線では機内エンターテイメントのためにイヤホンが配布される場合もあるが、音質は極めて劣悪だ。
- 撮影機材: 旅の思い出を残す一つの手段だ。
- カメラボディ: 割となんでもよい。レンズを交換する隙を晒したくない場合は複数持って行こう。
- 広角レンズ: 風景、建築、料理や集合写真など、トリミングする前提で幅広く応用できる。大は小を兼ねるとはこのことだ。
- 超望遠ズーム: 150-600mmなど。2kg程度で取り回しがよく、野鳥や動物の撮影に適する。
- 高倍率ズームレンズ: 画質・明るさにこだわらないなら、18-300mmなどの高倍率ズームレンズの一本だけでもよい。多くはAPS-C向けで、風景から、野鳥などの撮影まで使える画角を持つ。
- 三脚: 夜景、自撮り、動画のいずれかを撮る予定なら三脚は欲しい。
いざという時は武器にもなる - カメラのバッテリーの充電器: 写真のみなら大抵数日は持つが、それ以上の場合は充電器を用意しておくと安心だ。
- カメラの予備バッテリー
- カメラとPCを接続するためのケーブルやハブ
- ラップトップPC: 現地で撮った写真を取り込んで編集、投稿するという一連の流れを実現する上であると望ましい。
- USB充電器: タップに5V出力が付いたようなものは変換プラグとのシナジーがある。
- スマートフォンを充電するためのケーブル
- モバイルバッテリー: 携帯端末のバッテリー切れは避けたい。航空機を使う場合、こちらは預けることができないので要注意。
国外の場合
- ボールペン: 税関申告書や出入国カードの記入に必要となる。機内で取り出せるようにしておこう。
- SIMカードを二枚挿しできるスマートフォン: SIMカードの入れ替えは紛失のリスクが伴う。二枚挿入できるスマートフォンを持って行こう。
- 旅券(パスポート): 言わずもがな。
- 海外キャッシングのできるカード: 大量の現金を事前に用意するのはコストやリスクの観点からあまりよろしくなく、通貨によってはそもそも事前に用意できないケースもある。現地で現金を得る手段として確保しておきたい。
- 変換プラグ: 行き先によっては必要となる。
- 現金: 最低限、現地の通貨をあらかじめ用意しておきたい。それが難しい場合、現地で両替できるようなものを代わりに持って行こう(メキシコペソなら米ドルなど)。
娯楽
荷物に余裕があるなら、いずれかを持っていくのも一興だ。
- 携帯用ゲーム機: 移動中の暇潰しになる。
- トランプ、花札、サイコロなど: 筆者とは無縁だが、複数人での旅なら一つの楽しみとなりうる。
- DJコントローラ: もし知人同士で自動車に乗り、運転しないということであれば車内が盛り上がること間違いなしだ。
- MIDIキーボード: 旅先の雰囲気を音楽として残したいなら是非とも用意したい。25鍵など小さいものでもあるとないとでは大違いだ。
出発の直前に(国外の場合)
- 日本食は食べたか? 長旅の前に日本の食べ物を体に蓄えておきたい。和食やラーメンなどはもちろんだが、見落としがちなのは洋食(オムライスなどの日本料理)だ。
- 風呂に入ったか? 日本から北米までの距離を渡る際、ほぼ一日風呂に入らずに過ごすことになる。しかも浴室は日本よりも簡素な場合がほとんどなので、出発する直前に入念に体を洗おう。
余録
いかがだろうか?今回ダラスへと向かう機内で執筆していたが、ネックライトと新聞紙を忘れていたことに気づいた。次回はこのリストを見返して気をつけると同時に、読者にもチェックリストの作成をおすすめしたい。
ある期間内に更新されたデータを素早く検索できるモデル
特定の技術とは関係ない、誰でも思いつきそうな、でも便利なお話。
こんなケースを考えてみよう。
人気のトレーディングカードゲームAugur Unlimitedを扱うショップ「しらさぎ商店」では、1000種類にも及ぶカードの買い取り・販売をしている。記録のため、カードごとに日時、価格、在庫数などをまとめたレコードを毎日データベースに書き込んでいる。
新着・売り切れや、価格の変化などを、指定された期間について一覧で表示するようなWebページを作りたいとオーナーは考えた。しかし、ユーザーからの要求ごとに全データの差分を取るのは、あまり効率的な手段とはいえない。レアなカードでもない限り価格は一定であることが多いからだ。 どうすれば更新されたものだけを効率よく取り出せるだろうか?
答えはシンプルで、「日時」を「作成日時」と「終了日時」に分け、価格などが変わった時だけ新しくレコードを作成すると同時に、前のレコードの終了日時を更新すればよいのだ。新しいレコードの終了日時は未来永劫先とする。こうすると、任意の期間t0 ~ t1について、「更新される前のデータの集合」と「更新後のデータの集合」を取得することができる(データベースがこのようなクエリを許す限り)。
- 更新前: t0 < 終了日時 < t1 かつ 作成日時 < t0
- 更新後: t0 < 作成日時 < t1 かつ 終了日時 > t1
図にするとこんな感じだ。
赤で示したレコードと緑で示したレコードを比較すると、サンカノゴイが削除され、イスカとカワセミが更新、コブハクチョウが新しく追加されたことがわかる。二番目の条件は、期間内に作成・終了されたレコード(カワセミ)を弾くためのものだ。
至極単純なアプローチだが、このような問題について記述している文章が見つからなかったので、今後のために残しておく。
追記 この形式はValid timeと呼ばれているらしい。
戊戌の追憶
この記事は、筆者が過ごした2018年を簡潔に振り返り、その経験を糧とすることを狙う。
1月
第二鰓弓由来側頸嚢胞という先天異常が原因で首が化膿し、激痛に苦しんでいた。対人関係のトラブルなどもあり軽い錯乱状態にあったのか、自分が知らない間に高い買い物をすることがあった。
drinkery: Boozy streaming library というストリーム処理ライブラリを作った。当初はすべて酒関係の用語を用いていたが、批判を受けてそこはやめた。今思えばそれで正解だった気がする。 パフォーマンスはモナディックなAPIを持つライブラリの中ではトップクラスで、双方向性や多入力多出力のような発展的な機能もあるが、ListTが正しく効率的に実装されているのがなによりの魅力である。ListTのために時々使っている。
危うい精神状態だったが被写体には恵まれた。この写真はけっこう気に入っている。
寒烏今日のおやつは霜柱 pic.twitter.com/wtbOIkGhYP
— 🐦ふみ (@fumieval) 2018年1月20日
2月
警察のおかげで対人トラブルは解消された。アリピプラゾール(エビリファイ)の服薬を始めて、自分の脳の働きが変質しているような感じがした。薬のせいか忘れ物が増えた。
Discordのボイスチャンネルに入った際に通知メッセージを投稿する GitHub - fumieval/discord-vc-notification を作った。実用とHaskellのチュートリアルを兼ねている。けっこうな数のサーバーに導入されたようで喜ばしい。
生き甲斐の一つである野鳥撮影は続けた。
写真を撮っていたらヤマガラが至近距離に割り込んできた pic.twitter.com/EMlHZEsBhn
— 🐦ふみ (@fumieval) 2018年2月11日
3月
花粉症とその薬のせいで生産性がかなり削られていた気がする。
PPL 2018: 第20回プログラミングおよびプログラミング言語ワークショップ
ヘラサギを初めて見た。これだけでも鳥取に来た甲斐がある pic.twitter.com/2Jl0CjMAlO
— 🐦ふみ (@fumieval) 2018年3月7日
4月
溜め込んでいた力を解放できた月だ。
Bigmoonが私の看板ライブラリであるextensibleを使っており、偵察のためにアルバイトを始めた。2週ごとに名古屋に出勤という形で楽しく仕事を続けている。
Haskell-jpの新しいロゴ案を作り、投票によって正式に採用された。薬でブーストされたセンスのおかげだと思う。
私がデザインしたHaskell-jpのロゴが採用された pic.twitter.com/jytvvOIk3M
— 🐦ふみ (@fumieval) 2018年4月12日
5月
アリピプラゾールの副作用の悪夢や物忘れが気になってくる。
Wikiシステムを作った。アルゴリズムからWebプログラミングまで様々な技術を応用するいい課題だった。現在もextensible攻略Wikiに使われている。
https://t.co/vI3YAK7KvS Haskell(と一部PureScript)でWikiを作ってみた。変更がリアルタイムで反映され、コンフリクトが発生した場合もその場で処理する迅速さが売りだ。extensibleパッケージの攻略Wikiとして現在も稼働中 https://t.co/ZWz3pxzRTR pic.twitter.com/Ei7TkgkWkS
— 🐦ふみ (@fumieval) 2018年5月9日
この時期はシギ・チドリ類が飛来し、干潟などでよく観察できる。毎年お疲れ様と言いたい。
死屍累々 pic.twitter.com/Ux3zZG7Vq5
— 🐦ふみ (@fumieval) 2018年5月25日
6月
スキーマを導出してWell typedかつ短いエンコードができるだけでなく、後方互換でコンポーザブルなデシリアライザを構築できる直列化ライブラリ、wineryを創り出した。機能面もパフォーマンスもなかなか優秀なライブラリだと思う。
サギの雛たちが育ってきてコロニーでは賑やかな光景が見られた。
— 🐦ふみ (@fumieval) 2018年6月18日
7月
CAT S60という、サーモグラフィー搭載のごついスマートフォンを買った。他に使っている人を見たことはなく、自慢できる一本だ。
Overwatchでメインで使っていたキャラクターであるシンメトラがリワークを受け、使える技が一新された。研究がされてないこともあいまって環境にぶっ刺さり、シルバーからプラチナ帯までのし上がった。
とあるオフ会に参加して知り合いが増えた。
8月
Lisztというデータベースの実装を一新し、全データを一つのファイルにまとめる試みをしていた。非常に興味深い例題でけっこうな時間を費やした。
Minecraft (Bedrock Edition)でよく遊んだ。しばらくMinecraftから離れていたが、新しい生物やブロックなどが追加されていて通話などもしつつ楽しめた。新たな居場所を見出したような気がした。
9月
Compact regionsというGHC 8.2の新機能の効果的な使い方を発見した。更新頻度が低いデータを納め、それをIORefで保持するのがよい。私が執筆したものではないがこの記事でよく説明されている。
ガス欠気味であまり新しいものを生み出せなかった。なおOverwatchではシンメトラで暴れまくった。
10月
三宅島へと飛んだ。船で行く予定だったが台風で欠航になり、仕方なく新中央航空の19人乗りの双発機で行った。
ほぼ徒歩で島を一周するという目論見で、穏やかながら新鮮な離島の雰囲気を味わうことができた。
上京した pic.twitter.com/vozwPdHWh9
— 🐦ふみ (@fumieval) 2018年10月1日
予約していた民宿の女将は欠航が理由で私は来ないと思い込んでいたようで、無人の宿の前で待ちぼうけを食うというアクシデントがあった。幸い、数時間したら女将が戻ってきたため蚊に血を吸い尽くされるのは免れた。女将の作ってくれた料理はとても美味しかった。結局、宿泊費はただにしてもらったので結果オーライである。
個人経営の古めかしいスーパーマーケットに東方や艦これのグッズが飾ってあり、離島は離島でも同じ世界にいることを感じた。
三宅島で過ごした三日間は、間違いなく幸せなものだった。鳥の多い時期にまた行きたい。
行きつけのMinecraft BEのサーバーが過疎気味だったので、自分で新しいサーバーを立てた。Java版ゆえにサーバーMODなどが豊富で、以前とはまた違った楽しみ方ができた。
11月
Haskell Day 2018が開催された。Haskell関連の勉強会が開催されるのは久しぶりだ。来場者は150人と大盛況で、有意義なものだった。なお、私はLisztについての発表をしたが、しばらくこのような場に立つことがなかったため発表スキルの低下を痛感した。
仕事ではGHC 8.6への移行に従事していた。セグメンテーション違反で落ちる致命的なバグが見つかったのもいい思い出だ。#15892 (Segmentation fault with ByteString) – GHC
自分でも理由はよくわからないが、この月は写真をあまり撮らなかった。よくよく考えると精神的に不安定だった気がする。
カラスは荒々しいイメージを持たれることが多いが、水浴びしている時の表情は本当に無邪気だ pic.twitter.com/uAuvmcd5x1
— 🐦ふみ (@fumieval) 2018年12月1日
12月
心の調子が急激によくなった。
簡潔データ構造に興味が湧き、厳密には簡潔データ構造ではないがElias-fano encodingという手法について調べていた。
普段扱っている純粋関数型データ構造とはまた違ったトリッキーさがあり、刺激的な体験だった。
ローグライクなカードゲームのSlay the Spireに少しはまったりした。一押しはディフェクトで、「山札から任意の2枚を選ぶ」「山札の先頭数枚から好きなだけ選んで捨てる」「捨て札を手札に戻す」といった制御系のカードを揃えていき、運ゲーを運ゲーでなくする過程が楽しい。
そして鳥の季節がやってきた。昨年は見られなかったアトリが来ていて喜ばしい。
アトリの飛来を確認した pic.twitter.com/cAnrqVi8qE
— 🐦ふみ (@fumieval) 2018年12月10日
総評
一年を通して、私の心と体、そして人間関係などの環境は徐々に改善された。ストレスの減少によって体づくりの効率も上がったのも間違いない。
そして、私は新しい能力、新しい道具、新しい仲間を得ることができた。来年からとは言わず今日から、それらを無駄にすることのないよう心がけていきたい。