ど根性ガエルの娘

昨日配信された15話が非常に話題になっています。

younganimal-densi.com - このウェブサイトは販売用です! - 白泉社 ふたりエッチ 木曽フミヒロ 我輩ノ彼ハ馬鹿である 土塚理弘 リソースおよび情報

14, 15話は非常に身につまされるものがあり、それはわたしが自身の経験や現状と対比させてしまうものを持っているからなのですが*1、多くの人にはどう届くのか、興味があります。

絆という言葉は、美しい言葉や心情を表すかのように、喧伝されるものではありますが、その意味は単に「断つことのできない人と人との結びつき」であり、語源をたどれば動物がたとえ意志を持って離れようとしても離れなれないようにするための綱であり、ザワザワしたものを感じえないものです。

愛情から出たものであるからそれを喜ばなければいけない、家庭が平穏であるためには相手が望む言葉を返さなければいけない。そういうどうしようもない関係性を築いていたとしても、それを断つこともできず、好意も愛着も消すことができない。

――これは絆だ、と思いました。

この漫画はまさしく家族の絆を描いていると思いました。わたし自身も未だにその絆に囚われたままであることを思い起こさせられます。

だから、とても共感してしまいます。


* * *


この漫画について、この15話のことが巷で話題になってから知ったのですが、その過程でみた意見について少し思うことがあります。

初期の話とここ最近(13話以降)の対比をもって、そこに矛盾を見出し、「連載開始時には隠されていた深い憎悪」や「告発の機をまっていた」「『現在は良好な家族仲』というのは嘘であった」的な解釈を見ると、やはりいろいろ感じるものがあります。

そうではない、とわたしは感じます。その矛盾的なものも含めてそれが描くべき家族の姿であり、一つのテーマと感じるからです。

作者に腹黒てき邪推をするかどうかは、そこに共感できるかどうかなのかな。なので、共感できるかどうかで大きく印象が変わってしまう(最初からこんな感じだったよという人と、急変したという人のギャップはそこなのかな、と思います)、でも、どちらの立ち位置に読者がいても、面白いと感じるこの踏み込みの深さは、この作品をきっと素晴らしい物にすると思います。

というわけでおすすめです。

