2010年1月31日日曜日

Haskell のWriter モナド (1) – Control.Monad.Writer を使う前に

1. 最終的な結果だけでなく、中間結果も出力したい

例えば、次のような簡単な計算を行わせた場合、

(1 + 2) * 3 - 4

答えは `5’  となる。

関数 (演算) が適用される、それぞれの過程の結果を考えると、

1 + 2 => 3

3 * 3 => 9

9 – 4 => 5

となる。

このように最終的な結果だけではなく、計算途中の結果も表示したい場合には、どうすればいいのだろう?

 

2. let 式で中間結果を束縛

まず、考えられる方法は、それぞれの中間結果を、後で参照できるように let 式内で変数に束縛。結果をコンマ区切りで表示するなら、

main = do let a = 1 + 2
              b = a * 3
              c = b - 4
          print $ show a ++ "," ++ show b ++ "," ++ show c ++ ","

結果は、以下のようになる。

"3,9,5,"

 

3. 結果の型を (値, 出力する文字列) に変更

次に、計算の方法を少し変えてみる。

先ほどは、計算過程の結果を「文字列」で返したが、今度は

  • 計算の結果
  • 出力する文字列

の 2 つの部分に分ける。つまり、各計算の結果をタプルで返すように変更。

計算 => (値, 出力する文字列)

main = let (a, log1) = (x, show x) where x = (1 + 2)
           (b, log2) = (y, show y) where y = (a * 3)
           (c, log3) = (z, show z) where z = (b - 4) 
       in  print (c, log1 ++ "," ++ log2 ++ "," ++ log3 ++ ",")

これを実行すると、

(5,"3,9,5,")

ここで出力関数 out を定義する。

out x = (x, show x)

上記を出力関数 out で置き換えると、

main = let (a, log1) = out (1 + 2)
           (b, log2) = out (a * 3)
           (c, log3) = out (b - 4) 
       in  print (c, log1 ++ "," ++ log2 ++ "," ++ log3 ++ ",")

 

4. 式の参照関係に表われるパターン

ここで、let 式内の計算の参照関係について注目する。

下図のように、「上の計算の結果」を「下の計算」が順々に参照していることがわかる。( let 式なので、式の並び順は本質的に意味はない。)

img01-31-2010[1].png

この参照関係は、State モナドについて考えたとき と同じパターン。

  1. 全体の「計算の連なり」の中において、2 つの式の参照関係のみに注目。
  2. 2 つの間の「連なり方」を考えると、式の「つなげ方」を共通の部品として取り出すことができる。
  3. 部品として取り出した「つなぎ」を利用して、全体の連なりを再構成する。

 

5. 2 つの式の「つなぎ方」を定義

では、2 つの式をつなげる関数名を comb と名付け考える。

comb 関数に与える引数を m, n とし、n が m の結果を参照すると考える。先ほどの例では、let 式において out 関数の結果を束縛する部分に相当する。

comb m n = let (a, log1) = m

計算 m の結果をタプル (a, log1) に束縛。 a が「計算の結果」で、変数 log1 は「出力する文字列」に相当。

下図は、「つなぎ」の定義と、先ほどの具体例を比較したところ。

img01-31-2010[2].png

次に、上記の計算結果 a を参照する計算 n について考える。

変数 a を与えられ、計算 n の結果を、タプル (b, log2) に束縛すればいいので、

comb m n = let (a, log1) = m
               (b, log2) = n a

変数 b は、関数適用 n a の「計算の結果」で、log2 は「出力する文字列」に相当。

下図は、「つなぎ」の定義と、先ほどの具体例を比較したところ。

img01-31-2010[4].png

最後に、計算結果 b と、最初の計算 m による出力 log1 と、計算 n による出力 log2 を結合した結果をタプルにつめて返す。

comb m n = let (a, log1) = m
               (b, log2) = n a
           in  (b, log1 ++ log2)

下図は、「つなぎ」の定義と、先ほどの具体例を比較したところ。

img01-31-2010[5].png

具体例では、「つなぎ」を考えずに 3 つの計算を一気に行っている。そのため、「つなぎ」の定義と変数名が異なるが、「つなぎ」は、最終的な結果 c と中間結果として出力したものを結合し、タプルに入れて返すことに相当する。

 

6. 「つなぎ」を使ってみる

上記の comb 関数を使い、先ほどの計算を再定義してみる。

ただし、out 関数においてコンマを出力するように変更した。

comb m n = let (a, log1) = m
               (b, log2) = n a
           in  (b, log1 ++ log2)

out x = (x, show x ++ ",")

main = print $ out (1 + 2)  `comb` \a ->
               out (a * 3)  `comb` \b ->
               out (b - 4)
「つなぎ」を使うメリット

comb 関数を使うことにより、各々の計算の参照関係に対して、気を使う必要がなくなった。どの式が、どの結果に依存するかについて、明示する必要がない。なぜなら、参照関係を comb 関数のつなぎ方に定義してあるため。

ところで、main 関数の定義において、括弧を省略している。これは、無名関数は「できるだけ右へ拡張される」ように解析されるから。

 

7. 前の結果に関心のない「つなぎ」

ここで、一連の本質的な計算とは関わりのない、文字列を出力をするための関数 outStr を定義してみる。

outStr x = ((), x)

本質的な計算の内容に関わらないので、返されるタプルの第1要素は () とする。

この関数を使い、次の計算を実行すると、

main = print $ outStr "begin: " `comb` \_ ->
               out (1 + 2)      `comb` \a ->
               out (a * 3)      `comb` \b ->
               out (b - 4)      `comb` \_->
               outStr " :end"

結果は、以下の通り。

((),"begin: 3,9,5, :end")

あらら… (@_@; 肝心の計算結果であるタプルの第1要素が () になってしまった。 この点は後で修正する。

先に comb 関数を使い、outStr 関数のような本質的な計算に関わらない関数を「つなげる」関数を定義しおく。 (Monad の >> に相当)

comb_ m n = m `comb` \x -> n

comb_ 関数により、上記を書き換えることができる。

main = print $ outStr "begin: " `comb_`
               out (1 + 2)      `comb` \a ->
               out (a * 3)      `comb` \b ->
               out (b - 4)      `comb_`
               outStr " :end"

 

8. 「計算の対象」と「出力する内容」を分離する

上記の「最後で返される値のタプルの第1要素が () となってしまう」問題に対処するために、計算の最後に結果を出力する関数を追加する。

main = print $ outStr "begin: " `comb_`
               out (1 + 2)      `comb` \a ->
               out (a * 3)      `comb` \b ->
               out (b - 4)      `comb` \c ->
               outStr " :end"   `comb_`
               out c

実行すると結果は、

(5,"begin: 3,9,5, :end5,")

計算結果は返ってきたけれど、最後に余分な文字列が出力されてしまった。 (+_+)

これは、out 関数において、次の二つの処理が混在していることが原因。

  1. 本質的な計算の結果を返す
  2. 出力する内容を返す
out x = (x, show x)

また、「本質的な計算の過程の一部を出力したいくない」場合にも対応できない。

例えば、最初の 1+2 の結果を出力せずに計算を進めることは、現在の定義ではできない。 なぜなら、out 関数を使って計算を進め、同時に結果を出力しているため。

この問題を解決するために、out 関数を変更する。これまでは出力する内容と伴に結果を返していたが、出力に特化する

out x = ((), show x ++ ",")

次に、出力は空であるが、結果を返すことに特化した関数を定義する。 (Monad の return に相当)

ret x = (x, "")

この二つの関数を使い、先ほどの関数を定義しなおすと、

main = print $ outStr "begin: " `comb_`
               ret (1 + 2)      `comb` \a -> 
               out a            `comb_`
               ret (a * 3)      `comb` \b ->
               out b            `comb_` 
               ret (b - 4)      `comb` \c ->
               out c            `comb_`
               outStr " :end"   `comb_`
               ret c

全体のソースコードはこちら

 

9. 命令型言語との比較

上記の定義は `comb` に目をつむれば、Python で定義したときとよく似ている。

def main():
    a = 1 + 2
    print a
    b = a * 3
    print b
    c = b - 4
    print c
    return c

print main()

Python で書いた場合と比較すると、

「前に定義した変数を参照する」

ことは、Haskell において

「内側の関数 (無名関数) の中から外側の引数を参照する」

ことに相当することがわかる。

 

参考文献

2010年1月28日木曜日

簡単中華風おにぎりの作り方

朝、おにぎりを作ろうと思ったら、昔買った

花椒塩  (ホワジャオイエン)

が残ってたので、普通の塩の代わりに手の平に塗ってニギニギ。

ホワジャオイエン【花椒塩】の意味 国語辞典 - goo辞書 によると、

中国の調味料の一。塩と山椒(さんしよう)の実をいっしょに煎(い)り、細かく挽(ひ)いたもの。揚げ物などに添える。

山椒好きにはたまらない調味料。 ^^ ちょっと舌がビリっとする。

普通の塩を少量混ぜた方が味に締まりがでたかも。こうやって他の調味料でも適当に手の平に塗れば、それ風のおにぎりのできあがり。

2010年1月24日日曜日

Meadow の dired でよく使うキー操作

良く使う基本操作

Meadow で必ずお世話になるのは dired 。

M-x dired

で起動し、表示したいディレクトリを指定。

 

日付で並び換え

ディレクトリが開かれたら、まずは日付で並び換え

s

の後、目的のファイルを探す。

C-s または C-r でインクリメンタルに目的の場所へ移動し Enter 。

 

ヘルプ

操作方法を忘れたら、

?

これだけは忘れられない。

 

すぐに忘れる表示の更新 g

dired でディレクトリを開いた後、新たに作成したファイルは表示されない。はじめこの仕様に戸惑った。 (+_+) 表示を更新するには GNU Emacsマニュアル: ディレクトリエディタdired によると、

g

これすぐに忘れる。 (+_+) なんで `g’ なのかな?更新のイメージとキーを結びつけられない。

 

