wvogel日記

自分用の技術備忘録が多めです.

入院記録 -脾摘その他-

脾臓摘出目的

  1. 幼少期に摘出したはずの脾臓が少なくとも2個復活し,血液ポンプとしての心臓にかかる負荷が本来より高くなっている.脾摘することで心肺への負担を軽減できれば,肺高血圧症の状態を改善できる可能性がある.
  2. この数年,胃の静脈瘤・動脈瘤破裂頻度が上昇している.脾臓を摘出し消化管系を流れる全血液量を軽減することで側副血行路圧を低くし瘤の発生を抑えることを期待する.

1 脾臓摘出術

入院前日

PCR検査を受ける.駐車場の一角に設けられたスペースで, 時間帯によって一般・入院予定者・入院患者が分けられていた.

入院当日

  • CT検査 ... 脾臓の位置・血管の位置を再度確認する目的
  • その他 ... 血液検査・X線など

術前説明

  1. 今回摘出する脾臓(脾腫)は2個.(術中,3個あることが発覚する)
  2. 幼少期の開腹痕を再利用する形で20cmほど開腹する.
  3. 幼少期に一度脾摘しているために本来の脾動脈がなく, 細かい欠陥が多数発達していることが予想される.
  4. 過去の脾摘から相当の年月が経過しているため,激しい癒着が想定される
  5. 通常,消化器官のこの規模の手術には硬膜外麻酔を施すが, 血小板の値が低く閾値すれすれのために不使用とする.
  6. 予定手術時間は7-8時間

一般向け参考資料 : 硬膜外麻酔

手術当日

朝8:30頃に病室を出て9:00頃開始.全身麻酔. 手術室で麻酔から覚醒した時に時刻を尋ねると15:50頃だったので,概ね予定手術時間通りに進んだようである.

ICUへ移動. Aライン,Vライン,CV,フローラン用CV(以前から留置されている), NGチューブ,その他心電図等が接続されている状態.

一般向け参考資料 :

ICUへは,機内モード電子書籍の持ち込みすら禁止されたので, そんな時のために用意していた紙本を持ち込んだ.
物を作って生きるには――23人のMaker Proが語る仕事と生活

手術翌日~

ICUから病棟の回復室へ移動.(昔は重症室樋=読んでいたが,気がついたら名称が変更になっていた) 食事再開.

さらに翌日に個室へ移動.しかし立ち上がると陰嚢に激痛が走る. お通じもない.

手術の説明を受ける.

  1. 成人男性の拳大の脾腫が2つ,黒豆大の脾腫が一つあり,合計3個の脾摘を行った. 幼少期から通算すると4個脾摘したことになる.
  2. 案の定,大量に細いし血管があったため,時間の大部分はその血管の処理に費やした.
  3. 小腸の癒着もあったのですべて剥がした(結果的にこれが原因で苦しむこととなる)

2 鼠径ヘルニア手術

症状

前述したように,術後から立位あるいは歩行をすると睾丸に激痛が走る. 術後2,3日語からはベッド上で仰臥位でも終始激痛が走るようになり, 脾摘よりもこちらの方がずっと苦しかった. 歩行に支障が出るほどだったので医師に報告し超音波をしたところ, 鼠径部から小腸が脱腸し陰嚢に入り込んでいた. 完全な嵌頓状態で水も溜まっており, 幸い鼠径が大きくすぐに腸が壊死することはないにしても 腸閉塞を起こしているためすぐに手術をすることとなった. 月曜に脾摘手術を行い,その週の金曜日であるから怒涛な一週間である.

処置内容・手術当日

術後は再びICUへ.この時に,前述した書籍の残り半分を読了する. 翌日,開腹室を経て一般病室へと移動した.

なお,ヘルニア傾向自体は幼少期からあったが, これまでは腸が癒着していたことでこれまで嵌頓を引き起こす状態にまでなることはなかった.

術後

術後は痛みもなくなりお通じも回復した. ただ,癒着が剥がされ自由に動けるようになったものの, 鼠径を封じられ行き場を失った腸が蠕動痛を数日に一度引き起こし, 何度かソセゴンのお世話になった.

脾臓摘出後の経過も問題なく食事も再開できたため,9月中旬に退院した. 開腹した手術部のスキン・ステープラは退院前日に半抜鉤し,退院当日の朝に全抜鉤した.

参考資料 : スキン・ステープラ

退院後

退院当日夜お風呂に入った痕,開腹部を保護するテープを張り替えると, 全長およそ20cmの開腹痕のうち10cm弱の傷口から黄色い膿のようなものが出てきており, 傷口がパックリ開いているように見えた. 病棟に電話したところ,翌日も治らなければその次の日に外来に来るように言われる.

翌日も治らなかったので退院してから二日後,外来に行くことに. ところが家を出る直前,血便が出る. 術後の痛みを抑えるために飲んでいたロキソニンによる潰瘍だと思われるが 外来であわせて相談することにする.

