鼻詰まり時の鼻うがい
前回の慢性上咽頭炎の近況で書き忘れていたが、花粉症などで鼻が詰まっている時の鼻うがいについて記す。
当然だが、鼻の通りが悪い状態では鼻うがいもやりづらいし、鼻が完全に詰まっている状態では鼻うがいはほぼ不可能だ。
そのような時にも鼻うがいをやるテクニックとして鍼灸師さんから教えてもらったのが「スクワットをする」ことである。スクワットをすることで一時的に鼻が通るようになるので、その隙に鼻うがいをするのだとか。花粉症で鼻が詰まっている時に実際にやってみたが、確かに心持ち鼻が通る気がした。
他にも、「ペットボトルか何かで脇の下を圧迫すると、その逆側の鼻が通るようになる」というのをためしてガッテンでやっていた。ただしこれは長時間やり過ぎると腕の血流が悪くなるので要注意である。
また、個人的な経験では、朝、冷水で顔を洗ったり髪を濡らしたりした後に鼻が通りやすくなったと思う。逆に、うがいをすると鼻が詰まった。理由はよく分からない。
ただ、本当に花粉症が厳しい時は何をどうしても完全に鼻を通すのは不可能である。完全に鼻が詰まっている時は無理せず鼻うがいをしない方がいい。一応通るが通りが悪いときも、くれぐれも慎重にするべきである。無理に鼻の中に水を押し込むと、予想外の場所に水圧がかかって(多分)危険だと思う。
GHCにおけるヒープとスタック
先ほど、このような記事をQiitaに書いた。
Haskellを書いていて、再帰関数の終了条件設定をミスって無限ループさせてしまうことが何回かあったのだが、CPUだけでなくメモリも無限に持って行かれ、スワップを使い始めたあたりからキーボードへの応答も著しく鈍くなり、とにかく大変な目にあった。
どうやら再帰関数の実行によってサンクが無限増殖し、そいつがヒープメモリを肥大化させているようだったので、RTSオプションでヒープの上限を与える方法を調べたのだった。
さて、RTSオプションのドキュメントを読むと、以下のような記述があることに気付いた。
-K
size [Default: 80% physical memory size] Set the maximum stack size for an individual thread to size bytes.-M
size [Default: unlimited] Set the maximum heap size to size bytes.
-M
オプションはヒープメモリサイズの上限である。こいつがデフォルトで青天井なので、サンクの無限増殖で大変な目にあったのだった。
一方、-K
オプションはスタックサイズの上限であり、こいつはデフォルトで(物理メモリの8割という大容量とはいえ)上限が設定されている。
では、ヒープとスタックの違いはなんだろうか?と思って調べてみた。
結論から言うと、どうやら以下のようなことらしい。
- ヒープはサンク(など)が置かれる
- スタックはサンクを評価する時に使われる
上記のスレでは定番のfoldl
を用いて説明されている。
遅延評価版のfoldlでは以下のように評価が進む。
foldl add 0 [1,2,3,4] = foldl add (add 0 1) [2,3,4] = foldl add (add (add 0 1) 2) [3,4] = foldl add (add (add (add 0 1) 2) 3) [4] = foldl add (add (add (add (add 0 1) 2) 3) 4) [] = add (add (add (add 0 1) 2) 3) 4
ここで、add
は(+)
の別名とする(見やすさのため)。
上記で積み上がるadd
の列は未評価の式、つまりサンクであり、ヒープに置かれる。よって、例えば
foldl add 0 $ repeat 0
とするとサンクの無限増殖によってヒープが肥大化する。この場合、いくらスタックサイズを制限してもどうしようもない。
さて、foldlの評価ではadd
が積み上がったサンクがさらに評価されていくが、ここでadd
(というか(+)
)は正格評価である。よって、
- (add X 4)を評価する。
- Xを評価する。そのためにaddと4は一旦脇においておく。
- Xは(add Y 3)である。これを評価する。
- Yを評価する。そのためにaddと3は一旦脇においておく。
- ...
となる。ここで、評価を進めるために「一旦脇においておく」場所が(多分)スタックとなる。よって、リストに要素が増えれば増えるほどスタックも深く、大きくなり、そのうち上限に当たって死ぬ。
同様の議論はHaskell-cafe Stack, Heap and GHCでもされていた。
ちなみに正格版のfoldl'では以下のように評価が進む。
foldl' add 0 [1,2,3,4] = foldl' add 1 [2,3,4] -- (add 0 1)が評価される = foldl' add 3 [3,4] -- (add 1 2)が評価される = foldl' add 6 [4] -- (add 3 3)が評価される = foldl' add 10 [] -- (add 6 4)が評価される = 10
正格版ではサンクは肥大化しないし、評価も1ステップごとに行われるのでスタックも肥大化しない。よって、
foldl' add 0 $ repeat 0
としてもメモリは食われない。ただCPUがぶん回るだけである。
慢性上咽頭炎の近況 (3)
前回の近況からおよそ2ヶ月経ったので、ここまでの近況を記す。
ランニング
前回、鼻やノドへのダメージを避けるために河原のランニングを断念するかということを書いた。要は冷たく乾燥した空気にさらされるのが良くないと思ったので、代わりにジムのランニングマシーンで走ることを思いついた。
自分は週1回位しか走らないので、フィットネスクラブやジムは月謝が無駄になるかもしれない。そこで区役所のそばにある公営のジムを調べたところ、会員登録も月謝も必要なく、200円でトレーニングルームを3時間使えるとのことだった。これは良いと思い、最近はそちらに通っている。割と混むことが多いが、十分な運動はできていると思う。
ただ、さすがに最近暖かくなってきたのでもう外を走ってもいいかもしれない。が、最近は花粉という別の強敵に襲われている。
花粉症
今年は2月の中旬を過ぎたあたりから花粉を感じるようになった。
自分はそこまで花粉症が酷くなく、シーズンの初めだけキツいイメージなので普段は薬を飲んでいない。が、今はどのみち週1回耳鼻科に通っているので、オロパタジンを処方していただいた。また、耳鼻科の先生曰く、「Bスポット療法は花粉症にも効果があると言われている」とのこと。
花粉症で鼻が弱っているせいか、Bスポットはいつもよりかなりしみるようになった。また、Bスポット後にはものスゴい量の鼻水が出る。30分ほど経つと少し落ち着いてくる。
いろいろな人と話す
職場のいろいろな人と話す機会があり、その時に慢性上咽頭炎についていろいろ説明してみた。残念ながら慢性上咽頭炎の経験者はいなかったが、よく分からない体調不良を経験した人は何人かいた。次のような話を伺った。
- 原因不明の体調不良があったが、漢方と食生活の改善で克服したという人。中医学の専門医にしっかり診てもらい、自分に合った漢方薬を処方してもらったのだそうだ。
- 走ると腕のあたりにじんましんが出るという人。いまだに出るらしく、原因は不明。
- 原因不明のじんましんを発症したが、海外出張に行ったらいつの間にか治っていたという人。
- 食後に異常に眠くなるという体調不良があったが、検査したところテニスのジョコビッチと同じグルテン不耐症であったことが発覚。小麦を食べないようにしたら改善したという人。
それにしても人間の体はまだまだ分からないことだらけだ。
風邪
2016年3月21日(月)の午後から鼻詰まりがひどくなり、鼻呼吸が全くできなくなった。経験がある人はわかると思うが、鼻が完全に詰まるのは本当にストレスがたまるものだ。普段あまり意識していない所作でも口、ノド、鼻、耳の気圧がおかしくなり、下手するとどこか破裂しそうになる。その夜はずっと口呼吸で寝ていた。
翌日(3月22日)の朝起きるとノドが痛く、熱があった。また、ここ最近見ることのなかった粘性の強い緑色の鼻水が出た。いつも通っている耳鼻科を受診し、風邪薬を処方していただいた。その日は一日寝て過ごした。
その後、特に朝に緑色の鼻水が詰まることがあったが、週末(3月27日(日))にはほぼ回復していた。
が、3月29日(火)にはまた少し体調が悪化。緑色の鼻水が出て、また、鼻風邪を引いた時によくある鼻がツーンとする感覚があった。ちょうど去年の6月にノド風邪を引いた時も一旦良くなってからまた酷くなった覚えがある。一体どうなっているのか。結局その週も睡眠を多めに取り、体力回復に努めた。
今回の風邪の特徴的な症状として緑色の鼻水があったと思う。鼻の粘膜的な何かが弱っているのだろうか。そう思い、試しに朝夕やっていた鼻うがいのうち、夕方のターンを一時的にやめてみた。すると、朝の鼻水が少なくなった。鼻うがいとの因果関係はよく分からないが。
症状の状況
この2ヶ月間の症状はだいたい以下のような感じだった。
- 目まい。平常時はほとんど感じなくなったが、お腹が空いたり、ある程度お腹が空いている状態でお菓子を食べたりすると決まって目まいを感じた。
- 頭がぼうっとして、光が眩しくなる感覚。液晶モニターを眩しく感じることがあった。これはあまり頻度は高くなく、いつ起きるのかも分からない。
2月初めは症状が少なかったように思うが、花粉症の症状が出てからはこれらの症状がそれなりに出るようになった。ただ、3月に風邪を引いて以降はこういった症状はかなり少なくなったように思う。慢性上咽頭炎が治ってきたからか、花粉が減ってきたからか、それとも暖かくなってきたからだろうか。
とはいえ、一時的に症状がほとんどなくなることはこれまでにもあったと思うので、あまり楽観視はできない。
Xubuntu Xenial 16.04のベータテストをやっている
今年4月にリリース予定の(X)ubuntu 16.04はLTSであり、乗り換えを予定している。
ここ最近、リリース後しばらくしてから使い始め、バグを見つけ、報告するもそれが修正されない or 修正されてもLTSのレポジトリに入らないということが多かったので、今回はベータ段階からテストすることにした。
現在、VirtualBox環境で試用中。メモはWikiにまとめている。
http://debugitos.main.jp/index.php?Ubuntu%2FXenial%A5%D9%A1%BC%A5%BF%A5%C6%A5%B9%A5%C8
Emacs周りのメジャーアップデートで割と苦労しているが、今のところ何とかなっている。随時更新する予定。
それにしてもGtk3は本当に安定しないな・・・
nested Alternative (or MonadPlus)
最近仕事でattoparsecを使ってパーサを書いたりしている。パーサを書く際はAlternativeクラスの(<|>)が便利だ。
(<|>) :: f a -> f a -> f a
(a <|> b)
とはざっくり言って、「aを実行して失敗したらbを実行する」ということ。Alternativeをアクションだととらえれば、これは「アクションaが例外を投げたらそれを全てキャッチして無視し、bを実行する」という(やや乱暴な)例外処理として考えることもできる。
さて、Alternativeのインスタンスであるモナドはいろいろある。例えばMaybeがそうである。また一方で、Alternativeのインスタンスを作るモナドトランスフォーマーもある。例えばExceptTである。
そこで先日ふと気になったのだが、例えばモナド(ExceptT e Maybe)は(<|>)についてどのような挙動を取るのだろうか?この場合、outer monad(ExceptT)とinner monad(Maybe)の2種類の例外が存在する。普段何気なく(<|>)を使っているとこういう基本的なところが分からなくなる。
ということで、実験してみた。
import Control.Applicative ((<|>)) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (ExceptT, throwE) type NestedM = ExceptT String Maybe outerFail :: String -> NestedM String outerFail = throwE innerFail :: NestedM String innerFail = lift Nothing ret :: String -> NestedM String ret = return
で、ghciで試す。
*Main> outerFail "outer_fail" <|> ret "fallback" ExceptT (Just (Right "fallback")) *Main> innerFail <|> ret "fallback" ExceptT Nothing *Main> outerFail "outer_fail1" <|> outerFail "outer_fail2" ExceptT (Just (Left "outer_fail1outer_fail2")) *Main> outerFail "outer_fail1" <|> innerFail <|> outerFail "outer_fail2" <|> ret "fallback" ExceptT Nothing
これより、以下のことが分かる。
- NestedMの(<|>)は、outer monad(ExceptT)の例外をキャッチして後続のアクションを実行する。
- NestedMの(<|>)は、inner monad(Maybe)の例外はキャッチできず、inner monadは即座に例外状態(Nothing)に落ちる。
もちろん、このへんの挙動はモナドトランスフォーマーの仕様次第だが、少なくともExceptTはこうなっているらしい。
ExceptTの実装を見れば分かるが、結局outer monad(ExceptT)のアクションはinner monad(Maybe)のコンテクストで実行される。よってinner monadが例外状態に落ちれば、outer monadに実装された例外処理はそもそも実行されない。よってouter monadで定義された(<|>)はinner monadの例外をキャッチできない。当たり前といえば当たり前のことなのだ。
(<|>)は便利なメソッドだが、モナドスタック中に複数のinstanceが存在する場合は混乱を招くかもしれない。それに対し、MonadThrowとMonadCatchを用いた例外処理(いわゆるJavaっぽい例外処理)は投げられた例外の型によって脱出スコープを制御できる。そのため、一つの例外処理モナド(例えばCatchT)でいろいろな脱出スコープを実現できる。その拡張性・柔軟性が、MonadThrow/MonadCatchの巧みな点かなと思った。
慢性上咽頭炎の近況 (2)
前回の近況から2ヶ月経ったので、これまでのことを報告する。
続きを読むYou can write multiple lines in TemplateHaskell splices
One day, I found out that TemplateHaskell splices (that $()
thingy) accept multiple lines (confirmed on GHC 7.6.3), like,
{-# LANGUAGE TemplateHaskell #-} import Data.Aeson.TH (deriveJSON, Options(..), SumEncoding(..), defaultOptions) import Data.Aeson (encode) import Data.ByteString.Lazy.Char8 (unpack) data Sex = Male | Femail | OtherSex data Person = Person { first_name :: String, second_name :: String, age :: Int, sex :: Sex } $(do let opts = defaultOptions { sumEncoding = ObjectWithSingleField } derive_sex <- deriveJSON opts ''Sex derive_person <- deriveJSON opts ''Person return (derive_sex ++ derive_person) ) main :: IO () main = do let p = Person { first_name = "Toshio", second_name = "Ito", age = 30, sex = Male } putStrLn $ unpack $ encode p
The executable code above prints
{"first_name":"Toshio","second_name":"Ito","age":30,"sex":"Male"}
The top-level splice (the $()
where we use deriveJSON
) expects the type Q [Dec]
. Q
is just a Monad
, so we can use do
notation inside the splice.
I've searched on the Internet about this multi-line feature, but never found any information. Maybe it's too obvious.