ディレクトリの作成も dired で +

Meadow/Emacs memo: ディレクトリ表示 ― dired など の基本的な操作を見たら、

+

でディレクトリを作成できたことにはじめて気がついた。いつも make-directory で作ってた。 ^^;

 

Explorer との連携をするための設定

いつか設定しようと思っていたエクスプローラとの連携。重い腰をあげ、Meadow/Emacs memo: ディレクトリ表示 ― dired など の「エクスプローラを起動する設定」を .emacs に追加。

これで dired で

E

により dired で開いていたディレクトリがエクスプローラで表示される。

編集中のファイルを開いている状態でも M-x explorer でそのファイルがあるディレクトリが開かれる。

快適~ ^^

2010年1月23日土曜日

Access の VBA でスタックトレースを表示

メニューより 「表示 > ツールバー > デバッグ」にチェックを入れる。

img01-21-2010[7].png

VBA のコードを実行する前にブレークポイントを設置。実行して停止したところで、ツールバーの「呼び出し履歴」のアイコンをクリック。

img01-21-2010[8].png

ショートカットキーは、Ctrl + L 。表示メニューにある。

Access の VBA で複数行をコメントアウト

メニューより 「表示 > ツールバー > 編集」にチェックを入れる。

img01-21-2010[7] - コピー.png

コードを複数行選択し、ツールバーのコメントブロックのアイコンをクリック。

img01-21-2010[9].png

ウィンドウを前面で固定 - 「最前面でポーズ」

1. 「貼り付け」の代わりに「最前面でポーズ」を使う

Windows xp では、特定のウィンドウを前面に固定するためのユーティリティとして「貼り付け」を利用していた。Vista でも愛用しようと思っていたけれど、なぜか自分の環境では起動してくれなかったり、動作がぎこちなかったりと残念ながら使用を諦めざる終えず。。 (+_+)

