来世から頑張る!!

技術ブログを目指して

MonadTって自動でMonadにはならないの???

Monadって難しいって話。

元ネタは xuweiさんのブログ

いやね、最近PlayでWebアプリ作ろうと頑張ってるんですよ。
そこでログ出力のために、引数で渡すものを必要に応じてWriterにできればなとか考えてたんですよ。
結果うまくはいかなかったんですが。

本編以外を極力省くため、試すコードはただの足し算。

def add(a: Int, b: Int): Int = a + b

これをブログを参考にIdでくるむ。

import scalaz._, Scalaz._

def add(a: Id[Int], b: Id[Int]): Id[Int] = for {
  x <- a
  y <- b
} yield x + y

// 実行
> val a = add(1, 2)
a: scalaz.Scalaz.Id[Int] = 3

それぞれIdに入っちゃったから取り出すためのfor書いて、中の値を使って処理。

これをさらにブログに書いてある通り、任意の入れ物から取り出せるように変更。

import scalaz._, Scalaz._
import scala.language.higherKinds

def add[F[_]](a: F[Int], b: F[Int])(implicit F: Monad[F]): F[Int] = for {
  x <- a
  y <- b
} yield x + y

// 実行
> val a = add[Id](1, 2)
a: scalaz.Scalaz.Id[Int] = 3

[Id]って書かないと実行できなくなった。。。
まあそこはIdで使うかどうかなのできっと問題無いのかな?
OptionやListで渡していれば予想通りの動き。

> val a = add(1.some, 2.some)
a: Option[Int] = Some(3)

> val b = add(List(1, 3, 5), List(7, 8))
b: List[Int] = List(8, 9, 10, 11, 12, 13)

MonadTransformer使って合成

> val a = List(1, 2, 3).liftM[OptionT]
a: scalaz.OptionT[List,Int] = OptionT(List(Some(1), Some(2), Some(3)))

> val b = List(5, 7).liftM[OptionT]
b: scalaz.OptionT[List,Int] = OptionT(List(Some(5), Some(7)))

> val c = add(a, b)
error: no type parameters for method add: (a: F[Int], b: F[Int])(implicit F: scalaz.Monad[F])F[Int] exist
so that it can be applied to arguments (scalaz.OptionT[List,Int], scalaz.OptionT[List,Int])

!?!?
OptionT[List, Int]なんてMonadじゃねぇぞ!!!って言われる。なんで?なんで?

よーしパパMonadとか実装しちゃうぞー(結婚したい)

type OptionTList[A] = OptionT[List, A]
implicit val OptionTListMonad = new Monad[OptionTList] {
  def point[A](a: => A) = List(a).liftM[OptionT]
  def bind[A, B](fa: OptionTList[A])(f: A => OptionTList[B]) = fa flatMap f
}

> val c = add(a, b)
error: no type parameters for method add: (a: F[Int], b: F[Int])(implicit F: scalaz.Monad[F])F[Int] exist
so that it can be applied to arguments (scalaz.OptionT[List,Int], scalaz.OptionT[List,Int])

通らない・・・

> val c = add[OptionTList](a, b)
c: OptionTList[Int] = OptionT(List(Some(6), Some(8), Some(7), Some(9), Some(8), Some(10)))

これなら通る。 つまりこう?

> val a: OptionTList[Int] = List(1, 2, 3).liftM[OptionT]
a: OptionTList[Int] = OptionT(List(Some(1), Some(2), Some(3)))

> val b: OptionTList[Int] = List(5, 7).liftM[OptionT]
b: OptionTList[Int] = OptionT(List(Some(5), Some(7)))

> val c = add(a, b)
c: OptionTList[Int] = OptionT(List(Some(6), Some(8), Some(7), Some(9), Some(8), Some(10)))

むぅー。。。
やり方を間違っている気しかしない。。。

やっぱり元ブログみたいにTraitでくるんでコンストラクターを中で呼び出さないとダメなのかな?
それとも、Monadの代わりに何かそれっぽいのがMonad Transformer用にある???

2015/04/23 追記 Scala勉強会にてねこはる先生に教えてもらった。

> val a = List(2, 3, 5).liftM[OptionT]
a: scalaz.OptionT[List,Int] = OptionT(List(Some(2), Some(3), Some(5)))