外来で開腹部を診てもらい,テープを張り替え様子をみることになった. 消化管出血に関しては,外来到着時点で軽い吐き気を覚えるくらいに悪化していたので Vラインを取りNGチュープ経由で胃にたまった出血を吸引してもらう. そのまま開腹室へ入院.

3 胃静脈瘤治療

開腹室で,牛乳に溶かしたトロンビン止血剤をNGチューブ経由で投与される. 入院した翌日,再びPCR検査を受けさせられる. 退院期間わずか1日とはいえPCR検査を受けなければいけないようである.

入院したのが4連休直前だったために内視鏡検査は翌週へ持ち越される. トロンビンが効いたのか日曜にはほぼNGチューブから回収される血も0になっていたため, 連休がために暇な絶食期間を強いられることとなる.

第一回内視鏡(観察)

胃壁から間欠的に血が出血していた. 内視鏡写真を見せてもらったが,間欠泉のイメージがまさにぴったりである. 最初はなんともないように見えたが,観察を続けるうちにピューっと勢いよく出血することを繰り返していた.

CT検査

通常はカテーテルで出血箇所の近くの血管までアクセスし出血している血管を潰す処置を施す. その処置に使える欠陥があるかどうかをCTで調べた.

参考資料:ヒストアクリル硬化療法

結論として,そのような欠陥はなかった. 門脈もない,脾臓もない,側副血行路多数の特殊な環境でそのような太い血管はなかなかないようである. そのため,内視鏡で胃にアクセスし瞬間接着剤のようなもので出血を止める硬化療法が取られることとなった. 消化器内科に硬化療法器材がなかったため道具を揃え後日改めて取り組むことに.

第二回内視鏡(処置)

第一回内視鏡から二日後に実施.1,2時間程度で完了したようである. 処置は問題なく完了したとのことだが,別の血管に圧がかかって出血する可能性もあるため, 後日再観察するまでは絶食になった.また無為に土日を過ごすこととなった.

第三回内視鏡(観察)

観察だけなので3,40分で完了した. 新たな出血は認められず,観察上問題ないことが確認できた. 2日ほど病院食で様子を診た後,週末に退院となった.

なお,第三回内視鏡の前日には,脾摘手術の肺高血圧症における効果を確かめるために心カテ検査も実施した.

参考資料:肺高血圧症評価項目例

退院

8月末入院-10月初旬退院と,およそ6週間も入院することとなった. 本来,脾臓摘出手術だけであれば10-14daysと言われていたが, 次から次へと問題が発生し,数ある入院生活の中でも非常にストレスフルなものの一つとなった.

四択クイズwebアプリ with Servant

動機

マーク式の資格試験などに使える,クイズアプリケーションを作りたい. しかしyesodなどで一からweb作るのは面倒だし,そもそもyesodは大量のメモリを 消費するので手持ちのVPSでは走らない...
web APIとして軽量に,しかもさくっと作りたい!(そうしないと資格勉強時間が...)

ということで,はじめてのwebアプリ開発のツールに選んだのはServantです.
素晴らしいことに,webアプリ開発経験0の私ですら,Servant側で詰まったことは 一度もなかったので,(サーバーの仕組みよくわからん)(webアプリ難しそう) といった超初心者にこそお勧めしたいフレームワークです.
javascriptAPIを呼ぶところだけつまずきましたが, こちらも多くの場合ブラウザの"賢い"キャッシュ機能によるものでした.

そもそもServantって?

詳しい記事はたくさんあるのであちこち参照してみてください. webアプリ知識0なので他の言語・フレームワークでは どういう風に開発するのか全く知りませんが,開発を終えてみて理解したことは

  • Haskellによるwebフレームワーク.割と軽量.
  • いつも通り型を合わせていけば動く.間違いなく動く
  • 通信部で書くことが本当に少ないので,サーバーの仕組みを理解しやすい
  • 通信部と処理部を完全に分離して実装することができる
  • メンテも簡単!

webアプリ開発って難しそう〜〜と思っていましたが,本当に簡単に書けてしまいます.

はじめに

本記事のソースコードは全て https://github.com/wvogel00/quiz4/ に置いてあります. GHC拡張やimportモジュールはそちらを参照してください.

そもそもwebAPIって?

(私が勝手につまずいていただけなので読み飛ばしてもらって大丈夫です)

私はこれまで測定器やハードウェアまわりのAPIしか触ったことがありませんでした. 例えばYOKOGAWAのTIA装置TA720を使って, 通信機の基準信号を1日かけて測定する測定系(アプリケーション)を作るとしましょう.

image TA720(タイムインターバルアナライザ)

この場合は,GPIBを介してPCと測定器を接続して, 文字列:STOPを送信すれば測定の停止, :CALCULATION:TPTOPEAK? でp-to-p測定値を取得できますし,他にも沢山のAPIがあります.
この:STOPのように, 「アプリケーションをプログラミングするために提供されている インターフェース」がAPIです. webも測定器と同様で,urlを住所ではなくAPI関数, そこにPOSTするJSONやらの値をAPIの引数として考えれば良いだけでした.