「貼り付け」の代わりに、「最前面でポーズ」 を使うことにした。とにかくシンプルで、操作が Pause キー 一発でウィンドウを前面に固定というのがいい。

 

2. 表示されるダイアログの OK ボタンを押す必要はない

前面に固定したいウィンドウにマウスのポインタを移動させ、Pause キーを押すと

img01-22-2010[2].png

というダイアログが表示される。この OK ボタンを押す必要はなく、1, 2 秒待っていると自動的に消える。

当初、OK ボタンを押さなければならないと勘違いして、

  1. 右手の薬指で Pause キーを押し、
  2. キーボードを目視せずに親指で Enter キーを押す

練習をしてしまった。 ^^;

「貼り付け」のように、ウィンドウをピンで止めるといギミックがないので、どれが前面にあるウィンドウなのか一目ではわからない。しかし、それほど多用するわけではないので、これで十分。

 

3. その他

追記(2012.3.26): 現在は、Dexpot の機能を使い、ウィンドウを前面に固定している。

2010年1月14日木曜日

被演算子の一つを返す AND, OR - デフォルト値の設定

if と真偽値 の続き。

 

Python のブール演算における例外的なこと

前回見たように、Python では if 文においてたくさんの値が `偽’ と見なされる。ただし、 ドキュメント の最後には次のように書かれている。

ブール値の結果を返す演算および組み込み関数は、特に注釈のない限り常に偽値として 0 またはFalse を返し、真値として 1 または True を返します (重要な例外: ブール演算 "or" および "and" は常に被演算子の中の一つを返します)。

(2.3.1 真値テスト より、太字は引用者による。もしくは 6. Built-in Types — Python v2.6.4 documentation を参照)

例えば、組み込み関数である all, any なら、

l = range(10,100)
print all(map(lambda x: x >= 10, l)) #=> True
print any(map(lambda x: x < 10, l))  #=> False

というようにブール値を返す。では、「重要な例外」と書かれている or, and は、どういう意味で重要なのだろう?なぜ素直にブール値を返さないのか?

ちなみにこれが Java なら、

条件AND式の型は,常に boolean とする。…

条件OR式の型は,常に boolean とする。

(Java言語規定 式 より)

のように必ずブール値を返す。

 

if 文における and, or

偽 と解釈される値の場合

普通、and 演算子は次のように if  と組み合わせて使用される。

x = "hoge"; y = "piyo"

if x != "" and y != "":
    print x, y

上記を実行すると、”hoge piyo” と出力。

ところで、Python の文字列は、if の検査において、空文字 でそれ以外は 真 として扱われた。よって、先ほどの if による空文字かどうかの検査の部分、

if x != "" and y != "":

を次のように書換えることができる。

if x and y:

この一見すると奇妙で意味を汲みにくい書き方を素直に読むなら、

もし x かつ y ならば…

となり、最初に書いた

もし x が空文字であり、かつ、 y も空文字であるならば…

に比べると曖昧に見える。

 

評価のされ方を確認

上記のように の値であることを想定して書いたときの、if 文と and, or 演算子における値の評価を確認してみることに。最初はちょっと奇妙に思えるが、以下のように文字列に and , or 演算子を適用。

print "hoge" and "piyo" and "fuga"  #=> “fuga”
print "hoge" or  "piyo" or  "fuga"  #=> “hoge”

最初見たドキュメントの通り、被演算子の中の一つが返されているのがわかる。このルールは、5.10 ブール演算 (boolean operation) によると、

x and y は、まず x を評価します; x が偽なら、x の値を返します; それ以外の場合には、 y の値を評価し、その結果を返します。

x or y は、まず x を評価します; x が真なら、x の値を返します; それ以外の場合には、 y の値を評価し、その結果を返します。

(andnot も、返す値を 01 に制限するのではなく、最後に評価した引数の値を返すので注意してください。

(太字は引用者による)

これに従うと、

if x and y:

の例では、次のような評価の流れとなる。

  1. and 演算の評価
    • x は空文字でないので真
    • y は空文字でないので真
    • y の値を返す
  2. if の評価
    • and 演算子の結果は空文字でないので真

 

JavaScript で || によってデフォルト値を設定する方法

4873113911ところで、Python に負けず劣らず と解釈される値が多い JavaScript では、Python の or に相当するのは ||JavaScript: The Good Parts (pp.24-25) には、この || 演算子を使った「デフォルト値」を設定する方法が書かれている。

例えば、`名前と年齢’ を対にしたハッシュを定義し、ハッシュの要素ではないものを参照しようとしたとする。

var persons = {"Tarou" : 10, "Jirou" : 20, "Hanako" : 30};
var age = persons["Saburou"] || 0;

|| 0 と書くことにより、 age には 0 が代入される。先ほどの Python の or と同じルールを思い出しながら評価の流れを考えると、

  1. persons[“Saburou”] に相当するものがないので undefined が返される。
  2. undefined は 偽 と見なされる。
  3. || 演算子のルールに従い、|| の右辺の 0 が評価され、0 が返される。

 

Python でも or でデフォルト値を設定

Python でも同じようにして、デフォルト値を設定できる。

a = 0
print a or 100 #=> 100