> val b = List(1, 9).liftM[OptionT]
b: scalaz.OptionT[List,Int] = OptionT(List(Some(1), Some(9)))

> val c = add[({type F[A] = OptionT[List, A]})#F](a, b)
c: scalaz.OptionT[[+A]List[A],Int] = OptionT(List(Some(3), Some(11), Some(4), Some(12), Some(6), Some(14))

Scala型推論オブジェクト指向的継承のために残念な部分が多い」ってのはこういうことなのかなぁ?

簡単に分かるPrologの魅力

昨日はPlayもくもく会に行ってきました。 色々と教えていただいてありがとうございます。

で、その中でたまたまprologのお話になったので、
prologの魅力が簡単に分かるような「prolog向きの」サンプルを書いてみたいと思います。

ちなみに、prologは頑張れば関数型言語向きの事もそれなりに出来たりはしますが、
向き不向きが激しい言語なので使い道は選びます。

prologは「ルールは分かるけど解き方はわからない」ものを解くプログラムが簡単に書けます。

今回はルール説明が要らないであろう、「ハノイの塔」でやりたいと思います。

prologハノイの塔

一応簡単に説明すると、地面に刺さった3本の棒といくつかの大きさの違う円盤を使ったゲームです。

  1. 円盤は左端の棒に大きいものから順に上に行くほど小さくなるように刺さっています。
  2. プレイヤーは1回につき1枚の円盤を他の棒に移動させることができます。
  3. ただし、円盤の上にはその円盤より大きい円盤は乗せることができません。
  4. 全ての円盤を右端の棒に移動させればゲームクリアです。

大体こんな感じのはずです。
実装にはgplorogを使います。他はわからないので。

棒の実装

難しいのは嫌なので、棒をリスト、円盤を数値にしてしまいます。 円盤がゼロの棒を[]、小さい円盤がひとつささった棒を[1]
複数個刺さっていたら[1, 2, 3]とか[2, 3, 5, 8]とかですね。

hanoi_tower([]).           % 空の塔
hanoi_tower([_|[]]).       % 1つの円盤が入った塔
hanoi_tower([H1, H2|T]) :- % 2つ以上の円盤が入った塔
  H1 < H2,
  hanoi_tower([H2|T]).

ここでやったことは単純に1本の棒の可能性を列挙しただけです。
|が最初の要素と後続のリストをわける記号になります。
lispで言うcarとcdr、haskellのheadとtailですね。 _(アンダースコア)は何らかの値が入るけど使わないので名前を付けないってやつです。

なので、棒(=hanoi_tower)は空([])か1つの円盤([_|[]])か2つ以上の円盤です。

2つ以上の時は上の円盤は下のより小さくないといけないので、大小比較をしています。
動作確認をしてみましょう。

$ gprolog --consult-file hanoi.pl

で指定したプログラムを読み取ってREPLを開けます。

| ?- hanoi_tower([]).

yes
| ?- hanoi_tower([1]).

true ? a

no
| ?- hanoi_tower([10]).

true ? a

(3 ms) no
| ?- hanoi_tower([1, 2, 3]).

true ? a

no
| ?- hanoi_tower([1, 3, 2]).

no

作れるはずのリストにtrueが返って来て居たら成功です。

棒のセットの実装

これも簡単にリストのリストで実装します。
[[1, 2, 3], [4, 5], []]こんなのです。

hanoi_set(X) :-
  X = [L, M, R],
  hanoi_tower(L),
  hanoi_tower(M),
  hanoi_tower(R).

読み方としては、

  1. hanoi_setであるXは要素数3のリスト[L, M, R]である。
  2. Lhanoi_towerである。
  3. Mhanoi_towerである。
  4. Rhanoi_towerである。

こんな感じです。

同じくREPLで確認します。

| ?- hanoi_set([[1, 2, 3], [4, 5], []]).

true ? a

no
| ?- hanoi_set([[1, 3], [2, 4, 5], []]).

true ? a

no
| ?- hanoi_set([[1, 3], [2, 4, 5], [7, 6]]).

no

多分大丈夫かと。

移動ルールを実装

ついに移動ルールを実装します。
ルール名をhanoiとしましょう。 引数を3つとって、元の状態、移動ルール、異動後の状態とします。

prologに慣れていないと何を言っているの?となりますが、
普通のプログラミング言語なら最後の引数は戻り値なんだと思えば大体あってると思います。

hanoi([[1, 2, 3], [], []], left_to_middle, X).

とした時に、X[[2, 3], [1], []]と入っていてほしいということです。

異動の種類としてはleft_to_middle, left_to_right, middle_to_left, middle_to_right, right_to_left, right_to_middleの6種類ですね。

hanoi(X, left_to_middle, Y) :-
  hanoi_set(X),
  [XL, XM, XR] = X,
  [H|T] = XL,
  Y = [T, [H|XM], XR],
  hanoi_set(Y).
  1. Xhanoi_set
  2. Xのそれぞれの列がXL, XM, XR
  3. XLの一番上をH, それ以外をTとした時
  4. Yは左からT, [H|XM], XRとなっていて
  5. Yhanoi_setの条件を満たしている(= ルール上可能な移動である)
| ?- hanoi([[1, 2, 3], [], []], left_to_middle, Y).

Y = [[2,3],[1],[]] ? a

no
| ?- hanoi([[2, 3], [1], []], left_to_middle, Y).

no
| ?- hanoi([[], [1], []], left_to_middle, Y).

no

他もまあ同様に実装できます。(が、省略します。)

hanoi(X, left_to_right, Y) :-
hanoi(X, middle_to_left, Y) :-
hanoi(X, middle_to_right, Y) :-
hanoi(X, right_to_left, Y) :-
hanoi(X, right_to_middle, Y) :-

複数回の移動に対応する

先ほどのhanoiは移動処理が単純な変数なので、1回の移動しか表現できません。

そこで、移動処理もリストにしてみましょう。
まず、移動0回の場合は空のリスト[]で移動前と移動後が当然同じですね。

hanoi(X, [], X) :- hanoi_set(X).

それ以外の場合は、先頭の移動をやった後次の移動を実行です。
リストのリストという場合を検証しなくてよいように、リストに入れられる値を制限します。

% handを定義
hand(left_to_middle).
hand(left_to_right).
hand(middle_to_left).
hand(middle_to_right).
hand(right_to_left).
hand(right_to_middle).

hanoi(X, M, Y) :-
  maplist(hand, M), % `M`に入るのはhandを満たす値のみ。
  [H|T] = M,
  hanoi(X, H, Z),
  hanoi(Z, T, Y).
  1. 移動方法がリストだった場合は先頭をH, 残りをTとします。
  2. Xに対し、Hという移動を実行したものをZとします。
  3. Yは、Zに対して残りのTの移動を全部実行したものです。
| ?- hanoi([[1, 2, 3], [], []], [left_to_middle, left_to_right], Y).

Y = [[3],[1],[2]] ? a

no
| ?- hanoi([[1, 2, 3], [], []], [left_to_middle, middle_to_right], Y).

Y = [[2,3],[],[1]] ? a

no

これで、ルールの実装は大体終わった気がします。

仕上げ

さて、ここで答えを求めたいので、prologさんの性格に合わせて少しだけ手間をかけてあげます。

prologさんはすぐに無限ループをしたがるので、回数制限を設けてあげます。
考え方が面倒ですが、N回以下なら試すとしたいのでN以下の数値のリストを作るrangeを作成しましょう。

range(Min, Max, List) :-
  integer(Min),
  integer(Max),
  Min < Max,
  List = [Min|T],
  N is Min + 1,
  range(N, Max, T).
range(X, X [X]).

isという演算子が初めて出てきましたが、まあ、意味的には予想通りです。
この場合N = Min + 1とは出来ない事に注意が必要です。

これでrange(0, 5, X).X = [0,1,2,3,4,5]が取得できます。

hanoi(X, M, N, Y) :- % 移動回数は最大N回まで
  range(0, N, R),
  hanoi(X, M, R, Y).
hanoi(X, M, [H|_], Y) :- % H回の移動で出来る組み合わせ
  length(M, H),
  hanoi(X, M, Y).
hanoi(X, M, [_|T], Y) :- 
  hanoi(X, M, T, Y).

完了です。
試してみましょう。

| ?- hanoi([[1], [], []], M, 1, [[], [], [1]]).

M = [left_to_right] ? a

(1 ms) no
| ?- hanoi([[1, 2], [], []], M, 1, [[], [], [1, 2]]).

(1 ms) no
| ?- hanoi([[1, 2], [], []], M, 2, [[], [], [1, 2]]).

no
| ?- hanoi([[1, 2], [], []], M, 3, [[], [], [1, 2]]).

M = [left_to_middle,left_to_right,middle_to_right] ? a

(2 ms) no
| ?- hanoi([[1, 2, 3], [], []], M, 7, [[], [], [1, 2, 3]]).

M = [left_to_right,left_to_middle,right_to_middle,left_to_right,middle_to_left,middle_to_righ,left_to_right] ? a

(1591 ms) no

こういう感じで結果がでます。

解き方を気にせず答えを求められるのが素晴らしいところですね。

ちなみに

このプログラムのままだと、円盤が4つ以上あるハノイの塔にチャレンジするとものすごく時間がかかります。
チューニング方は色々あるのですが、まあ、単純に可能性が無い奴はすぐエラーにしてしまうという方法で実行速度を上げることができます。

それは今回は扱わないと言うことで。

emerge app-emulation/dockerとカーネルの癒着問題

Dockerのインストールを試みるよ!!!

なんでDockerかと言いますと、Haskell環境がほしい!とか、LAMP環境がほしい!!とかいろいろあるものの、環境が汚れきって手に終えなくなることを経験してきたからなのです。

しかし、このDocker、なかなかに凶悪です。
インストールしようとするだけで、カーネルコンパイルオプション変えろとおっしゃられるのです!!!

emergeすると、以下のようなメッセージを表示してくるのです。 もちろん、左端の*はエラーを表す赤色です。

 *   CONFIG_NETFILTER_XT_MATCH_ADDRTYPE:         is not set when it should be.
 *   CONFIG_AUFS_FS: is required to be set if and only if aufs-sources are used
 *   CONFIG_DM_THIN_PROVISIONING:        is not set when it should be.
 * Please check to make sure these options are set correctly.

はじめのうちのエラー文はもう残してなくて申し訳ないのですが、まあ最初は○○が足りない!系で/usr/src/linux/.configにあるコメントアウトを外して=yみたいに変えてあげるだけで消えてなくなってくれたのですが、最後に残ったのは上のようなやつばかりなわけです。

そもそもCONFIG_NETFILTER_XT_MATCH_ADDRTYPEなんてないので、近そうなCONFIG_NETFILTER_XT_MATCH_で始まるやつをコメントアウトしてみたりするんだけど、 CONFIG_AUFS_FSなんかは近い奴すらなかったり、元から

# CONFIG_DM_THIN_PROVISIONING is not set

ってなってたりして、全然どう設定していいのかわからなかったりする。

でもまあ、とりあえず言われるがままに何度か足したり消したりしていたら、赤い丸印は出たままでもなぜかインストールできたっぽい。

不思議。

まだインストールしてから起動していないけれど、今日はここまで。

一応、記念に.configファイルを残しておいた。
.config(gist)

Windows 7上に入れたVirtualBoxを4.3.22にバージョンアップしたらssh接続が途切れる問題に遭遇?

今のところ原因不明。。。

Windows 7VirtualBoxを入れてその中のLinuxからsshでサーバーにつなぐ使い方をしていたんだけど、今日4.3.22にアップデートできますという通知が来たので軽い気持ちで上げてしまった。

別に何の問題もなく使えていたと思っていたら、なぜかよくsshが途中で切れる。

症状としては、最初の数分は普通に使えていても、突然何の入力も受け付けなく固まった状態になる。

で、数秒後に

Write failed: Broken pipe

と表示される。

このメッセージ自体は数分放置したときの自動切断などで表示されるものらしいけど、
今回は明らかに操作中に切れている。。。

ググってみたけど見つからず。

Changelog for VirtualBox
ネットワーク自体は初期設定(NAT)だし変な設定はしていないと思うんだけどなぁ。

結局Linux環境はGNOMEで妥協した。

まあタイトル通りなのですが、gnome-tweak-toolのフォントの倍率を1.4にしつつ、残りは書くツールごとの調整機能で倍率を調整しました。

OSはUbuntu GNOME 14.04

見た目が気に入ったかどうかで選んだので、細かいことは気にしません。

どうやらgnome-tweak-toolはデフォルトで入っているようですが、入ってなければaptで。

sudo apt-get install gnome-tweak-tool

こいつを起動して、フォントのところの倍率を設定。
これでお行儀の良いツールはすべて1.4倍になってくれるらしい。

しかし現実には膨らんではくれないツールがたくさんあるので、個々で頑張って対応します。

Firefox

はい。おっきくなるのはタイトルバーぐらいです。

ですが、どうやらコンフィグで対応しているようです。
アドレスバーにabout:configと入力、警告に恐れを抱きつつも、続行。
最上段に表示される検索バーにlayout.css.devPixelsPerPxと入力。
cssぐらいまで入れれば大体みつかります。

-1.0となっている値をプラスの値に設定すればその倍率になるみたいなので、ここにも1.4を入力します。
ちなみにこの値、Firefox Syncを使っていても他のPCには影響しないようです。安心ですね。

Intellij IDEA

これが一番の問題。普段使う時間が長くなる予定なのに、全然拡大されない。 IDEA

文字の小ささがつらいですね。

File -> Settings -> Appearance & Behavior -> Appearanceにある、Overrides default font by (not recommended)を変更して、おっきくしてしまいました。
not recommendedだけどね。。。 あとは同じくSettingsにあるEditorのあたりのフォントサイズを手当たり次第大きく。

スプラッシュの大きさは変わりませんが、まあ使う上では問題ないので妥協です。 やっと環境ができて良かったです。

次からはなんかプログラミングっぽいこと書きたいですね。

VMwareにUbuntuを入れると画面がすごく小さいので解像度を下げたい

なんとかしたい

問題点

UbuntuVMware上に入れたら、元のモニターが1960x1080の11.6インチだったために、96dpi?ではすごく文字が小さくなってしまった。

解決への挑戦

システム設定から行けないかな?とチャレンジ

システムの設定から軽々変えられちゃうだろ?と思い、システム設定を開いてみる。
ところが、システム設定にはほとんど項目がない。なんで?昔はもっとあった気がする。 システム設定

Unity Tweak Tool

Ubuntuなので、Unity Tweak Toolってやつを入れて[Fonts]にあるテキスト倍率をいじってみる。

おっきくなった!!

と思いきや、残念ながらタイトルバーぐらいしか変わらないみたい。
というか、個人的にすごく肝心なWebブラウザのコンテンツのサイズが変わらない。

一時しのぎ的にFirefoxにNoSquintというツールを入れてコンテンツの倍率は120%にしてみたけど、 これはアドレスバーのサイズは変わらないみたいだ。 Firefoxのアドレスバーのサイズが

なんとか騙し騙し使えはするけど、やはりこのあといろいろなツールをインストールしていくことを考えると、OSそのもののdpiを144とかにしたいなぁ。

どうすればいいんだろう?

2015-2-4 追記 システム設定がほとんど項目がないのは、unity-control-centerが入っていないのでは?というコメントを頂けたので、apt-get unity-control-centerしてみる。 そしたら最初に消したibusが再インストールされ、システム設定が増えた。 システム設定フル!! ありがとうございます。ありがとうございます。

ここからディスプレイを選び、解像度の変更を試みてみた結果がこれ。 解像度を小さく うん、外枠ができただけで文字サイズは変わらないんだね。。。

ちなみに、設定からディスプレイの項目を一度でもいじると再起動時にモニターの設定がおかしいというエラーが出るようです。

保存したモニターの設定を適用できませんでした。

これはググった結果、~/.config/moniter.xmlを消せばいいみたい。

ただ、ディスプレイの項目にあるメニューとタイトルバーの拡大縮小をいじった結果、firefoxのアドレスバーは大きくなったっぽい。
相変わらず、Web表示部分のサイズは変わらずなので意味が全くない(拡大縮小機能をもってない別のツールには使えない)けど。 中身の文字も大きくなってほしい

emerge -uDN @world したらstartx後のキーボードレイアウトが英語になった。

ふしぎ。

このままでは困るので、何とかして治そうと試みます。
幸い、レスポンスをいただけたので、これをヒントに治しましょう。

Localization: HowToのページを見ると、新しいファイルを作って設定を書けばいいようです。

Section "InputClass"
    Identifier "keyboard-all"
    Driver "evdev"
    Option "XkbLayout" "jp"
    Option "XkbModel" "jp106"
    MatchIsKeyboard "on"
EndSection

これだけ書かれたファイルを作って、後はXを再起動です。

始めはXkbLayoutjp106って書いてうまくいかなかったけれど、これで無事キーボードが治りました。

@kjm さんありがとうございます。