では早速,web APIを作るために,仕様を作っていきましょう.

仕様決め

クイズのデータ型

今回はこれがサーバーとクライアントの間でやりとりするJSONに含む情報となります.

data Quiz = {
  statement :: Text -- 問題文
  , a :: Text -- 選択肢A
  , b :: Text -- 選択肢B
  , c :: Text -- 選択肢C
  , d :: Text -- 選択肢D
  , answer :: Text -- 解答
  , explanation :: Text -- 解説
}

また,4択クイズですので,statement,a,b,c,d,answerは必須, explanationは文字列が空でも良いことにします.

必要なAPI

今回作成するweb APIでは,クイズの作成とクイズの出題に関する2つのAPIが必要になります.

  • /quiz4/make ... POSTメソッド. 上述のQUizデータをもつJSONを渡してクイズを登録するAPIです.登録結果も返します.
  • /quiz4/get ... GETメソッド 上述のQuizデータをもつJSON,登録済みのクイズの中から適当に返してくれます.

ついでに,ブラウザ上でも使えるように,webページも作成しましょう.

  • /quiz4 ... クイズ用のwebページ(html)を返します.

仕様が決まったので早速実装しましょう.

APIの定義

サーバで使えるapiを定義します!基本的には上であげた3つが使えれば良いのですが, htmlをつかう以上,html/css/js,それから画像ファイルも使えるようにしておきたいですね. また,ServantではHTMLは型として定義されていないので,自前で用意します. (他のライブラリを使えばできなくはないのですが,依存関係はできるだけ少なくしましょう)

ではさっそく,このファイル達を使うために型を定義します.

data HTML
instance Accept HTML where
    contentType _ = "text" // "html" /: ("charset", "utf-8")

instance MimeRender HTML BS.ByteString where
    mimeRender _ bs = bs
    
data FileType = HTMLFile | CSSFile | JSFile | IMGFile deriving Eq

これを使って,APIを定義します.

type API = "quiz4" :> Get '[HTML] BS.ByteString
    :<|> "quiz4" :> "html" :> Raw
    :<|> "quiz4" :> "css" :> Raw
    :<|> "quiz4" :> "js" :> Raw
    :<|> "quiz4" :> "img" :> Raw
    :<|> "quiz4" :> "get" :> Get '[JSON] Quiz
    :<|> "quiz4" :> "make" :> ReqBody '[JSON] Quiz :> Post '[PlainText] T.Text

(日本語も使いたのでHTMLはByteStringにしましたが,T.Textに統一すれば良かった...)

とてもシンプルです.一行目と最後の二行は,仕様で決めたAPIそのものですね.

  • topページ ... /quiz4を受け取ったなら,Htmlページを返す
  • get API ... /quiz4を受け取り./getを受け取ったなら, JSON形式でQuiz型の値を返す
  • make API ... /quiz4を受け取り./makeを受け取ったなら, JSON形式でQuiz型の値を受け取り,結果を平文で返す.
  • その他(cssの場合) ... /cssを受け取ったなら,それに続くファイル名と一致するファイルをそのまま返す

APIを定義できたら,実装していきます

APIの実装

server関数に,上述のAPIを定義しましょう.API:<|>で,定義したAPIの順番通りにつなげます.

quizServer :: BS.ByteString -> Server API
quizServer toppage = getHtml toppage
                :<|> getStatics HTMLFile
                :<|> getStatics CSSFile
                :<|> getStatics JSFile
                :<|> getStatics IMGFile
                :<|> getQuiz    -- getAPI
                :<|> postMake   -- makeAPI

すばらしい.ほぼ定義通りに書くだけですね.それぞれの関数の型は次の通りで,上で定義したAPIとの対応がわかると思います.

getHtml :: BS.ByteString -> Handler BS.ByteString
getStatics :: FileType -> Tagged Handler Application
getQuiz :: Handler QUiz
postMake :: Quiz -> Handler T.text

まずはtoppageを返すgetHtmlを実装します.引数で渡されたByteStrng表現htmlファイルを Handlerモナドに包んで渡すだけですから

getHtml html = liftIO $ return html

で終わりです.
他のhtml/css/js/imgファイルも,リクエストが来たらそのままファイルの中身を返すだけですから

getStatics HTMLFile = serveDirectoryWebApp "html"
getStatics CSSFile = serveDirectoryWebApp "css"
getStatics JSFile = serveDirectoryWebApp "js"
getStatics IMGFile = serveDirectoryWebApp "img"

で完了です.serveDirectoryWebApp関数は,指定したフォルダの中にある該当ファイルを返します. これにより,次のようなhtmlファイルをtoppageとしてquizServerに渡すと, css,js,favicon画像を読み込んで作成したコンテンツを正しく表示できます.