ん~ (@_@; 一見意味がわかりずらい。。

このようにデフォルト値の設定を or 演算子でできるのは、

  1. 偽 と見なされる値がいくつかあり、
  2. or の結果がブール値ではなく評価した値そのものを返している

ことによる。

 

ところで、どうしても短く書きたいなら、条件式 を使った方が意図が明確になる。
print a if a else 100

いや、正直言うとこの書き方も好きではない。

print a if a != 0 else 100

頭が堅いのかな?こうでないと今一安心できない ^^;

 

Ruby でも || でデフォルト値を設定

Ruby の場合は、nil が 偽 と見なされるので、次のように書ける。

a = nil
p a || 100 #=> 100

しかし、Python や JavaScript の癖が抜けずに、

a = 0
p a || 100 #=> 0

と書くと、意図したものと違う結果になるので注意。

 

ある一つの機能は、一つのことだけのために限ってほしい

結局、言語によって 偽 と見なす値が異なることと、and , or のような演算子がブール値でなく、評価した値のいずれかを返すという仕様が、自分のようなすぐに忘れる脳みそにとってはどうしても好きになれない。 (+_+) そもそもブール演算に用いるものを、デフォルト値の設定に使うという発想が自分はダメ。道具は用途を限り、それにのみ使えるようにしておかないと、脳みその容量が少ないので混乱してしまう。 一時慣れたとしても、しばらく使ってないと忘れる自信ならたっぷりある。(o_ _)o~†

加えて、演算子が場合に応じて異なる型の値を返すということも居心地が悪い。 (+_+)  「返すならブール型だけで勘弁してください」と言いたい気分。異なる値を返すなら、特定のインターフェイスを実装している型、もしくは、Haskell で言うなら同じ型クラスのインスタンスのみに限定してほしい。以前、少しの手間を惜しみ、ある関数で異る型の値を返すようにしたことがある。一時的の`つもり’で書いたのだけれど、それを忘れ、後になって見つけづらいバグになってしまった。 もちろん、「ちゃんとテストコード書いておけよ」という話なんだけれど。

とにかく、ポカミスを連発し、ミスをしてないという思い込み、エラーの発見が苦手な自分のようなタイプにとって、例外的なことは本能的に避けたくなる。多分、そうでない人にとっては、便利さとのトレードオフが考慮された例外的な事柄は便利だと感じるのだろうなぁ。

 

追記 (2010.1.16) : Google Python スタイルガイド では、True/False の評価について次のように書かれている。

可能な場合は、非明示的な false を利用する。…

利点:

Python のブール値を使うと、可読性が高まり、エラーを防ぐことができます。さらにほとんどの場合で、これは高速です。

うーん、可読性高まるのかなぁ~ (@_@; 

 

Haskell で似た関数を定義する

Haskell で同じようにデフォルト値を設定しようと、次のように書くと、

a = 0
b = a || 100

|| が適用する型が一致しないので、当然エラーとなる。どうしても上記のようなデフォルト値を設定する演算子が使いたいのなら、|| とは別に定義する。

デフォルト値を設定する演算子を ||| で定義するなら、

(|||) x d = if x == 0 then d else x

となる。これを使って、

a = 0
b = a ||| 100  -- b は 100

ただし、これでは数値にしか使えない。

 

JavaScript の or に相当する ||| 関数

前回、Haskell で Python のように型ごとに if の解釈を変えるためにオーバーロード を利用した。コードは以下の通り。

class Boolean b where
    isTrue :: b -> Bool

instance Boolean Bool where
    isTrue True  = True
    isTrue False = False

instance Boolean Integer where
    isTrue 0 = False
    isTrue _ = True

instance Boolean [a] where
    isTrue [] = False
    isTrue _  = True

if' x e1 e2 = if isTrue x then e1 else e2

それぞれの型でオーバーロードした if’ 関数は 型クラス Boolean のインスタンスに適用できる。これを元に if’ を定義したときと同じように考える。

(|||) e1 e2 = if isTrue e1 then e1 else e2

… あれ? ||| と if’ の定義は形がよく似ている。こんな冗長なコードを書いていたらだめか。。

if’ を使って書き直すと、

(|||) e1 e2 = if' e1 e1 e2

引数を省略して以下のように書けるけれど、これだと意図がわかりずらいかな。 (@_@;

(|||) e1 = if' e1 e1

試してみる。

*Main> 0 ||| 100
100
*Main> 1 ||| 100
1
*Main> [] ||| [1,2,3]
[1,2,3]
*Main> [1,2] ||| [1,2,3]
[1,2]
*Main> "" ||| "hoge"
"hoge"
*Main> "piyo" ||| "piyo"
"piyo"

 

ついでに Python の and に相当する &&& も定義する。

(&&&) e1 e2 = if' e1 e2 e1

if’ , &&&, ||| を組み合わせて試すと、

*Main> if' ("hoge" &&& "piyo" &&& "fuga") 1 0
1
*Main> if' ("hoge" &&& "piyo" &&& "" &&& "fuga") 1 0
0
*Main> if' (1 &&& 2 &&& 3) 1 0
1
*Main> if' (1 &&& 2 &&& 0 &&& 3) 1 0
0
*Main> if' ("hoge" ||| "" ||| "piyo") 1 0
1
*Main> if' ("" ||| "") 1 0
0
*Main> if' (1 ||| 0 ||| 2) 1 0
1
*Main> if' (0 ||| 0) 1 0
0

全体のコードはこちら

 

Ruby でも同様に

Haskell では if を関数として定義し、型ごとにオーバーロードし、and, or に相当する関数を定義した。同じように Python でもできないかと考えたが、Python は組込クラスに手を出せない。 (+_+) それに対して、Ruby は組込クラスにメソッドを追加することができる。 (cf. リストと文字列を透過的に扱うには ) そこで、Ruby で同じように書けるか試してみることに。

Ruby では偽と解釈される値は false 以外に nil だけだった。これを Python と同じように Array, String, Integer でもそれぞれ偽と解釈される値を想定し、 if をメソッドとし実装。それに伴い and, or メソッドも追加し、or メソッドでデフォルトの値を設定できるようにする。

作成するモジュールとクラスは以下の通り。

img01-13-2010[1].png

Control モジュールで if メソッドを定義。これを使って、Bool モジュールで and, or メソッドを実装。イテレータを実装するときのようにテンプレートメソッドパターンを使って、各々のクラスで値による真偽のルールを記述する。

 

実装

まずは Control と Bool モジュールから。 Control モジュールの if メソッドは、真の値のときに評価されるブロック e1 と、偽の値のときに評価されるブロック e2 を受けとり、if メソッドが呼出されたオブジェクトの値に応じて e1 もしくは e2 を返す。 if_ メソッドは、その場で評価に応じてブロックを実行するようにした。 isTrue? と呼出しているメソッドは、このモジュールをインクルードする組込クラスで定義する。

Bool モジュールは Control モジュールで定義した if メソッドを使って and, or メソッドを定義。

module Control
  def if(e1, e2)
     if self.isTrue? then e1 else e2 end
  end
  def if_(e1, e2)
    self.if(e1, e2).call
  end  
end

module Bool
  include Control
  def and(other)
    self.if(other, self)
  end
  def or(other)
    self.if(self, other)
  end
end

後は、組込クラスで isTrue? メソッドを実装し、真偽と解釈される値の範囲を設定。

class TrueClass
  include Bool
  def isTrue?
    if self == true then true else false end
  end
end

class FalseClass
  include Bool
  def isTrue?
    if self != false then true else false end
  end
end

class NilClass
  include Bool
  def isTrue?
    if self != nil then true else false end
  end
end

class String
  include Bool
  def isTrue?
    if self != "" then true else false end
  end
end

class Integer
  include Bool
  def isTrue?
    if self != 0 then true else false end
  end
end

class Array
  include Bool
  def isTrue?
    if not self.empty? then true else false end
  end
end

まずは if と if_  メソッドを試してみる。変数 x, y に false, nil, "", 0, [] に入れて同じ動作をするか確認。if メソッドにおいて、オブジェクトの値によって設定した真偽が決定し、実行するブロックが決まる。

x = 0
y = 1

x.if(proc{ p "T" },
     proc{ p "F"}).call

x.if_(proc{ 
        p "T" 
        y.if_(proc{ p "TT" }, 
              proc{ p "FF" })
      },
      proc{ 
        p "F"
        y.if_(proc{ p "FT"}, 
              proc{ p "FF"})
      })

次に and, or を試す。

p true.and true                 #=> true          p 1.and 2.and 3                 #=> 3
p "hoge".and "piyo".and "fuga"  #=> “fuga”

p "hoge".or "piyo".or "fuga"    #=> “hoge”
p "".or "hoge"                  #=> “hoge”

ダックタイピングな Ruby なので、こんなのも書けてしまうか。(+_+)

p false.or 0.or "".or [].or 1000

もう何がなんだかわからなくなってきた。まぁ、これが書けたからどうというわけでもなく、あくまでもこんな風にも記述できるという実験ということで。 ^^;

コード全体はこちら

 

Scheme の and , or はどうなの?

そういえば、Scheme でも見かけは同じような動作をする。

(and 1 2 3)                  ;=> 3
(and "hoge" "piyo" "fuga")   ;=> “fuga”
(or 1 2 3)                   ;=> 1
(or "hoge" "piyo" "fuga")    ;=> “hoge”

Structure and Interpretation of Computer Programs によると、

  • (and <e1> ... <en>)

    The interpreter evaluates the expressions <e> one at a time, in left-to-right order. If any <e> evaluates to false, the value of the and expression is false, and the rest of the <e>'s are not evaluated. If all <e>'s evaluate to true values, the value of the and expression is the value of the last one.

  • (or <e1> ... <en>)

    The interpreter evaluates the expressions <e> one at a time, in left-to-right order. If any <e> evaluates to a true value, that value is returned as the value of the or expression, and the rest of the <e>'s are not evaluated. If all <e>'s evaluate to false, the value of the or expression is false.

    (太字は引用者による)

  • こういった評価の方法をするものを special form と言うらしいが、

    Each special form has its own evaluation rule. …

    the evaluation rule for expressions can be described by a simple general rule together with specialized rules for a small number of special forms. …

    Special syntactic forms that are simply convenient alternative surface structures for things that can be written in more uniform ways are sometimes called syntactic sugar,

    (Structure and Interpretation of Computer Programs より)

    Scheme 入門 15. 構文の定義 によると、

    マクロとは式の変換です。

    or, and はマクロで、以下のように再帰的に定義されています。マクロ定義も再帰的に定義できるので、かなり複雑な構文を定義することができます。

    Scheme はまだ全然わからないので、これは保留。ただし、式の評価方法という観点から、他の言語とは違うメタレベルを導入して定義してあるような気が。「ブール型だからこうだよ」というように型の視点からではなく。

     

    参考

    2010年1月11日月曜日

    Access で参照整合性を設定したフィールドを含むレコードの入力をキャンセルする

    参照整合性の設定

    例えば、家計簿のようなものを作ろうと思い、以下のようなリレーションシップを持ったテーブルを作成した。ここでは、使った金額の 「支払い」 において、誰がどこへ支払い、その明細を管理したいもとする。 (抜粋)

    CropperCapture2010[1].png

    「支払」 テーブルの 「支払先ID」 フィールドは 「組織」 テーブルの ID を外部キーとして持ち、「支払人ID」 は 「支払人」 テーブルの ID を外部キーとして持ち、参照整合性を設定。

     

    フォームの作成

    次に、「支払」 テーブルを元にフォームを作成し、動作の確認をする。

    CropperCapture2010[2].png

    ここで、「支払先」 または 「支払人」 に値を入力をした後、フォームのデザインに戻りたいとする。もし、「支払先」 と 「支払人」 フィールドに参照整合性制約に抵触しない値を入力した場合、その値がデータベースに保存された後、フォームデザインの画面に切り替わる。しかし、参照整合性制約に抵触した値を入力した場合、フォームデザインに戻ろうと思っても、

    バリアント型でない変数に Null 値を代入しようとしました。

    とエラーダイアログが表示される。入力を取消したいと思い、入力した値を BackSpace キー消しても同じダイアログが表示。

     

    Esc キーで入力のキャンセル

    メニュー下の「レコードの削除」ボタンを押しても、全てのフィールドに正当な値が入力されてないとダメ。仕方がないので、とりあえず正当な値を入力しておき、その後レコードの削除するということで対応できる。しかし、これでは面倒。 (+_+)

    [ACC97]: Access データベース内の関連テーブル内のレコードを編集します。 によると、

    注: 入力ミスを修正、BackSpace キーを押します。 現在のフィールドに加えた変更をキャンセルするには、Esc キーを押します。 レコード全体に加えた変更をキャンセル、Esc キーを押します

    (太字は引用者による)

    あ~、Esc キーだったのか。基本的な操作すらわかってないな。。 ^^;

    画面の拡大に「快速ズーム」

    Magic Mouse のズーム機能が欲しい

    B002TOJHB8老眼ではないけれど、入力フィールドの文字ポイントが小さいとストレスがたまる。 (+_+) Ctrl + U のショートカットキーでシステム標準の拡大鏡の使い勝手も今一。

    下の動画で紹介されている Mac のズーム機能に似たものはないかな?

     

    QZoom

    Vector:QZoom「快速ズームソフト」 によると、

    QZoomとは、WindowsでMac OS Xのズーム機能を実現するためのソフトです。…

    もちろんマウススクロールでの拡大・縮小にも対応してます。

    おぉ~、これはいい ^^ 直観的で、素早い操作が一番。

     

    ショートカットキーの設定

    起動すると、タスクトレイに常駐。ここら設定画面を呼出し、キャンセル操作のショートカットキーだけ変更した。なぜなら、他のアプリで Esc キーに操作が割り当てられていた場合、QZoom の設定が優先的に扱われてしまったため。

     

    Vista で画面がチラつくとき

    Windows Vista において、Skype でビデオチャットをしているときに管理者権限が必要な操作をすると、なぜか Aero 機能が無効になる。このとき、QZoom を利用すると画面がチラついて操作がもたつく。この場合は Skype を一度終了させると Aero 機能が再度有効になるので、その後に Qzoom を起動しなおす。これは他の拡大するためのアプリでも同様だった。

    2010年1月7日木曜日

    if と真偽値

    Java の if 文での検査は真偽値のみ

    最初に馴染んだ言語が Java だったので、「if 文での検査で使えるのは当然 true または false でしょ」という感覚が染み込んでいる。例えば、Java で

    if (0){
     ...
    }

    をコンパイルしようとすると、

    Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - 互換性のない型
      期待値: boolean
      検出値:    int

    というエラーが表示される。

    Java言語規定 ブロック及び文 の 「14.8 if 文」によると、

    if ( Expression ) Statement

    … 式(Expression) は,論理型をもたなければならない。そうでなければ,コンパイル時エラーが発生する。

    (太字は引用者による)

    という仕様であるため。

     

    C 言語 の if 文での検査は 0 が 偽

    しかし、C 言語では上記のコードでもコンパイルが通る。なぜなら、初級C言語Q&A(5) によると、

    Q 【真偽値】

    他の言語には真偽の値を表現するための型が用意されているものがあるが、なぜC言語には用意されていないのか。

    … 最も大きな理由は、それがなくても実用上差し支えないからでしょう。実際、1という値を真、0という値を偽であることにすれば、整数型を使って真偽を表現することが可能ですから。

    この `0’  を偽 と見なすのは最初違和感があった。。 (@_@;

    数値の比較をしてみると、

    1 == 1

    で 1 が返り、

    1 < 1

    で 0 が返る。

    この仕様だと、自分でブール型を用意しなければ、真偽値を返すことを意図した関数と、数値を返す関数をコンパイラが区別できない。 (+_+)

    ただし、プログラミング言語 C の新機能 によると C99 で、

    今度の C 言語では新しい整数型 _Bool 型を導入することでその問題を解決します。この型は 0 と 1 が入れば十分な大きさとされており、必ずしも int 型と同じサイズであるとは限りません。

    となり、型的にはスッキリとする。

     

    Python, Ruby の真偽テスト

    Python はたくさんの値が 偽 と解釈される

    Python は 2.3.1 真値テスト で述べられているように、たくさんの型の値が条件文で 偽 として扱われる。例えば、リストと文字列の上位にシーケンス型があり、空文字・空リスト

    ”” , []

    は 偽 。(これにより、if の検査で リストと文字列を透過的に扱う ことができる。)

    これを「真偽テスト」というのは言い過ぎでないの?ってくらい多い。 ^^; バグを誘発しそうで怖い。。

     

    Ruby は false に加えて nil のみ

    Ruby はこれに対して、false に加え nil が偽と判定されるのみ。 Python 比べたらスッキリしている。ただし、Java で

    if (null) {

    なんて書いたらエラーになるので、これもやや違和感を覚えた。

    しかし、Ruby で上記のような書き方は実際に多用されているのだろうか?いや、そもそも多用されていたら Null Object パターン に持ちこむか。 JavaScript では DOM の操作でよく見るけれど。

     

    JavaScript の falsy

    4873113911JavaScript の場合は、

    以下に、条件式が falsy, つまり偽と評価される値を示す。

    • false
    • null
    • undefined
    • 空文字
    • 数値の 0
    • 数値の NaN

    (JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス, p.14 より)

    false ならぬ falsy という単語で述べられているように、Python に負けず劣らず 偽 と解釈される値が多い。 (@_@;

    (ちなみに、最初 JavaScript 見て嫌だなと思ったのがこの真偽テストに絡んでいる。「え?この if で何を確認してるの?」と、馴染まない内はイラつかされるオブジェクトの存否チェック、そしてネストネストでズラズラズラ…。あ~、どこに本質的な仕事が書かれているんだろう?とコードの森で迷い。。)

    総じて Script 系の言語は、さっと書くという利便性のために、「どこまでを if の検査で 偽 と見なすか」について設計思想が違うということかな。

     

    型とは

    4873112753話変わって、「型」とは何だっただろうか? 以前 にも引用したが 「データベース実践講義」 の 「2.3 型とは」 (p34) によると、

    型とはいったい何か。基本的には、値の名前付き有限集合である。…

    すべての型が、その型の値もしくは変数に作用する演算子の連想集合を持つ …

    とのこと。

    ある値の集合が型であり、その型に属する値に対して適用できる関数や操作が定義されるという見方。

     

    Java のプリミティブ型としての boolean

    Java に戻り、真偽値について確認してみると、

    boolean は,リテラル true 及び false (3.10.3) で示す二つの可能な値をもつ論理的な量を表す。

    (Java言語規定 型,値及び変数 の 4.2.5 boolean型 及び boolean値 より)

    値の集合が定義され、適用できる論理演算子が言語仕様として示されている。ただし、プリミティブ型である true, false が Java 言語で書ける枠内で、型に属する値として定義されているわけでない。 &&, || のような論理演算子についても同様。 Java を使う側は概念的イメージとして boolean 型を意識し、それに即してコードを書くとコンパイラが型をチェックしてくれる。

     

    C の真偽は数値

    前述の通り、C言語では C99 より前は真偽値に対応する型は用意されてなかった。真偽値として見なしたい値は数値なので、数値に対する演算子が適用できてしまう。 boolean 型として扱うとプログラマが意識したところで、コンパイラにとっては区別不能。

     

    Ruby の TrueClass , FalseClass

    Ruby では、

    Ruby の面白いのは、Boolean というクラスは無く、 true と false はそれぞれ TrueClass と FalseClass というクラスのインスタンスである。

    (「各言語におけるtrue/falseまとめ - 床のトルストイ、ゲイとするとのこと」 より)

    これ読むまで、てっきり Boolean というクラスがあると思っていた。 ^^;

    確認のため TrueClass のスーパークラスとインクルードしているモジュールを表示させてみると、

    p TrueClass.ancestors   #=> [TrueClass, Object, Kernel, BasicObject]

    となり、true と false だけをまとめる型は定義されていない。つまり、Ruby の言語の枠内でブール型というものが独立して明示されていない。だから、 and や or のようなものが、

    再定義できない演算子(制御構造)

    (演算子式 - Rubyリファレンスマニュアル より)

    として、何となく微妙な感じの位置付けになっているのかな?

     

    Python の真偽は数値のサブタイプ

    ついでなので Python についても調べたら、 3.2 標準型の階層

    ブール型 (boolean)

    … ブール型は整数のサブタイプで、ほとんどの演算コンテキストにおいてブール型値はそれぞれ 0 または 1 のように振舞います。ただし、文字列に変換されたときのみ、それぞれ文字列 "False" および "True" が返されます。

    うげぇ~ (@_@; 整数のサブタイプだったとは。。

    ということは、次の計算がエラーとならない。

    print True + True + False #=> 2
    print True * 10           #=> 10

    上記のような計算を成立させることの積極的な意味は何だろう???

    ともあれ、`0’ が if 文において 偽 と解釈される理由はこういう設計に依るのかも。どうしても ごちゃごちゃしているという感が否めない。 (+_+)

    PEP 285 -- Adding a bool type では次のように述べられている。、

    There's a small but vocal minority that would prefer to see
    "textbook" bools that don't support arithmetic operations at
    all, but most reviewers agree with me that bools should always
    allow arithmetic operations.

    6) Should bool inherit from int?

    => Yes.

    ということで、この設計はこのままずっと行くみたい。

     

    Haskell の Bool 型

    これに比べて Haskell はシンプル。 Data.Bool に、型とその値の集合が次のように定義されており、

    data Bool  = False  | True

    Bool 型に適用できる関数が示されている。

    (&&) :: Bool -> Bool -> Bool
    (||) :: Bool -> Bool -> Bool
    not  :: Bool -> Bool

    そして、条件式 である if – then – else の検査では Bool 型しか許されない。 case 式と同等なものとして以下のことが成り立つようになっている。

    if e 1 then e 2 else e 3 = case e 1 of { True -> e 2 ; False -> e 3 }

    ( The Haskell 98 Report: Expressions  3.6 Conditionals より)

    先ほどの 「型とは何か?」 という視点から見ると、なんてスッキリとしているんだろう。 ^^ 

    … とは言ったものの、最初は「何でこんなものが普通の関数と同じように定義されているの?」 と思ったけれど。 ^^;

     

    型ごとに if の解釈を変えたいならオーバーロードで

    もし、Python のように Bool 型に加え、空リスト、空文字、0 で False としたいなら、if-then-else とは異なる if’ 関数を定義し、それぞれの型でオーバーロードするのがいいかも。

    if を関数で定義するとは、表計算で、

    if (条件, 真の場合, 偽の場合)

    のように使うときのイメージで。 (cf. Data.Bool.HT の if’ 関数)

     

    まずは、値に適用したら Bool 型を返す isTrue 関数を持つ、型クラス Boolean を定義。

    class Boolean b where
        isTrue :: b -> Bool

    Bool, Integer, [a] 型を、型クラス Boolean のインスタンスにして、それぞれの型に応じた True , False を定義。

    instance Boolean Bool where
        isTrue True  = True
        isTrue False = False
    
    instance Boolean Integer where
        isTrue 0 = False
        isTrue _ = True
    
    instance Boolean [a] where
        isTrue [] = False
        isTrue _  = True

    これを使って if’ 関数を定義。 x の値が 真 と見なした値の場合 e1 が評価され、偽 の場合 e2 が評価される。

    if' x e1 e2 = if isTrue x then e1 else e2

     

    試しに使ってみる。数値の場合、

    *Main> if' 0 "hoge" "piyo"
    "piyo"
    *Main> if' 1 "hoge" "piyo"
    "hoge"

    リストの場合、

    *Main> if' [] "hoge" "piyo"
    "piyo"
    *Main> if' [1,2,3] "hoge" "piyo"
    "hoge"
    *Main> 

    リストで定義しているので、文字列の場合でもちゃんと動作する。

    *Main> if' "" "hoge" "piyo"
    "piyo"
    *Main> if' "test" "hoge" "piyo"
    "hoge"

     

    if’ を適用できる型を忘れてしまっても :i で調べられる

    上記の if’ 関数の型を調べると、

    *Main> :i if'
    if' :: (Boolean b) => b -> t -> t -> t

    もし、if’ 関数は何の型を 真・偽 と見なすか忘れてしまっても、 :i で 型クラス Boolean のインスタンスを表示できる。

    *Main> :i Boolean
    class Boolean b where t :: b -> Bool
    instance Boolean Bool
    instance Boolean Integer
    instance Boolean [a]

     

    if の解釈は所与のものであってほしくない

    上記のように if – then – else は ブール型の検査のみで、それ以外の値を 真・偽 と見なして評価したいなら別腹で定義する方が好み。これなら評価の方法を自分の好きなように任意の型で定義できる。

    Python でも同じように、クラスごとに真偽テストの挙動をカスタマイズできる。

    以下の値は偽であると見なされます …

    __nonzero__() または __len__() メソッドが定義されているようなユーザ定義クラスのインスタンスで、それらのメソッドが整数値ゼロまたは bool 値の False を返すとき

    (2.3.1 真値テスト より)

    (このメソッド名を見ても、あくまでも bool は int のサブタイプだぞ、覚えておけよ ! という気迫 を感じる。 ^^;)

    結局のところ、if で型ごとに 真偽 が異なることは、オーバーロードのことだと解釈できる。これを言語が所与のものしてしまうと、「一体それはどういう訳なんだろう?」と疑問がわき、頭の中がごちゃごちゃになり、すぐに忘れるこの脳みそ。 (+_+)

     

    Scheme

    最近ちょっといじりはじめた Scheme ではどうなっているか調べたら、Structure and Interpretation of Computer Programs によると、

    17 ``Interpreted as either true or false'' means this: In Scheme, there are two distinguished values that are denoted by the constants #t and #f . When the interpreter checks a predicate's value, it interprets #f as false. Any other value is treated as true. (Thus, providing #t is logically unnecessary, but it is convenient.)

    真偽は #t, #f の定数として定義され、#f のみが偽で、それ以外は真。 #t は実質的にはいらないと。

    うーーーん (@_@; 違和感を感じるなぁ~。

     

    関連記事

     

    参考サイト

    2010年1月2日土曜日

    DrScheme (Racket) で Emacs のキーバインディング

    1. 継続を理解するために Scheme を学びたい

    関数型言語を学んでいると、どこかで「継続」や「マクロ」という概念に出くわす。

    継続といえば Scheme 。 Scheme – Wikipedia によると、

    SchemeはMITコンピュータ科学・人工知能研究所の研究者だったガイ・スティールとジェラルド・ジェイ・サスマンによって、1975年ごろから開発・公開が始められた。もともとの開発の動機は、継続末尾再帰といったプログラミングのコンセプトを使って、彼らが研究していた並行プログラミングにおける制御構造の理論を検証するためだったという。

    Haskell では Continuation モナド で継続による計算を実現している。これを理解するには、Scheme の継続を予め理解しておく必要がある。

    Lisp 系の言語はこれまで学んだことがない。SICP をいつか読みたいと思いつつ、ずっと放置していた。

    SICP を学ぶ前に、以下のサイトで Scheme を勉強することにした。

     

    2. DrScheme (Racket) で Scheme を試す

    もうひとつの Scheme 入門 では、Scheme の処理系として

    が紹介されている。

    Racket – Wikipedia によると、

    Racket では関数型のコア言語,に加え、以下のような幅広い種類の構造を自由に統合させている。

    • mixin クラスシステム。
    • Standard MLのように洗練されて表現力豊かなコンポーネント(モジュール)システム。
    • 高階言語杳としては初めてのコントラクトシステム。[4]
    • メタプログラミングのためのパワフルなマクロシステムをもつ。
    • 実用的なシステムとして、最初に部分継続を実装した。

    Scheme 入門 1. Scheme 処理系のインストール によると、

    DrScheme を利用する場合は MzScheme も同時にインストールされます。そのため、MzScheme を単独でインストールする必要はありません。実は、MzScheme は PLT-Scheme という処理系のエンジン部分で、DrScheme は PLT-Scheme のフロントエンドです

    お気軽に Scheme に触れるには DrScheme を使うのが良い。

    他のエディタから利用するには、以下を参照。

     

    3. Emacs のキーバインディングを利用する

    DrScheme のデフォルトのキー操作は使いにくい。 (+_+) Emacs のキーバインディングにするには設定を変更する。

    3.3 Keyboard Shortcuts によると、

    If you are most familiar with Emacs-style key bindings (especially on windows or some linux installations where the control key is, by default, for the menu shortcuts), you should uncheck the Enable keybindings in menus preference. Many of the keybindings below are inspired by Emacs.

    メニューより、「Edit > Preferences… > Editing > General」の Enable keybindings in menus のチェックをはずす。

    testtest.png

    ただし、メタキーの入力は Escape キーを利用する。

    On Windows and Mac OS X, Meta is only available through the Escape key.

    (同上より)

     

    3. コードの実行と編集

    DrScheme (Racket) では、

    • F5 でコードの実行
    • F6 でエディタに戻る

     

    4. 移動

    括弧単位でカーソルを移動するには、3.3 Keyboard Shortcuts によると、

  • M-C-f : move forward one S-expression

  • M-C-b : move backward one S-expression

  • 上記のようにメタキーの入力をするには、Escape キーを押せばいいので、

    1. Escape キーを押す。
    2. C-f または C-b を押す。

    という操作をする。

     

    Escape キーの代替

    追記(2012/08/03): Escape キーは、ホームポジションから遠い位置にある。 キー入力が面倒な場合、AutoHotkey で代替すると良い。

    これにより、

    1. Ctrl + [
    2. Ctrl + f

    の順にキーを入力すると M-C-f と同じ動作になる。 このとき、Ctrl キー押したまま [, f を入力すれば良い。押下したキーを離す必要はない。

     

    5. コードの補完