*1:作中の環境と違って、わたしの父はギャンブルはしませんしふつうに稼ぎ続けましし、母は壊れませんでしたし虐待もありません。――って、なんか不自然な擁護っぽくなってしまってうまくかけないのですが(^^; あくまで共感の種になる似た傾向の経験があるという程度と言いたいです

Elm と Erlang

ElmErlang を使ったプログラミングを1ヶ月くらいしていました。気づけばミュータブルな変数を断って一ヶ月生活できたわけで、これは わたし史上 偉大な成果であります。ミュータブルな変数なんてイラなかったんだね!びっくりだー。

そんなお仕事体験という、ちょっと思ったことを軽くメモをとるつもりで書いてみましょう

Elm

Elm は実はよくわかっていないのですが、「構文はだいたいHaskellだよね?*1」といういい加減なアプローチと、あとはなんとなく型が合うようにコードを書けば期待通りに動いてくれるわけで、自分の頭を使っていない分、自分が堕落していくのがわかります。素晴らしい言語です。

一度ビルドが通ってしまえば実行時エラーと無縁というのは、素晴らしいことで、そのメリットだけに Elm を愛してしまいそうです*2Erlang で作った APIサーバーとやり取りする際の JSON のデコード周りに「ビルドが通ったのにエラー」が残っていて、逆にそこがまっさきに疑わしいというのが「らしい」という感じ。

Elm はフレームワークも非常によく出来ています。

  • まず、型として Event (そのページで受けうるイベントの直和)と、Model (レコード)を定義します
  • つぎに、EventによりModelがどのように更新されるかの update 関数を書きます
  • あとは Model をどういう Htmlにレンダリングするかの view 関数をかけば出来上がりです。

そんな枠組にはまってコードを書いてている時、その抽象度の高さと簡潔さに仕様記述をしているような気分になります。

正直、仕事で Elm を使うのは無謀としかいいようがないのですが、生き馬の目を抜く JavaScript のライブラリーやAltJSの世界をみると、来年のことを予想するのは鬼に笑われてもしかたがないなぁと思います*3。まぁ、うちの商品の SPA はおまけみたいなもんなので、Elmでできることしかしなくても問題ないでしょう。

ちょっと困ったのはわたしの Chromebook に Elm をインストールできなかったこと。今の elm は npm で簡単インストールできるのですが、わたしの Chromebook Flip は ARM なので npm ではインストールできず、自分でビルドする必要があります。が、ストレージの容量がたりず、ビルド環境を構築するだけの空き容量がない....orz


このままのあなたでいて。あとはどこまで泥仕合に巻き込まれないか、という点に注目したいなと思います。

Erlang

一方の Erlang。わたしは Smalltalk のメッセージ始まり、アクターモデル経由で Erlang に興味を持ちました。Prolog 由来のシンタックスも気に入っています。

とても動的なところは、「好きだー」と思う時と「型ほしいー」と思う時があります。折衷的に Dialyzer を使って、しこしこと -spec を書いています。これだけで随分な手違いを見つけてくれますが、チェックしてくれる内容にその手間に対して非力と思うこともあります。タプルをつかっているときは気持ちいいけれど、レコードを使っているときはどうにかならないかなと思ったり。

一見代入に見える = がパターンマッチで、異常系を簡単に退けられるのは嬉しいですし、それで落ちてしまってよし、という割り切りはコードを見やすくしてくれるので好きです。でも意外に try-of-catch を書いてしまう自分がいますね。ここらへんは わたしのErlang 力がたりないのかなぁ。

WebServer として Cowboy を使いました。99s のページには非常に親近感を感じます。基本的にはよいものだーと思うのですが、cowboy-rest ハンドラーは便利な反面 HTTPステータスコード を任意に返させてくれないのに困ったりしました。

rebar3 非常に便利で、もう手放せません。これがあれば Erlang アプリケーションをなんにも考えないで作れます。「rebar3 new から始める Erlang」なんて本があればいいのに。


* * *


わたしは、プライベートでもあまり積極的に関数プログラミングをしないですし、お仕事でもなんのかんので C++ のコードを書くことが多い日々を送っているので、関数プログラミングの当たり前がイマイチ手に馴染んでいません。

それは、うちの職場は関数プログラミングを愛している人ばかりなので、とても肩身が狭いですね。

*1::と::が逆になっているのがわたしには心地いい

*2:ちょっと前まで JavaScript で require is not required なぞという、えらく哲学的なエラーに苦しめられていただけに、感動すらしてしまいます。

*3:2015年のトレンドが AngularJS で 2016年は React.js とかいわれちゃうと...【翻訳】 2016年にJavaScriptを学んでどう感じたか - Endo Tech Blog

Kindle (安いやつ) を買ってみました

Amazon のセールで、プライム会員限定ですが Kindle (一番安い eペーパ端末)が、2,480円ということもあり、買ってみました。普段は8,980円なので、破格ですし、絶対的にも安いです。

とはいえわたしは、初代 Paperwhite(2012年モデル), 二代目 Paperwhite(2013年モデル)を持っていますので、どうしても必要というわけではないのですが

  • フロントライトのない Kindle を使ってみたい
  • 「キャンペーン情報」なるものがどれだけうざいものなのかを体験してみたい
  • 白ボディを体験してみたい

という、体感を得たかったのでポチッとしてみた次第です。

フロントライト無しについて

フロントライトのないKindle でみたい要素は3つあって

  • 導光板が表示面の手前にない
  • フロントライトでのドーピングがなくなるので見かけのコントラスト比が低くなる
  • そもそも のコントラスト比はどうなのか?
    • Paperwhite2013よりE-Ink の世代が古いので 同解像度E-Inkで比べればコントラスト比は低くなる
    • でも解像度は低いので、同世代E-Ink で比べればコントラスト比は高くなる
  • その低い解像度は読書にどれくらい影響がある?

というところ。

導光板が無いことについて

Kindle Paperwhiteがおもったより読み易くないのは、フロントライト層の厚みがあるせいだと思ってます。目がピントを合わせるべき面(文字面)の手前に反射やホコリで焦点面があり、そこにピンが迷うことが「目の疲れ」の一員じゃないかな、とおもってて、本来 eペーパー端末はそこがないのが強みです。

無印 Kindle では導光板がフロントにないので読みやすそうと思ったのですが、実際読みやすいですね。

ただ、フロントの保護の透明層があるので完全に表示面が露出しているわけではなく、若干の奥行きがあるかな?と思います。ホコリとか汚れとかがあるとやはり紙とは違う違和感を感じます。ですが「意識しないと違和感を感じにくい」というレベルであり、ホコリや汚れが字の上に浮いている感覚は フロントライトありのKindleに比べて遥かにすくないのは「楽」に感じますね。

コントラスト比について

画面のコントラストですが、Paperwhite は 解像度を上げてコントラスト比が落ちた分を フロントライトでドーピングして稼いでいます。そこは解像度が Paperwhite以前の世代である Kindle(無印) だとどうなのか。でも、 Kindle(無印) は E-Ink の世代が 2013以降の Paperwhite に比べて一世代古いので、その分素のコントラスト比が低いハズ。――などなど、いいのかわるいのかがスペックから全然読み取れませんので、実機に興味があったところです。

結論から言えば、Paperwhite(2013)のライトを最低にした状態*1よりもコントラスト比は悪く感じます。これは解像度が低いので文字がちょい太めにみえてしまうことと、ボディが白いことの影響もあるかなあ、と思います。感触としては新聞っぽい灰色さを感じます。Paperwhite は新聞より良い紙をつかってるようにかんじますから、やっぱり見かけのコントラスト比は低いですね。

もちろん新聞を読むのに困ったりしませんので、実用的には問題はないですが、快適度は違うなという実感です。

画面解像度について

うーん、厳しい。

一ページに収まる情報量がすくないとわたしはすごく読みづらく感じます。Kindle はもともと紙の本に対して「見開きにできない」という欠点があるので、それを補うために、わたしは、フォントサイズを最低にして読んでいるのですが*2、そうするとこの解像度は辛いな、と思わせます。

画数の多い漢字ですと一昔前のPCの画面フォントみたい。たとえば「器」という字の右半分の口と左半分の口のサイズが違うように見えてしまいます(明朝の場合)。普段愛用している「筑紫明朝」だと解像度がこのフォントの表示に耐えられていないのではと感じるものもあります。漢字だけでなくひらがなもパッとみただけでジャギジャギしているのがわかりますね。

ただこれは、わたしが 212ppi の Kindle になれてしまってるから厳しいというのはありそうです。300ppi の Kindle 入手したら 今の手持ちのKindleに同じ感想を抱きそうですねぇ(^^;

キャンペーン情報について

キャンペーン情報について、巷では2000円アップしても「なし」を選んでおけとよく言われていますが、わたしとしてはあんまり気になりませんでした。

キャンペーン情報ありを選ぶと

  • スリープ時に スクリーンセイバー(というか謎の絵)ではなく、月替りセールとかの広告が出る
  • ホーム画面の先頭にキャンペーン情報が追加される

という二点が変更になるようです。

最初のやつは、まぁ読んでない時の挙動ですので、いいかなー、と思います。電源ボタンでスリープ解除したときは、広告の解除にスワイプが必要なくして欲しいかな、と思います*3が、まぁ、しかたがないかな。

二点目は、正直 キャンペーン情報無し版でも先頭ページには「月替りセール」「ベストセラー」「Kindleセレクト25」とか表示されてるし、どんぐりの背比べかなぁ、と思います。若干「広告らしい体裁」なのがいやらしいですが、「ある」と「なし」の違いではなく、程度問題ではあるので、気にしない人なら大丈夫でしょう。

もともとどんなKindleを買っても広告は表示されます。これを「広告あり」ではなく「キャンペーン情報あり」と表記しているのはそういう事情があるからでしょう。

元からある広告の中で、一番鬱陶しいのが本を閉じる時です。

Kindleは普通にページめくりして最終ページにいったら閉じてくれません。「本を閉じる前に」というダイアログが表示されて、著者のその他の本や「この本を買った人は...」が表示されます。この情報自体は有益なのですが、「ページをめくる」というイベントをこのアクションがもっていってしまってるので、本を閉じるには「メニューを表示させてホームに戻る」操作をする必要になります。これが面倒くさいのです。

どうせ有料になるのなら、この操作をさせないで普通に本が閉じられるオプション(さらに2000円増しとか)もほしいですね。



――と、レビューを書いている間に Kindleファームアップが降りてきて、鬱陶しい度が若干上がりました。

新たに HOME 画面が増設されて、これまでのHOME相当が「マイライブラリ」と一階層下になりました。このページ自体が広告の塊ですね。それと「マイライブラリ」の先頭ページのみにでていた広告(キャンペーン情報)が常に表示されっぱなしになりました。

新しいHOMEは、読書中の「書庫」⇔「本」の動線を妨害するので、かなりうっとおしいです。新しいHOME自体は 設定でOFFにすることができ*4、これまでどおりのマイライブラリのトップをホームとして表示することはできます。

Kindle はもともとメニューからしか本を閉じられないのですので、ここに書庫ボタンがあればよかったのかもしれません*5が、もともとページを捲りきることで閉じられないのはおかしいという話ではあります。

ここらへんの「遷移のうっとおしさ」は、Kindleの「画面エリアごとの暗黙のボタン」の性もありますし、そもそもの仮想ボタン自身の押しにくさは eペーパー端末の宿命かもしれません。でもページめくりという概念で全部操作できている間は結構快適ですので、そういうふうに組み立ててほしいなぁ、と思います。でも Amazonって、商売への動線がつながりやすくるために意図的にやってる感があるのよね..。

白いボディについて

見た目はすっきりしててすごく好きなのですが、eペーパーの灰色さが目立つのが欠点かなぁ。実利面だけで選ぶのならば黒がいいかもしれませんね。あと手垢とか目立ちそうだなぁ、と思います。

でも白は独特の見た目の軽さがあって、わたし好きなんですけどね。

今後の使いみちについて

お風呂端末にする予定です。

お風呂で半身お湯に浸かりながら本を読むのが大好きなので、Paperwhite(2013)をお風呂に持ち込んでいるのですが、タオルにくるんで極力濡らさないように読んでいるとはいえ、蒸気に暴露されている環境ではあるわけで、流石に壊れてしまいそう。

そこで、この 2500円 kindle の出番となるわけです。どうせお風呂ではメガネをかけていないので、どうせぼやけて見えるので低解像度でも問題ないかなーと思ってます*6

おまけ /低価格系Kindle 仕様比較

Kindle Paperwhite(2012) Kindle Paperwhite(2013) Kindle Paperwhite(2015) Kindle7
発表日 2012年9月6日 2013年9月3日 2015年6月18日 2014年9月17日
発売日 2012年11月19日(日本) 2013年10月22日(日本) 2015年6月30日 2014年10月2日
世代 5 6 7 7
E-Ink Pearl Carta Carta Pearl
ppi 212ppi 212ppi 300ppi 167ppi
フロントライト あり あり あり なし
内蔵ストレージ 2GB 4GB(日本のみ) 4GB 4GB
通信方式(Wi-Fi) 802.11b/g/n 802.11b/g/n 802.11b/g/n 802.11b/g/n
通信方式 3G 3G 3G なし
価格(広告無し) 7,980円 9,980円 16,280円 10,980円
価格(広告無し+3G) 12,980円 14,980円 21,480円 なし
大きさ 169 x 117 x 9.1 mm 169 x 117 x 9.1 mm(多分) 169 x 117 x 9.1 mm 169 x 119 x 10.2 mm
重さ 213g(WiFi版)、221g(WiFi+3G版) 206g(WiFi版)、215g(WiFi+3G版) 205g (WiFi版)、217g(WiFi+3G版) 191g

もともとKindle Paperwhite が 第4世代のKindle の後継として誕生したことを考えると、Kindleの歴史は値上げの歴史ですね。円相場の影響も大きそうですが、電書立ち上げ期はどこも逆ざやだったのかもで、その是正から「ちゃんと儲かる電書端末」を模索しているのかもしれません。oasis とかその一環なのかしら。

ともあれ 5月22日までのPaperwhiteのキャンペーンで 7,300円引きになってるのですが、計算してみれば 21,480 - 7,300 = 14,180円で、ほぼ 前の世代のKindle Paperwhite の価格ですね。

*1:一番くらい明るさになるだけで、OFFにはならない

*2:わたしが老眼になる前に画面サイズの大きな Kindle が出てほしいなぁ

*3:この挙動の理由は、マグネットカバーを使ったオートスリープ・リリースだと広告が目に入らなくなっちゃうからでしょうね/まぁ動きを切り分ける仕組み自体でコスト増を招くのなら本末転倒ではあります

*4:立て三つ星(…を90°回展させたやつ)から [設定]→[端末オプション]→[Kindleのカスタマイズ]→[詳細設定]→[ホーム画面の表示] で オフ

*5:「戻る」ボタンの場合、書庫から本を開いていたとしても、ページ内ジャンプをしていたりすると書庫にもどれない

*6:老眼じゃなくって近視ですよ!

yecc/leex を使ってみる

yacc/lex のようなツールは、いろんなプログラミング環境向けにあります。Erlang にも yecc/leex と いうツールがありますので、今日はそれをちょっと使ってみるみたいな。それにしても e がくどいですね。

Step1: まずは構文解析

まずはHelloWorld 的な位置づけの、電卓を書いてみます。とりあえず構文のみ。

prs.yrl

Nonterminals
lines line expression term block.

Terminals
integer
'(' ')' '+' '-' '*' '/' '~n'.

Rootsymbol lines.

lines -> line.
lines -> lines line.

line -> expression '~n'.

expression -> term.
expression -> expression '+' term.
expression -> expression '-' term.

term -> block.
term -> term '*' block.
term -> term '/' block.

block -> '(' expression ')'.
block -> integer.

yecc の構文は yacc を踏襲しない独自なものになっています。押さえるべきは、

  • Token が {token-name, line-num} というタプルであることが期待されていること。(token-name は atom であればよい)。
  • Nonterminal で非終端要素、 Terminals で終端要素を教えてあげて、 RootSymbols でルートシンボルを指定する
  • あとは還元規則を書くだけ。

ビルドは、Erl からやる場合はこんな感じ。

1> y(prs).
{ok,"prs.erl"}
2> c(prs).
{ok, prs}

生成された prs.erl を見ればわかりますが、parse/1 という関数がつくられているので、これに入力を渡してあげます。まだ スキャナーが無いので、タブル直打ちで実行させてみましょう。

3> prs:parse([{integer, 1}, {'+',1}, {integer,1}, {'*',1}, {integer, 1}, {'~n',1}]).
{ok,'$undefined'} 

一応エラーにならずに実行されました。

Step2: 計算をさせてみる

それだけだと何にも意味がないので、処理をつけていく。トークンは、{トークン種別, 行数, …好きに使ってね…} という構成なので、3要素目にたとえば 数値を入れるとかして使う。

  • prs.yrl
Nonterminals
lines line expression term block.

Terminals
integer
'(' ')' '+' '-' '*' '/' '~n'.

Rootsymbol lines.


lines -> line.
lines -> lines line.

line -> expression '~n' : io:format("(ans) ~w~n", ['$1']).

expression -> term                : '$1'.
expression -> expression '+' term : '$1' + '$3'.
expression -> expression '-' term : '$1' + '$3'.

term -> block                     : '$1'.
term -> term '*' block            : '$1' * '$3'.
term -> term '/' block            : '$1' / '$3'.

block -> '(' expression ')'       : '$2'.
block -> integer                  : element(3, '$1').

構文木を構築せずにその場で計算してたり、一行毎に結果を標準出力に出したりしているなんともアレゲな感じですが、ゆるしてください。

意味値は '$1' で参照します。注意すべきは 1オリジン。ここいらへんは yacc と一緒です。

15> y(prs).                                                                    
{ok,"prs.erl"}
16> c(prs).                                                                    
{ok,prs}    
17> prs:parse([{integer, 1, 3}, {'+',1}, {integer,1,2}, {'*',1}, {integer, 1,5}, {'~n',1}]).
(ans) 13
{ok,'$undefined'}

Step3:Lexarを書く

leex をつかって 字句解析器を作ります。まぁ、意外性のない感じです。

  • scn.xrl
Definitions.

INT = [0-9]+
WHITESPACE = [\s\t]


Rules.

{INT}         : {token, {integer, TokenLine, list_to_integer(TokenChars) }}.
\+            : {token, {'+', TokenLine}}.
\-            : {token, {'-', TokenLine}}.
\*            : {token, {'*', TokenLine}}.
\/            : {token, {'/', TokenLine}}.
\(            : {token, {'(', TokenLine}}.
\)            : {token, {')', TokenLine}}.
\n            : {token, {'~n', TokenLine}}.
{WHITESPACE}+ : skip_token.

Erlang code.

TokenLine でそのToken のいる行数がとれ、skip_token で飛ばしたいものを指定します*1

Lexerのコードを作るには leex:file/1 を使います。 yecc みたいにy(). 的な erl 上の短縮名はないみたい。

40> leex:file('scn.xrl').
{ok,"./scn.erl"}
41> c(scn).   
{ok,scn}
42> scn:string("42+2 * 3").
{ok,[{integer,1,42},
     {'+',1},
     {integer,1,2},
     {'*',1},
     {integer,1,3}],
    1}
43> 

せっかくなので、さっき作ったパーザーとくっつけてみます。srn:string/1 で文字列から字句解析。

86> prs:parse(element(2, scn:string("(1+1)\n"))).
(ans) 2
{ok,'$undefined'}

ファイルから読み込むのはイカのような感じ。

  • calc.txt
1 + 1
3 + 4 * 5
(6 + 7) * 8

file:read_file/1 で読み込んだ内容を binary_to_list/1 で文字列化して testscn:string/1 に食べさせる。

60> test:parse(element(2, testscn:string(binary_to_list(element(2, file:read_file('calc.txt')))))).
(ans) 2
(ans) 23
(ans) 104
{ok,'$undefined'}

Step4: 曖昧性と衝突の解決

Reduce/Recude 衝突 と Shift/Reduce 衝突

LR(1) の構文解析の仕組みは、いっぱい本がでていますので、ホントはそちらを読むのが良いかと思いますが、とりあえず「どんな規則をかけばよいのか」「どんな規則を書いたらだめなのか」を説明します。

  • 生成されるパーザーは、トークンを順番に喰っていき(これをシフト(Shift)という)、喰ったトークン列が 還元規則の左側にマッチするとき 右側に還元(Reduce)される
  • 左辺側に還元(Reduce)するときには、ただ一つの可能性しか内容に書く。そうしないと Reduce/Reduce Conflict が起こる
  • シフト(Shift)すべきか 還元(Reduce)すべきかわからなくなるときも Shift/Reduce Conflict が起こる

もうすこし補足すると LR(1) の 1 は、「一つ先読み」という意味なので、2番目3番目のやつは「一つ先読みしても/一つ先読みしたら」どっちをやればいいのかわからなくなるということ。

Reduce/Reduce衝突はたとえばこれ

  • err1.yrl
Nonterminals target block.
Terminals a b c.
Rootsymbol target.

target -> a b c.
target -> block.

block -> a b c.
10> y(err1).
err1.yrl: Parse action conflict scanning symbol '$end' in state 5:
   Reduce to target from a b c (rule 1 at line 5)
      vs.
   reduce to block from a b c (rule 3 at line 8).
err1.yrl: Warning: conflicts: 0 shift/reduce, 1 reduce/reduce
error

a b c と食べた時、どちらの還元規則を適用すればいいか、パーザーにはわかりません。これはエラーになります。

Shift/Reduce 衝突はこんな感じ。

  • err2.yrl
Nonterminals target block.
Terminals a b c d.
Rootsymbol target.

target -> a b c d.
target -> block d.

block -> a b c.
12> y(err2).
err2.yrl: Warning: conflicts: 1 shift/reduce, 0 reduce/reduce
{ok,"err2.erl"}

a b c と来た場合、その後 d をシフトすべきか、 a b c をblock に還元すべきかわかりません。これは、警告になります。

shift/reduce 衝突はよ 中置演算での数式と、ぶら下がり else が典型的です。たとえば以下の様な規則において、"a + b - c" という入力があったとき a + b の次のアクションとして、"a + b" を式に還元すべきか、- c を シフトすべきかわかりません。

  • err3.yrl
Nonterminals exp.
Terminals integer '+' '-'.
Rootsymbol exp.

exp -> integer         : element(3, '$1').
exp -> exp '+' exp     : {'+', '$1', '$3'}.
exp -> exp '-' exp     : {'-', '$1', '$3'}.
26> y(err3).
err3.yrl: Warning: conflicts: 4 shift/reduce, 0 reduce/reduce
{ok,"err3.erl"}

ちなみにこの警告を無視するとどうなるかというと、右結合として処理されます。

28> err3:parse([{integer,0,"1"}, {'+',0}, {integer,0,"2"}, {'-',0}, {integer,0,"3"}]).  
{ok,{'+',"1",{'-',"2","3"}}}

これを避けるため、Step1 では term という 非終端要素を用意して、二項演算子の右辺と左辺を別の要素として定義しているわけですね。(さらに、* と / の優先度を実現するために block という要素も作って3段構えになっています)

結合規則と優先順の指定(優先順位規則)

本質的には上記のように逃げる(というより構文としてちゃんと定義してあげる)のが正しいのですが、それは結構めんどくさいことも多いので、Yacc のようなパーザジェネレータは トークンに対してどちらに結合するのかと、その優先順位を指定することが出来るオプションが設けられていることが多いです。もちろん yecc にもありますので、それを使ってみます。

  • prs.yrl
Nonterminals
lines line expression uminus.

Terminals
integer
float
'(' ')' '+' '-' '*' '/' '~n'.

Left  300 '+' '-'.
Left  400 '*' '/'.
Unary 500 uminus.

Rootsymbol lines.

lines -> line           : ['$1'].
lines -> lines line     : '$1' ++ ['$2'].

line -> expression      : io:format("(ans1) ~w~n", ['$1']), '$1'.
line -> expression '~n' : io:format("(ans2) ~w~n", ['$1']), '$1'.

expression -> expression '+' expression : '$1' + '$3'.
expression -> expression '-' expression : '$1' - '$3'.
expression -> expression '*' expression : '$1' * '$3'.
expression -> expression '/' expression : '$1' / '$3'.
expression -> '(' expression ')'        : '$2'.
expression -> integer                   : element(3, '$1').
expression -> float                     : element(3, '$1').
expression -> uminus                    : '$1'.

uminus -> '-' expression                : io:format("(minus~w)", ['$2']),  -1 * '$2'.
  • scn.xrl
Definitions.

INT = [0-9]+
WHITESPACE = [\s\t]


Rules.
{INT}\.{INT}  : {token, {float,   TokenLine, list_to_float(TokenChars) }}.
{INT}         : {token, {integer, TokenLine, list_to_integer(TokenChars) }}.
\+            : {token, {'+', TokenLine}}.
\-            : {token, {'-', TokenLine}}.
\*            : {token, {'*', TokenLine}}.
\/            : {token, {'/', TokenLine}}.
\(            : {token, {'(', TokenLine}}.
\)            : {token, {')', TokenLine}}.
\n            : {token, {'~n', TokenLine}}.
{WHITESPACE}+ : skip_token.

Erlang code.
  • calc.erl
-module(calc).
-export([from_string/1, from_file/1]).

from_string( String ) ->
    {ok, Tokens, _} = scn:string(String),
    {ok, AnsList}   = prs:parse(Tokens),
    AnsList.

from_file( FileName )->
    {ok, F} = file:read_file(FileName),
    from_string(binary_to_list(F)).
解説
Left  300 '+' '-'.
Left  400 '*' '/'.
Unary 500 uminus.

の部分が結合規則になります。どのように結合するか(あるいはしないか)を Left Right Nonassoc で指定します。その次の数値は優先順位になります。数字の大きい方が優先順位が高いです。

Left と Right の働きは以下を見るとわかりやすいかな。

  • test1.yrl
Nonterminals exp.
Terminals integer '+' '-'.
Rootsymbol exp.

Left 100 '+' '-'.

exp -> integer         : element(3, '$1').
exp -> exp '+' exp     : {'+', '$1', '$3'}.
exp -> exp '-' exp     : {'-', '$1', '$3'}.
  • test2.yrl
Nonterminals exp.
Terminals integer '+' '-'.
Rootsymbol exp.

Right 100 '+' '-'.

exp -> integer         : element(3, '$1').
exp -> exp '+' exp     : {'+', '$1', '$3'}.
exp -> exp '-' exp     : {'-', '$1', '$3'}.
3> test1:parse([{integer,0,"1"}, {'+',0}, {integer,0,"2"}, {'-',0}, {integer,0,"3"}]).
{ok,{'-',{'+',"1","2"},"3"}}
6> test2:parse([{integer,0,"1"}, {'+',0}, {integer,0,"2"}, {'-',0}, {integer,0,"3"}]).
{ok,{'+',"1",{'-',"2","3"}}}

ちなみに

  • test3.yrl
Nonterminals exp.
Terminals integer '+' '-'.
Rootsymbol exp.

Nonassoc 100 '+' '-'.

exp -> integer         : element(3, '$1').
exp -> exp '+' exp     : {'+', '$1', '$3'}.
exp -> exp '-' exp     : {'-', '$1', '$3'}.

にすると、

9> test3:parse([{integer,0,"1"}, {'+',0}, {integer,0,"2"}, {'-',0}, {integer,0,"3"}]).
{error,{0,test3,["syntax error before: ","'-'"]}}

とエラーになります。これは例えば C言語で、 "A < B < C" がエラーになるのを期待するようなケースですね。



よく単行演算の '-' を ゴニョゴニョするために使われる Yacc の %prec に相当するものはなさそうで、そのかわりUnarry を使うようです。 %prec は他にもいろいろできちゃうので、よくないな、と廃止されたんじゃないかな、とか思ったり。

もし、

Left  300 '+' '-'.
Left  400 '*' '/'.
Unary 100 uminus.

のようにして パーザーを生成させると

83> calc:from_string("-3+2").       
(minus6)(ans1) -5
[-5]

となり-(3+2)となるのがわかります。

まとめ

優先順位規則の適用は、シンプルに shift/reduce衝突を解決でき、すっきりとした文法記述を実現するスマートな手段に思えます。しかし、それが効果的に振る舞う状況は限られており、その実態は goto 文のように厄介な極まりない難読文法を作ってしまいがちです。

lex & yacc プログラミング の 3.5.1 より引用すると、

文法中で発生するすべてのシフト/還元衝突は優先順位規則を使用することで解消することが出来る。しかしながら、この考え方はけっして頭のいい方法ではない。数式文法中の衝突の原因は簡単に理解できるので、優先順位規則がもたらす効果も明白であった。ほかの場合でも、優先順位規則はシフト/還元衝突の問題を解消してくれるが、優先順位規則が文法にもたらす影響というものは、一般には判断しづらい。
優先順位を使うのは次の二つの場合にとどめるように忠告しておく。数式文法中と if-then-else形式の文法中での「懸垂 else (dangling else)」を解消する場合である。これ以外の場合には、できるならば、文法を修正して衝突を解消したほうがいい。

なんてことが書いてあります。ゴールデンハンマーしないように気をつけたいところです。

lex&yaccプログラミング (NUTSHELL HANDBOOKS)

lex&yaccプログラミング (NUTSHELL HANDBOOKS)

原著第二版の翻訳です。1994年の古い本です(オライリー・ジャパンがまだ無い頃なので ASCIIから出ている、というくらい古い)が、決定版とでも言うべき内容になっています。でも、そろそろ flex & bison を翻訳してくれてもいいとおもうの。

flex & bison: Text Processing Tools

flex & bison: Text Processing Tools

*1:サンプルとかだと、Erlang code. の節はなくても良いっぽい感じだったのだけれども、自分でやってみたら「ないよ!」と怒られるのでつけてみたら解決した

スイッチングハブについて素人っぽくアレコレ

オフィスのスイッチングハブを買い足すことになったのですが、わたしはインフラ周りは疎いので、スイッチングハブってどこのメーカがよいのかさっぱりわかりません。ちなみに我が家のハブはプラネックスが多いのですが、根拠あってのものではありません(買った当時に安くって金属筐体のものを選んだら、たまたまプラネックスになっただけです)。なので、少し調べてみました。

お勧めのスイッチングハブ - usyWiki

2ch のおすすめのスイッチングハブ スレのまとめwiki.

ここによると、

  • 一般人なら量販店でデザインで選べ
  • ちょっと機械好きなら NETGEAR
  • 割りと機械好きならアライドテレシス
  • すごく機械好きなら個々で聞くまでもなく自分で選べるだろ

以上

とのことです。わふぅ!アライドテレシス!!、八角形の板金ボディに虹のラインでATi のロゴ。こんなの↓

カッコイイ!

わたしが新人のころ居た会社でよく転がっていたのはコイツですので、「信頼できるヤツ」ってブランドがわたしのなかで出来上がってるのですよね!媚びてない外観がとてもキュートです。


さてさて、2ch のスイッチ大好きな人の話をざっとあらうと、選択のポイントは

  • チップ
  • 発熱/排熱 *1
  • 動作温度 (発熱も含めてなので、余裕があるとなお嬉しい)
  • ケミコンの品質 (熱はケミコンの大敵です)
  • EMI対策 (VCCIクラス)*2
  • JumboFrame対応 *3

みたいな感じ。

島ハブは、まぁそこら辺の適当なメーカーを買っても「ハズレ」を引かなければ問題ないかな?上流のスイッチはちょっと真面目に選ぼうということ(当たり前)。

上流のスイッチは、大した規模じゃないですし管理する人もいないですし、今回はアンマネージドスイッチでいいかな、と思ってます。がっつり乱暴ブランドのみでセグメント分けするならば、ご予算潤沢ならば

そうでなければ

  • NETGEAR

かなぁ、といった印象。結局素人はブランドで選ぶなぁ、と痛感した次第(逆ブランドにも敏感になっちゃうしね)。*4

余談

今回調べてみて初めて知ったのですが、ヤマハってL2スイッチ つくってたんですね。

【特別企画】ヤマハが満を持して提供するスイッチ「SWX2200」試用レポート - クラウド Watch

2011年からの参入だそうです。

――このタイミングでスイッチ市場に参入した意図は?

平野:スイッチ市場には、すでにたくさんのメーカーや製品が出ています。その中でアンマネージドスイッチが圧倒的に数が多いわけですが、それがあるべき姿かは疑問に思っていました。ユーザーはネットワークを管理したがっているのに、いままでのマネージドスイッチは高価で難しいため手が出せず、やむをえずアンマネージドスイッチを使っているのではないか、という仮説を持っていたわけです。

ルーターと連携する形だそうですが、Luaも使えるそうですし、面白そうかも。


・・・なんか NAVERまとめのような 記事を書いてしまった...。

*1:電源内蔵なら、金属筐体にしたいよね、とか金属筐体とヒートシンクがつながってるといいね!とか

*2:VCCI クラスBが家庭用で厳しい規格(当該機器から10メートル以内の距離でテレビやラジオ等の放送受信機を使用することが予想される事を想定)。クラスAが商業環境用

*3:通常は速度にそんなに効かない(せいぜいCPU負荷が軽くなる程度) だが、ギガサイズのファイルのやり取りに有効とか。サイズは 9K が事実上の標準で、12Kを超えるとエラー検出が弱くなるなるため実用的でない

*4:マニアは個別の製品について語れてこそマニアなんだなぁ

Kami(かつての Notable PDF)で 日本語PDFの表示が壊れるようになった

Chromebook で使える わたしが知るうちで唯一の「見開きページ表示」ができる PDF リーダーである Kami が、ごく最近のアップデートが原因(たぶん)で、本がまともに表示できなくなりました。

こんなかんじ。

ご覧の有様だよっ!(死語)

これはオーム社で買った関数プログラミング入門を表示している有様なのですが、これ、つい一昨日まではちゃんと読めていたのですよね。一方で、

のように、同じオーム社プログラミングClojure は問題なくいけるのですよね。なにがちがうのかしら。

とはいえ、一部の書籍が読めないではなく、多くの書籍が読めないは困り者です。手持ちの電書PDFを開いてみたらこんな塩梅に。

関数プログラミング入門 オーム社
プログラミングClojure(第二版) オーム社
アジャイルな見積りと計画づくり 達人出版会
プログラミングErlang オーム社
プログラミングHaskell オーム社
7つの言語7つの世界 オーム社
型システム入門 オーム社
Gitによるバージョン管理 オーム社
自由自在 Squeak プログラミング
SICP非公式日本語訳(id:takeda25版)
入門GTK+

むー、、、。これだけ読めないとさすがにきびちい。

Chrome自体のPDF閲覧機能では読めるのと、2台の ChromebookWindows 上の Chrome で同アプリを動かしても減少がまったく同じことから、すくなくともアプリ側にも原因があるのは確実です。このような場合、修正されるまでは「Kami がだめなら ○○をつかえばいいじゃない」としたいところですが、アプリの層が薄い(しかも日本市場が小さい)Chromebook、代替手段がないのが本当にきびしい。

おしえて、Chromebook 好きの偉い人...(T△T

追記 (2016/02/24)

今日見たら治ってました。素早い対応に感謝です。

オフラインでもPDF読めるので、ローカルPC側でレンダリングしてるのだと思っていたけれども、実行するスクリプトを毎度サーバーから取ってきてるのかな?てことは、ただのサーバートラブルなのかしら。

Chromebook の容量枯渇問題

足りてないって怒られた

なんか Chromebook の容量がいっぱいになったとメッセージが出ます。

確認してみれば、

はふぅ、1.1GBしか無いってばよ。

丁度 GNU Smalltalk をビルドした直後だったので、Crouton の Ubuntu がいっぱいということかな、と思ったので、du で確認してみました。

$ sudo du -s -h /mnt/stateful_partition/crouton
3.6G    /mnt/stateful_partition/crouton

3.6GB。いろいろやる前はどのくらいだったのか見ていないのだけれども、思ったより健闘(?)しています。それでも確かに、16GB のフル容量に対してしまえば、それなりにインパクトがあるのですが、うーん、これを削るのは難しそうです。全体を df で見てみましょう。

$ df -h
Filesystem                                                    Size  Used Avail Use% Mounted on
/dev/root                                                     1.2G  873M  332M  73% /
devtmpfs                                                      2.0G     0  2.0G   0% /dev
tmp                                                           2.0G  3.3M  2.0G   1% /tmp
run                                                           2.0G  376K  2.0G   1% /run
shmfs                                                         2.0G  160M  1.9G   8% /dev/shm
/dev/mmcblk0p1                                                 11G  7.9G  1.9G  82% /home
/dev/mmcblk0p8                                                 12M   24K   12M   1% /usr/share/oem
/dev/mapper/encstateful                                       3.1G   76M  3.0G   3% /var
media                                                         2.0G     0  2.0G   0% /media
none                                                          2.0G     0  2.0G   0% /sys/fs/cgroup
/home/.shadow/3314e1fd7f1317c0e5f28abbe37227f8a48af75b/vault   11G  7.9G  1.9G  82% /home/chronos/user
tmpfs                                                         128K   12K  116K  10% /run/crw
/dev/mmcblk1p1                                                 58G  487M   58G   1% /media/removable/SD Card


/home/.shadow の下がいっぱい容量を食べています。11GBの全体サイズにたいして約8GB持っていってます。このフォルダはユーザーランドになっていて、ChromeOS では Chromeやそのアプリ以外の プログラムが動かせない領域になっているそうです。ふーむぅ。

ということは、Chromeアプリ自体や、そのキャッシュで結構容量を使い果たしているのですね。たしかに Kindle とかいっぱい本がダウンロードしてありますし(のんのんびより 全巻とか)、電子書籍アプリも自前領域にコピーしてキャッシュしてるっぽいので、これらが効いていそうです。

なのでアプリからセコセコ削除してみたのですが、再び容量を確認してもサイズが縮まっておりません。ふにー、どうしたら開放されるの? (T△T

Crouton を避けるしかない?

うにゅー。仕方がないので、Crouton を外部ストレージに避けるしかないのかな?といろいろ調べ始めたのですが、ちょっと問題ありありです。

まず、Chromebook に増設できるストレージは、SDカードか、USBメモリーです*1。ならば、ここに Crouton の chroot 先を作ればよいわけです。

となれば、指しっぱなしでも取り扱いが安心の SDカードで行きたいところですが(今も64GB のマイクロSD を指しっぱなしにしていますし)、少し調べてみたら、Chromebookって SDカードがスリープさせると毎にアンマウント/マウントされる仕様なんだそうですよ。……全然しらなんだ。

そうすると、ここにUbuntu環境を作ってしまうとスリープするたんびに落ちてしまうわけで、それは全く嬉しくないです。

そんなわけなので、Chromebook ユーザ界隈では 出っ張らない USB 3.0 のメモリーを指しっぱにして、そこに Crouton を入れるのが定番になっているそうですが、実は Chromebook Flip って、USB 3.0 のポート無いのですよね......orz

もちろん USB2.0 のポートはあるので、そこで Croutonってのは出来るのですが、流石に速度がたりませんにゃ。

うーん、ニントモカントモ。

*1:SSD の換装が出来る機種もごくまれにありますが、殆どの機種が eMMC でボード直付けです