<html>
    <head>
        <link rel="stylesheet" href="/quiz4/css/quiz4.css">
        <link rel="shortcut icon" href="/quiz4/img/favicon.ico">
        <script type="text/javascript" src="/quiz4/js/main.js"></script>
        <meta charset="UTF-8">
        <title>クイズ4</title>
    </head>
    <body>
        <div class="buttonContainer">
            <div class="indexButton" id="solveProblem">
                問題を解く
            </div>

            <div class="indexButton" id="makeProblem">
                作問する
            </div>
        </div>
    </body>
</html>

<head href="~~">が,API定義の通りに/quiz4/cssのようになっていますね. 実行ディレクトリにcss,js,htmlなどのフォルダを作成して適当にcssなどを作れば,次のようなページが表示されます.

1

では,肝心の二つのAPIの実装に入りましょう.

getQuiz : GETメソッド

GETメソッドなので関数は値を取らず,登録されているクイズの中から適当にどれか一つを選んで返します. 本来であればsqlデータベースを使うのですが,今回ははじめて作るwebアプリなので サーバーの構造をみやすくするために,テキストファイルに読み書きすることで実装しました.

getQuiz :: Handler Quiz
getQuiz = do
    qs <- liftIO $ T.lines <$> TIO.readFile database
    now <- liftIO $ floor . utctDayTime <$> getCurrentTime :: Handler Int
    let i = mod now $ length qs
    return $ encodeQ $ qs !! i

時刻をもとにファイルからランダムに読み出した[T.Text]型の値を encodeQでQuiz型に変換して返します. シンプルですね. 今回の仕様には定義していませんでしたが, 過去に出題した問題とできるだけ重複しないロジックにする場合は, モナドを積んでいけば良さそうです.

postMake : POSTメソッド

POSTメソッドなので関数は定義にのっとりQuiz型の値をとり, その結果を平文(T.Text)で返します.

postMake :: Quiz -> Handler T.Text
postMake q = do
    result <- liftIO $ registerDB q
    case result of
        Done -> return "success"
        AlreadyExist -> return "すでに同じ問題が存在します"
        ValueLack v -> return $ T.append v "は必須項目です"
        Fail -> return "fail. please report to developer."

registerDBでファイルにクイズを書き込むアクションを実行し, その結果でresultを束縛します. この返り値RegisterResultは次の値を取ります.

data RegisterResult = Done | AlreadyExist | ValueLack T.Text | Fail deriving Eq
  • Done ... クイズの登録に成功しています
  • AlreadyExist ... 既に同じ問題が登録されている場合は,登録しません. apiを複数回叩いてしまうことを防止しています.
  • ValueLack T.Text ... Quiz型のフィールドの中に抜けているものがある状態です. 解説文explanation以外は必須項目なので,漏れがある場合は登録せず,漏れている 値のフィールド名を文字列で返します.
  • Fail ... 登録に失敗しています.

JSONとQuiz型の変換

以上が書ければほぼ実装は終わりですが,最後に一つやり残しがあります. JSON型とQuiz型の相互変換です.api関数に書かれている

  "quiz4" :> "get" :> Get '[JSON] Quiz

は,Quiz型の値を[JSON]形式で渡すことを意味しますが, この処理はどこで行われるのでしょう. これは,Quiz型をToJSON,FromJSONという型クラスのインスタンスにすることで実現されます. Quiz型のもつフィールド名とJSON内のフィールド名が異なる場合や, 少し処理を加えてから JSONと相互変換したい場合はごりごりinstanceにする処理を書かなければなりませんが, フィールド名がQuiz型,JSON間で共通で何の処理もしない場合には,次の一行で解決です.

$(deriveJSON defaultOptions ''Quiz)

これだけで,JSONとQuiz型の FromJSON, ToJSONインスタンスを実装したことになります.

APIを使ってみる

ではAPIを叩いてみましょう.

クイズを取得

curlでは次のコマンドを叩くだけです.yourservernameには,localhostなどを入れてください. 何かしらのQuizを登録済みなら,その中の一個がJSONで返ってきます.

curl yourservername:8080/quiz4/get

jsでは,例えば次のようにかけます.

function getJsonData(url)
{
    xhr = new XMLHttpRequest;
    xhr.open('GET', url, true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onload = () => {
        console.log(xhr.responseText);
        var rec = JSON.parse(xhr.response);
        // rec.statementやrec.answerで各フィールドの値にアクセスできる
    }
    xhr.onerror = () => {
        console.log(xhr.status);
    }
    xhr.send(null);
}

クイズを登録する

次のcurlでレスポンスが得られます.ここでは試しに,answerフィールドを空欄のままpostしました.

curl -X POST -H "Content-Type: application/json" -d '{"statement":"this is teset", "a":"a", "b":"b","c":"c","d":"d","answer":"","explanation":"this is test post"}' yourservername.com:8080/quiz4/make
# ↓結果
解答は必須項目です

answerフィールドが抜けているので,解答は必須項目ですと返ってきました. また,jsでは,例えば次のコードでpostできます.

function postJsonData(url, jsondata)
{
    xhr = new XMLHttpRequest;
    xhr.open('post', url);  ここでは//url = /quiz4/make
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onload = () => {
        console.log(xhr.status);
        console.log(jsondata);
    }
    xhr.onerror = () => {
        console.log(xhr.status);
    }
    xhr.send(jsondata);
}

無事に四択クイズのwebアプリケーションを作ることができました. ユーザー認証やクイズジャンルを選べるようにするなど拡張性はまだまだありますが, とりあえず趣味で使う分には実用的なものができました.

おわりに

webアプリケーションなんもわからん!webAPIて何!?状態だったところから, たった二日(うち1日はjs側コードに苦しんだ)で形になるくらい, Servantは簡単でした.久々に感動したフレームワークです.webエンジニアデビュー成功,ということで.

HaskellでHTTPリクエストを作る

先日,ATCoderの問題を解けずにAC (Accepted)が獲れなかったら,代わりにGVS法で頭部にAC電流を流すことで,
確実にACが獲できるシステム"GVSCoder"を開発し,動画にして公開しました.
GVSCoderの中身はHaskellで書かれており,その時にはまったhttpリクエストの作り方について備忘録を記録しておきます.

一応,動画のリンクを貼っておきます.

AtCoderでAC取れなかったら頭にAC電流流す

また,ソースコードおよび電気刺激手法:GVSについての詳細はこちらにあります
GitHub - wvogel00/gvscoder: if you get other than AC, you get GV Stimulus!

HTTPリクエス

Network.HTTP.Clientでリクエストを作成する場合,Request型には次のフィールドがあります.

method :: ByteString
secure :: Bool
host :: ByteString
port :: Int
path ::ByteString
queryString :: ByteString
requestHeaders :: [(HeaderName, ByteString)]
requestBody :: RequestBody
proxy :: Maybe Proxy

ByteStringがたくさん出てくるので,OverloadedStringsプラグマを使いましょう.

そしてPOSTする場合,通常(?)であれば,requestBodyにユーザー情報などを設定するはずなのですが,うまくいきません.
試しに下のコードを走らせると,2回目のリクエストに対しては400エラーが返ってきます.

-- これはatcoderサイトでは動かない
post = do
    manager <- newManager tlsManagerSettings
    req <- parseRequest "https://atcoder.jp/login"
    res <- httpLbs req manager
    -- クッキーの取得
    let cookie = responseCookieJar res
    -- クッキー挿入
    -- setLoginInfoは,リクエストボディにユーザー情報を付与する関数
    req' <- attatchCookie (setLoginInfo req user) cookie
    -- ログインリクエストを投げる
    loginRes <- httpLbs req' manager
    BS.putStrLn.toStrict $ responseBody loginRes

400エラーはCSRFトークンエラーです.
2回目のリクエストを作成した時にCSRFトークン取得に失敗しているのかと思い,コンソール状にリクエストの中身を表示させてみると,CSRFの値は正しくrequestHeaderに装入されています.

結論から言うと,atcoderでは,CSRFトークンはrequestHeaderではなく,クエリに設定してPOSTしてやる必要があります.(最初,CSRF取得は自前でやる必要があるのだと勘違いしていたのでParserを組んでしまいましたが,これが役に立ちました)
すなわち,setLoginInfo関数の中身は,次のようにします.

data User = User{
    name :: BS.ByteString,
    pass :: BS.ByteString,
    csrf :: BS.ByteString
} deriving (Eq,Show)

setLoginInfo :: Request -> User -> Request
setLoginInfo req user = req{
    method = "POST",
    queryString = BS.concat [
            "?csrf_token=", csrf user,
            "&username=", name user,
            "&password=", pass user
            ]
    }

無事にログインに成功します.
少し面倒ですが,レスポンスを受け取るたびにcookieの値を更新してやることでセッションを維持することができます.
(不勉強で,cookieは使い回すものだと思っていました)

おそらくatcoderのリクエストの作り方は少し変わっていると思うので,
色々なサイトで試してみたいです.
web周りはほとんど触ったことがなかったので,今回初心者の詰まり方をしたおかげで色々勉強になりました.
コードがすぐに動いて結果がみられるというのはなかなか楽しいですね.

鉱物鑑定士試験8級7級受検記録

2020/7/25-27にかけて石ふしぎ大発見展が開催されています.
日本・世界の各地の鉱物が大阪はOMMビルに集うイベントで規模としてもそれなりに大きいイベントです.
毎年,このイベントの傍ら,同会場で鉱物鑑定士試験が実施されています.
級は1級-8級まであり,飛び級は不可で誰でも8級から受検しなければなりません.しかも,併願受検が可能なのは8.7,6級まで,それも一度に2つまでという縛りがあります.
1,2級の受験資格を満たすには鉱物関連の論文などが必要ですので,アマチュアで目指せるのは最大3級と思って良いでしょう.

毎年日程が合わなかったのですが,今回はスケジュールが合ったので8,7級を受検することにしました.
この2つはいわば鉱物を啓蒙するための入門用試験なので,参考資料持ち込みokです.必ず合格できます.(私見

石ふしぎ展

全3日,全5回の入れ替え制(最終日のみ入れ替えなし)
コロナのため,入場には事前予約が必要です.
試験にかぶらないよう,そして少しでも感染リスクを下げるため,初日の第一タームを予約しました.

一番最初で元気も有り余っていたからか,初めに行ったガーネット専門店が印象的でした.店員さんも気さくで,お客第一号だからか色々丁寧に教えてくれました.

f:id:wvogel00:20200726182316j:plain
ガーネット + グラファイト

そのあともあちこちまわりましたが,皆すごい気さくに話しかけてくるのでびっくりしました.大阪ですね.
今回は辰砂とスピネルを手に入れることを一つ目標にしていましたが,個人的にはルチル・赤鉄鉱・水晶の共生標本が格好良くて今回入手した中では1,2を争います.

f:id:wvogel00:20200726183033p:plain
ルチル・赤鉄鉱・水晶の共生

撮影環境が良くなくて魅力が十分伝わりませんが.

念願のジオード割り体験もしました.
友人と2人,密度の最も低そうなジオードを選び,機械で割ってみました.石の中はなかなか綺麗に水晶が群生しており,いい買い物です.

f:id:wvogel00:20200726183435p:plain
ジオード割り体験.三つに割れた

2時間弱展示を回ったあと,昼食をとり,試験会場に向かいます.

8級(2020/7/25) 15:00-

受検人数は25~30人の間.友人と2人で受検.
受検記念のお土産に石(花崗岩)をもらいました.

8級講習

通常だと4人くらいのグループに一つ鉱物標本群が与えられるのですが,コロナ対策か,2人に一つ鉱物標本が与えられました.
講師1人,巡回してくれる鉱物鑑定士2人の体制で,わからなかったらすぐ教えてくれます.石墨は手が汚れるのでエタノールでキレイキレイしましょう.
劈開とか条線の基本的なこととかを教えてくれます.
講習で試験に出そうな箇所は念押しされるので,寝ていても合格できるでしょう.

8級鑑定試験

A4一枚の回答用紙と,鉱物5種が貼り付けられた厚紙が机上に置かれます.回答用紙の上半分に5種の鉱物の鑑定結果を.下半分に二択問題が10問ほどあるので,回答欄に{o,x}を記入します.
特徴的で,誰でもみたことのある鉱物しか出ません.

7級(2020/7/26) 11:00-

受検人数は少し減って,20人弱だったと思います.今日は1人で来ましたが,中学時代の理科の先生が講師でした.

7級講習

こちらも,2人に一つ,鉱物標本が与えられ,講師1人,巡回してくれる鉱物鑑定士2人の体制です.
輝水鉛鉱で遊ぶと少し手が汚れるので,やはりエタノールでキレイキレイしましょう.8級同様,鉱物の特徴を解説してくれます.少し産状にも触れますが,気にするのは6級からでいいそうです.共生鉱物の情報は有用なので覚えておきましょう.

7級では灰重石や重晶石といった石も加わってきます.灰重石は見事に蛍光するので楽しいですね.
あと,輝石・角閃石はおそろしく紛らわしいです.
講習が終わると,なんとなく石の中にある鉱物を見つけられる気になります.

7級鑑定試験

ホッチキスで閉じられたA4の回答用紙二枚と,鉱物10種が入った箱が渡されます.箱を開けると,5行2列に仕切られた10個の空間に鉱物が収められています.

回答用紙1枚目は筆記問題です.10問あります.記述式ですが,鉱物名や元素記号を埋めていくだけで,論述ではありません.8級の内容も少し入っていましたが,焦ることはありません.8級の内容はマスターしているはずですし,参考資料も手元にあるのですから.

回答用紙2枚目に,鉱物10種の鑑別結果を記入します.しかし丁寧なことに,回答欄の横には簡単な鉱物の特徴を書いてくれています.(e.g. 六角柱状など)
私には一つ,普通輝石か透輝石か鑑別できないものがありましたが,合格点は70点くらいですから気にしないことにしましょう.

試験結果は1,2週間後に郵送されてくるそうです.
おっと,するともう8月ですね.

感想

元々鉱物の知識は人並み以上にはあるつもりですが,実物をみて回ったのは久々なのでいい刺激になりました.
友人も言っていましたが,資格試験の講習を受けてから石ふしぎ展を回るとより楽しめたかも知れません.

暇潰しゲームをHaskellで書いた

連休中にさくっと作れるアプリケーションを探していて,高校時代に友人がHSPで書いた暇潰しゲームを思いだしました.

Glossなら一度ゲームを作成したこともあるので割とすぐできるだろうと書き始め半日位でできました.
ソースコードは下記にあります.
https://github.com/wvogel00/hima

解説記事を書こうと思ったのですが,glossの使い方は過去に一度記事にもしていますし,他にまとまったサイトもあるのでこの記事の最後にある参考資料をあたってください.

一番つまずいたのはgloss-juicyのビルドです.cabalもだいぶ進化したらしいので,使えるようになっていきたい.

キーイベント発生時に効果音を再生する方法を思いつかなかったので,音響は実装していません.多分gloss以外のライブラリを使うのが良いのだろうな


連休の暇潰しにどうぞ遊んでください.


暇潰しゲームデモ動画

余談:
今回はじめてイラスト作成にKritaを使いました.液タブからペンタブに戻ると線がぶれっぶれ

HaskellチャットサーバでCSSやJSリクエストを返す

Haskellでチャットサーバを立てる記事( Haskellでチャットサーバーを建ててみた - Qiita )を読んで,私も試してみたくなりました

この記事のプログラムは昔MakerFaireに出した時にも参考にしていて,一度websocketを使った通信は書いたことがあるのですが,
当時はよく理解しないまま書いて,最後時間に追われバグ取りも友人にやってもらっていました.

とはいえ今回はチャットサーバ書くだけですから写経で良いわけです.
ただ入力フォームしかないのは寂しいので,jsとcssを書いて,それなりにアプリらしい外観にします.

しかしその場合,ただプログラムを写経しただけでは動作しません.
ブラウザの開発者用ツールを見ればわかると思いますが,jsやcssファイルの読み込みに失敗します.
これは,クライアントがサーバーにアクセスしてhtmlを呼び出した後に順番にやってくる,htmlから呼び出される他のファイルに関するリクエストを全無視しているからです.
これらのファイル呼び出しリクエストも処理してやらねばなりません.

こういう時には,pathInfo関数を使ってリクエストの中身を調べることが出来ます.

pathInfo :: Request -> [Text]

リクエストの情報を知ることが出来ます.これでリクエストの中身を見ると,次のような結果を得ることが出来ます

-- 一番最初,サーバーにアクセスしてきた時
pathInfo req
[]

-- html中の<script type="text/javascript" src="js/test.js"></script>に関してリクエストを受けた時
pathInfo req
["js", "test.js"]

このリクエスト内容に合ったレスポンスを返してやれば,cssやjsで着飾ったサイトを表示することが出来ます.
変更するのは,参考記事のうち,app関数だけです.例えば次のように変更します.

app :: Appplication
app req respond = do
    case pathInfo req of
        [] -> respond $ serveFile "text/html" "index.html"
        ["js", js] -> respond.serveFile "text/javascript" $ "js/" ++ unpack js
        ["css", css] -> respond.serveFile "text/css" $ "css/" ++ unpack css
        ["img", img] -> respond.serveFile "image/png" $ "img/" ++ unpack img

serveFile mime filePath = responseFile status200 [("Content-Type",mime)] filePath Nothing

pathInfoの返す値はTextのリストなので,適宜Data.Text.unpackでStringに戻します.
Mimeの部分も,下記URLなどを参考に,返すファイル内容によって変更してください.
拡張子とMIMEタイプ - とほほのWWW入門

Haskellで書く3D結晶単位胞ビューア

暑すぎて熱中症になりそうです.
私は自宅ですが,三密が満たされている場所ではもっと暑そうです.
密といえば,立方最密構造ですよね!!
三密が禁忌とされる今,Haskellの3DCGライブラリnot-glossを使って,金属単結晶とイオン結晶体のビューアを作成します.

ソースコードは下記にあります.
GitHub - wvogel00/minerals

結晶構造

自然界での事象はその多くが安定な状態に収束する傾向にあり,
身近なところでは金属結晶などの集合体もその良い例です.
自然界の金を例にとると,自由電子を介する金属結合によって最も安定な状態で結晶として産出され,結晶構造は最も充填率が高い立方最密構造(または面心立方構造)をしています.
金属は単結晶では主に,立方最密構造,六方最密構造,体心立方構造で安定します.
10剤の結晶は無数の原子が連なっていますが,その繰り返しの最小単位を単位胞といい,単位胞が決まればその物質の結晶構造は説明されたことになります.
そして,ブラべ格子によって,三次元での結晶がとりうる結晶構造の種類は14種類であることが証明されています.
つまり,最大14種類に対応すれば良いわけですが,step by stepということで,
今回は金属結晶とNaCl型イオン結晶のみ実装しました.

XMLパーサ

金属によって安定な結晶状態は変わってきますので,XML形式で保存した元素データベースを作成し,ParsecをもとにしたパーサライブラリTrifectaでパースしていきます.
try関数に頼りまくっています.空白処理もありあまり美しくありませんが,Trifectaは割と使いやすいのでもし使うときには参考にしてください.

xmlの書式は下記の通りですが,まだ全てのデータを取り込んだ訳ではないので,
タグの読み込みに失敗することも想定してパース関数を作りました.
Teは六方最密構造をとるので格子定数にcがありますが,立方最密の場合にはcフィールドはなく,aのみが記載されています.

<elements>
    <element>
        <name>Te</name>
        <structure>hcp</structure>
        <a>0.446</a>
        <c>0.593</c>
        <density>6.236</density>
        <ion>
            <e>-2</e>
            <radius>0.221</radius>
        </ion>
    </element>
</elements>

このようにフィールドがない時にはMaybeが便利なのでとりあえず包みます.
また,元素によっては複数の価数イオンをもつので,XMLを格納する代数的データ型は以下のように定義しています.

data XML = XML{
    name :: Element, 
    structure :: Maybe Structure,
    _a :: Maybe Float,
    _c :: Maybe Float,
    _density :: Maybe Float,
    _ions :: [Ion]
    }
    deriving (Eq,Show)

化学式パーサ

XMLとは別に,実行ファイルに結晶化学式を引数として渡して,その結晶構造を表示させる必要がありますので,ここも簡単にパーサを書きました.あまりにも簡単すぎて書く必要もなかったかもしれません.まあ練習と思って.とても短いのでここにも掲載します.

parseCF :: Parser [(Maybe Element, Int)]
parseCF = (:) <$> element <*> many element

element :: Parser (Maybe Element, Int)
element = (tuple <$> upper <*> try (many lower) <*> try (many digit)) where
    tuple u l n = (findElem $ u:l, safeRead n)
    safeRead "" = 1
    safeRead x = read x

findElem :: String -> Maybe Element
findElem elem = find((==elem).show) [H ..]

結晶中の元素とその個数を返し,

"NaCl" -> [(Na,1), (Cl,1)]
"FeS2" -> [(Fe,1),(S,2)]

のように動作します.

ポーリングの法則とマーデルングエネルギー

まだ実装中ですが,イオン結晶を形成するには,各原子同士が単位胞内に収まる必要があり,幾何学的観点からはポーリングの法則に,静電エネルギー観点からはマーデルングエネルギーに従います.ポーリングは陽イオン・陰イオンの原子半径や結合の優先順位について説明しており,マーデルングエネルギーはイオン間距離と,結晶構造から決まるマーデルング定数によって決定されいます.
一応ポーリングの法則の判定関数は実装したのですが,マーデルングは結晶構造と関わってくるので先延ばしにしています.

例えば,ポーリングの法則に従うと,半径r1の陽イオンから半径r2の陰イオンへの配位数は下記関数に従います

coordinateN' r1 r2
    | r1/r2 < 0.155 = 2
    | r1/r2 < 0.225 = 3
    | r1/r2 < 0.414 = 4
    | r1/r2 < 0.732 = 6 -- NaCl型
    | otherwise = 8     -- CsCl型

陽イオンから陰イオンへの配位数がわかれば,陰イオンから陽イオンへの配位数は,ポーリングの第二法則によって決めることができます.

3Dライブラリ not-gloss

以上の知識を踏まえて,早速not-glossを使ってビューアを作成していきます.
not-glossについては割と詳しく下記で説明されています.
not-glossでお手軽3Dグラフィック描画 - Qiita
ただ,角度回転を使う際にはSpatialMathライブラリが必要ですので,そこだけご注意ください.(コンパイルすればすぐわかりますが)
また,text表示は日本語には対応していません.画像にして表示するのが良いと思います.
本当はテキスト入力とかパラメータ変更をできるUIにしたいのですが,その場合はwxHaskellとか使った方がいい気がしている.
今回はdisplay関数を使ってぐるぐる回せるものを作成しただけなので扱いで特にハマったところはありません.
アニメーションもいつか挑戦してもいいかもしれない.

金属の単結晶の場合は割と簡単で,xmlに元素ごとの安定な構造が記載されていますからそれにしたがって場合分けします

monoCrystal :: XML -> [VisObject Float]
monoCrystal xml = case structure xml of
    Just HCP -> hcpMonoStructure (fromJust $ _a xml) (fromJust $ _c xml)
    Just CCP -> ccpMonoStructure (fromJust $ _a xml)
    Just FCC -> ccpMonoStructure (fromJust $ _a xml)
    Nothing -> [drawText (show (name xml) ++" have no mono-crystal")]

今回,trace関数をはじめてまともに使いました.便利ですね
場合分けができたら結晶構造ごとにフレームを作成して元素を配置してokです.
グラフ理論では少し記述が難しいように感じたので今回全部決め打ちでコードにしています.ただ,この部分も外部ファイルに追い出しても良いかも.どなたかグラフでの表現含め,アイデアあればご教示ください.

いかに,六方最密構造の例としてMg,また立方最密構造の例としてAuを掲載します.

f:id:wvogel00:20200511183416p:plain
六方最密構造 Mg
f:id:wvogel00:20200511183507p:plain
立方最密構造 Au

イオン結晶

現段階ではまだ価数やポーリングの法則を使った場合分けができていないので,(陽->陰イオン配位数,陰->陽イオン配位数)= (6,4)の
NaCl型のものしか表示できませんが,その結果も掲載します.

f:id:wvogel00:20200511183725p:plain
NaCl型イオン結晶 NaCl

なんでこんなものを作り始めたのか謎ですが,結構はまってしまいました.