<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5579535841246168871</id><updated>2024-08-29T14:02:10.336+09:00</updated><category term="haskell"/><category term="BDD"/><category term="TDD"/><category term="javascript"/><category term="メモ"/><category term="Design"/><category term="HUnit"/><category term="Java"/><category term="Python"/><category term="CSS3"/><category term="CodingBat"/><category term="CoffeeScript"/><category term="GUI"/><category term="Gauche"/><category term="HTML5"/><category term="MacOSX"/><category term="MetroStyle"/><category term="NetBeans"/><category term="PHP"/><category term="PHPUnit"/><category term="QuickCheck"/><category term="pear"/><category term="pecl"/><category term="Animation"/><category term="Dropbox"/><category term="Homebrew"/><category term="OOP"/><category term="Phing"/><category term="Processing Scala Spde"/><category term="Scheme"/><category term="UX"/><category term="Vows"/><category term="Web"/><category term="cabal"/><category term="canvas"/><category term="jQuery"/><category term="node.js"/><category term="vim"/><category term="wtfjs"/><category term="zsh"/><title type='text'>coding as drawing</title><subtitle type='html'>VoQnのお勉強メモとかです。&#xa;あんまり役に立ちませんよ</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>34</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-5873976258386478689</id><published>2012-08-23T16:30:00.000+09:00</published><updated>2012-08-24T08:08:02.343+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="node.js"/><title type='text'>Fibonacci Algorithm Benchmark</title><content type='html'>&lt;h3&gt;Subject&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;http://teddziuba.com/2011/10/node-js-is-cancer.html&quot;&gt;Node.js is cancer&lt;/a&gt; (リンク先は素に削除さている、&lt;a href=&quot;http://blog.uu59.org/2011-10-04-node-js-is-cancer.html&quot;&gt;uu59さんによる和訳記事はこっち&lt;/a&gt;) でも言われてた「node.js でフィボナッチは遅い」。&lt;/p&gt;&lt;p&gt;この例はあくまでフィボナッチ関数がどうの、という話をする為ではないし、そもそも&lt;a href=&quot;http://blog.nodejs.org/2011/10/04/an-easy-way-to-build-scalable-network-programs/&quot;&gt;Ryanが言うように&lt;/a&gt;&lt;br /&gt;
&lt;blockquote&gt;Node.jsはスケーラブルなネットワークプログラムを簡単に書くための手段であって、レイトレーシングや、ウェブブラウザや、動画エンコーディングシステムをNode.jsで作ってはいけない。&lt;/blockquote&gt;のであって、Suckな例として出すには藪蛇なものだったのだけれど。&lt;/p&gt;&lt;p&gt;それはそれとして、じゃあ実際フィボナッチ関数の実装アルゴリズムは複数あるけれど、実際どれくらいの差が出るのかベンチマークとってみました。&lt;/p&gt;&lt;h3&gt;How&lt;/h3&gt;&lt;h4&gt;Hardware/OS&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;MacBook 2009 (2.26GHz Intel Core 2 Duo)&lt;/li&gt;
&lt;li&gt;Mac OS X - 10.7.4(Lion)&lt;/li&gt;
&lt;li&gt;Node.js v0.8.0&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;fib(n) in 60fps&lt;/h4&gt;&lt;p&gt;60fps(およそ16ミリ秒) 以内で処理が終わる限界を調査して、速い実装ほど、引数 &lt;code&gt;n&lt;/code&gt; の値が大きくなることを確認する。&lt;/p&gt;&lt;p&gt;なぜ、同じ引数 (例えば &lt;code&gt;n = 1000&lt;/code&gt;) を渡して実行時間を比較する方法でないかというと、それぞれの実装のパフォーマンスの差があまりにも大きいのと、再帰処理による実装では引数が大きいとコールスタックの上限を超えてしまい、参考にならないから。&lt;/p&gt;&lt;h4&gt;Algorithm&lt;/h4&gt;&lt;dl&gt;&lt;dt&gt;Recursive normal &lt;i&gt;O(n^2)&lt;/i&gt;&lt;/dt&gt;
&lt;dd&gt;普通に &lt;code&gt;Fib(n) = n &amp;lt;= 2 ? 1 : Fib(n - 2) + Fib(n - 1)&lt;/code&gt; の実装&lt;/dd&gt;
&lt;dt&gt;Recursive with accumulation &lt;i&gt;O(n)&lt;/i&gt;&lt;/dt&gt;
&lt;dd&gt;蓄積変数を2つ用意したイテレータを内部で用意し、そちらを再帰&lt;/dd&gt;
&lt;dt&gt;Recursive sequential square &lt;i&gt;O(log n)&lt;/i&gt;&lt;/dt&gt;
&lt;dd&gt;逐次平方による再帰&lt;/dd&gt;
&lt;dt&gt;Procedure with accumulation &lt;i&gt;O(n)&lt;/i&gt;&lt;/dt&gt;
&lt;dd&gt;蓄積変数を非再帰で &lt;code&gt;while&lt;/code&gt; ループ&lt;/dd&gt;
&lt;dt&gt;Procedure sequential square &lt;i&gt;O(log n)&lt;/i&gt;&lt;/dt&gt;
&lt;dd&gt;逐次平方の非再帰&lt;/dd&gt; &lt;/dl&gt;&lt;p&gt;なお、最速アルゴリズムである &lt;em&gt;黄金比のn乗を√5で割った近似値で計算する方法&lt;/em&gt; は &lt;code&gt;O(1)&lt;/code&gt; で、「制限時間内で回せる限界を図る」ベンチマークを取ると終了しなくなるのがわかっていたので除外した。&lt;/p&gt;&lt;h4&gt;最速アルゴリズム : φ ^ n / √5 (&lt;i&gt;O(log n)&lt;/i&gt;)&lt;/h4&gt;&lt;p&gt;黄金比のn乗を√5で割ると、フィボナッチ数列のn番目の値と近似します&lt;/p&gt;&lt;pre class=&quot;brush:javascript&quot; title=&quot;fib-fastest.js&quot;&gt;var PHY = (1 + Math.sqrt(5)) / 2;

var SQRT_5 = Math.sqrt(5);

var fib = function(n) {
  return Math.round(Math.pow(PHY, n) / SQRT_5);
};&lt;/pre&gt;&lt;p&gt;定数時間で処理が終わります。&lt;/p&gt;&lt;h3&gt;Result&lt;/h3&gt;&lt;pre class=&quot;console&quot;&gt;&lt;code&gt;$ node fib-benchmark.js
::: Fibonacci Implements Benchmark :::
by 60 fps, each implements reach ...
recursives normal ::: fib( 30 )
recursives accumu ::: fib( 7617 )
recursives square ::: fib( 997146 )
procedures accumu ::: fib( 13740 )
procedures square ::: fib( 5265634 )
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;大体予想どおりだった。注目すべきは同じ O(n) や O(log n) であるはずの実装が、再帰か非再帰かでかなりのパフォーマンスの差が出ていること。関数呼び出しがかなりのオーバーヘッドになってるのが確認できる。&lt;/p&gt;&lt;p&gt;理屈的には同じパフォーマンスであるはずの逐次平方処理でも、再帰版と非再帰版でほぼ5倍の差が出ている。&lt;/p&gt;&lt;h3&gt;Implements&lt;/h3&gt;&lt;pre class=&quot;brush:javascript&quot; title=&quot;fib.js&quot;&gt;var fibonacci_implements = {
  recursives: {
    normal: function fib(n) {
      return n &amp;lt;= 2 ? 1 : fib(n - 2) + fib(n - 1);
    },
    accumu: function fib(n) {
      var iterator = function(a, b, c) {
        return c &amp;lt;= 0 ? b : iterator(b, a + b, c - 1);
      };
      return iterator(1, 0, n);
    },
    square: function fib(n) {
      var iterator = function(p, q, a, b, c) {
        return c &amp;lt;= 0 ? b :
          c % 2 === 0 ?
            iterator(p * p + q * q, q * (q + 2 * p), a, b, c / 2) :
            iterator(p, q, (a + b) * q + a * p, a * q + b * p, c - 1);
      };
      return iterator(0, 1, 1, 0, n);
    }
  },
  procedures: {
    accumu: function fib(n) {
      var a, b, c, tmp;
      a = 0;
      b = 1;
      c = n;
      while (c &amp;gt; 0) {
        tmp = b;
        b = a + b;
        a = tmp;
        c = c - 1;
      }
      return b;
    },
    square: function fib(n) {
      var p, q, a, b, c, tmp_1, tmp_2;
      a = q = 1;
      p = b = 0;
      c = n;
      while (c &amp;gt; 0) {
        if (c % 2 === 0) {
          tmp_1 = p * p + q * q;
          tmp_2 = q * (q + 2 * p);
          p = tmp_1;
          q = tmp_2;
          c = c / 2;
        } else {
          tmp_1 = (a + b) * q + a * p;
          tmp_2 = a * q + b * p;
          a = tmp_1;
          b = tmp_2;
          c = c - 1;
        }
      }
      return b;
    }
  }
};
&lt;/pre&gt;&lt;h3&gt;Benchmark Script&lt;/h3&gt;&lt;pre class=&quot;brush:javascript&quot; title=&quot;fib-benchmark.js&quot;&gt;var fibonacci_implements = require(&#39;fib.js&#39;);

var FPS_60 = Math.floor(1000 / 60);

var get_time = function() {
  return new Date().getTime();
};

var benchmark_fibonacci = function(fib_func) {
  var start_at, end_at, limit, should_step, n, i, l, score, sum;
  limit = FPS_60;
  i = 0;
  l = 100;
  score = 0;
  while (i &amp;lt; l) {
    should_step = true;
    n = 0;
    while (should_step) {
      n++;
      start_at = get_time();
      try {
        fib_func(n);
        end_at = get_time();
        should_step = end_at - start_at &amp;lt; limit;
      } catch (e) { // maybe &quot;Stack Over Flow&quot;
        should_step = false;
      }
    }
    score += n;
    i++;
  }
  return Math.floor(score / l);
};

(function main() {
  var type_name, sub_type_name, type, fib, time;

  console.log(&#39;::: Fibonacci Implements Benchmark :::&#39;);
  console.log(&#39;by 60 fps, each implements reach ...&#39;);

  for (type_name in fibonacci_implements) {
    if (fibonacci_implements.hasOwnProperty(type_name)) {
      type = fibonacci_implements[type_name];

      for (sub_type_name in type) {
        if (type.hasOwnProperty(sub_type_name)) {
          fib = type[sub_type_name];
          arg = benchmark(fib);
          console.log(type_name, sub_type_name, &#39;:::&#39;, &#39;fib(&#39;, arg, &#39;)&#39;);
        }
      }
    }
  }
})();
&lt;/pre&gt;&lt;h3&gt;結論&lt;/h3&gt;&lt;p&gt;確かにJavaScriptって関数呼び出しがコストになったりするので、速い実装にこだわると如何にして関数呼び出しを避けられるかっていう所に頭使うのだけど、それ以前に最速アルゴリズムが定数時間で終わるように、基本的なことまずできてからやるべきではあると思います。&lt;/p&gt;&lt;p&gt;それと、そもそも 「&lt;i&gt;O(n^2)&lt;/i&gt; かかるような処理でも良いじゃないか、たかが JavaScript なのだし」（過剰に委ねずにすむ方法を考える方が大事では？ そんなに頑張りたいんだったらネイティブでやれ）って考え方もある。&lt;/p&gt;&lt;!--Syntax Highlight--&gt;&lt;br /&gt;
&lt;script src=&#39;http://syntaxhighlight-blogger-dynamic.googlecode.com/files/syntaxhighlighter-20111212.js&#39; type=&#39;text/javascript&#39;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/5873976258386478689/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/08/fibonacci-algorithm-benchmark.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5873976258386478689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5873976258386478689'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/08/fibonacci-algorithm-benchmark.html' title='Fibonacci Algorithm Benchmark'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-8158532268261997963</id><published>2012-05-04T20:56:00.000+09:00</published><updated>2012-05-04T20:56:36.495+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design"/><category scheme="http://www.blogger.com/atom/ns#" term="GUI"/><category scheme="http://www.blogger.com/atom/ns#" term="MetroStyle"/><category scheme="http://www.blogger.com/atom/ns#" term="UX"/><title type='text'>ニコニコ超デザイン-Metro考察編-を公開しました</title><content type='html'>&lt;div style=&quot;width:425px&quot; id=&quot;__ss_12797234&quot;&gt;&lt;strong style=&quot;display:block;margin:12px 0 4px&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/VoQn/metro-12797234&quot; title=&quot;ニコニコ超デザイン-Metro考察編-&quot; target=&quot;_blank&quot;&gt;ニコニコ超デザイン-Metro考察編-&lt;/a&gt;&lt;/strong&gt; &lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/12797234&quot; width=&quot;425&quot; height=&quot;355&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt; &lt;div style=&quot;padding:5px 0 12px&quot;&gt;View more &lt;a href=&quot;http://www.slideshare.net/&quot; target=&quot;_blank&quot;&gt;presentations&lt;/a&gt; from &lt;a href=&quot;http://www.slideshare.net/VoQn&quot; target=&quot;_blank&quot;&gt;Mizushima Kazuhiro&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;a href=&quot;http://www.slideshare.net/VoQn/metro-12761898&quot;&gt;ニコニコ超デザイン-Metro死闘編-&lt;/a&gt;の続きです，&lt;/p&gt;&lt;p&gt;元々は社内勉強会の資料として作成しました&lt;br /&gt;
死闘編は経緯だったりネタ的な要素に走りすぎたのでデザインに関するナレッジはここにまとめてあります&lt;br /&gt;
より実践的かつ具体的なノウハウ等は正規リリース後あたりで公開したいと思っています&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/8158532268261997963/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/05/metro.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/8158532268261997963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/8158532268261997963'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/05/metro.html' title='ニコニコ超デザイン-Metro考察編-を公開しました'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-5934738847466310101</id><published>2012-05-02T04:39:00.000+09:00</published><updated>2012-05-02T05:03:21.959+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design"/><category scheme="http://www.blogger.com/atom/ns#" term="MetroStyle"/><title type='text'>ニコニコ超会議で発表したスライドを公開しました</title><content type='html'>&lt;div style=&quot;width:425px&quot; id=&quot;__ss_12761898&quot;&gt;&lt;strong style=&quot;display:block;margin:12px 0 4px&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/VoQn/metro-12761898&quot; title=&quot;ニコニコ超デザイン-Metro死闘編-&quot; target=&quot;_blank&quot;&gt;ニコニコ超デザイン-Metro死闘編-&lt;/a&gt;&lt;/strong&gt; &lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/12761898&quot; width=&quot;425&quot; height=&quot;355&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt; &lt;div style=&quot;padding:5px 0 12px&quot;&gt;View more &lt;a href=&quot;http://www.slideshare.net/&quot; target=&quot;_blank&quot;&gt;presentations&lt;/a&gt; from &lt;a href=&quot;http://www.slideshare.net/VoQn&quot; target=&quot;_blank&quot;&gt;Mizushima Kazuhiro&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;h3&gt;この発表内容について&lt;/h3&gt;&lt;p&gt;経緯として，超会議2日目の&lt;a href=&quot;http://info.nicovideo.jp/conference/cho-engineer-meeting/&quot;&gt;超エンジニアミーティング&lt;/a&gt;では，午前に&lt;a href=&quot;http://live.nicovideo.jp/watch/lv90334722&quot;&gt;プロ生勉強会の中で，koizuka さんによる &quot;ニコニコ超開発&quot; の発表&lt;/a&gt;があり，僕がデザインで参加した ニコニコ動画の MetroStyleApp の開発状況や裏話的なものはもう話されていたのですが，LT枠が余ったというのと，午前中講演に行けなかった人用に，「天丼芸」として用意し，発表したのがこの内容です&lt;/p&gt;&lt;p&gt;本来なら，もっと有用な&lt;strong&gt;「Metro Style App のデザインの勘所ってどこよ?」&lt;/strong&gt;とか&lt;strong&gt;「プログラマーとのスムーズなやり取りが出来るデザインワークフローってどうやるの?」&lt;/strong&gt;とか，そういった内容をプレゼンすべきだったのですが…&lt;/p&gt;&lt;p&gt;どう考えても 5分でまとめ切れないというのと，自身がまだそれをキチンとしたカタチで整理できていないところもあり，&lt;q&gt;「逼迫した状況で出来るだけ，やるだけやるしかなかった」感が伝わりさえすればいいや&lt;/q&gt;と思い，来場者の方には最後に笑って締めていただこうとこのようなカタチになりました&lt;/p&gt;&lt;h3&gt;おわりに-ニコニコ超会議に参加された皆様へ御礼&lt;/h3&gt;&lt;p&gt;2012/04/28-29 に幕張メッセで&lt;a href=&quot;http://www.chokaigi.jp/&quot;&gt;ニコニコ超会議&lt;/a&gt; が行われ，来場してきてくださったユーザーの皆様，企画に登壇者として参加してくれた皆様に再度お礼を申しあげます．&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/5934738847466310101/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/05/blog-post.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5934738847466310101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5934738847466310101'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/05/blog-post.html' title='ニコニコ超会議で発表したスライドを公開しました'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-5734015084616102756</id><published>2012-03-09T15:19:00.000+09:00</published><updated>2012-03-09T16:24:36.566+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell その5 : テスト用ユーティリティ関数など</title><content type='html'>&lt;p&gt;前回までのエントリーはこちら&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html&quot;&gt;BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html&quot;&gt;BDD on Haskell チュートリアル その3-1 catch できない哀しみ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html&quot;&gt;BDD on Haskell チュートリアル その3-2 throw Exception を使わず基本は Either を使おう&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-3.html&quot;&gt;BDD on Haskell チュートリアル その3-3 : 仕様変更はテストから&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-4.html&quot;&gt;BDD on Haskell チュートリアル その4 : リファクタリングとパフォーマンスチューニング&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;今回はテストコードを手早く書く為のユーティリティを紹介&lt;/p&gt;&lt;h3&gt;例1 TemplateHaskell を使う&lt;/h3&gt;&lt;p&gt;&lt;code&gt;test-framework-th&lt;/code&gt; というライブラリを利用すると，関数の先頭に &lt;code&gt;case_&lt;/code&gt; &lt;code&gt;prop_&lt;/code&gt; というプレフィックスがあるものを自動的にテストユニットに組み入れてくれる&lt;/p&gt;&lt;pre&gt;&lt;code&gt;{-# LANGUAGE TemplateHaskell #-}

import Test.Framework.TH ( defaultMainGenerator )
import Test.Framework ( defaultMain, Test )
import Test.Framework.Providers.HUnit ( testCase )
import Test.Framework.Providers.QuickCheck2 ( testProperty )
import Test.HUnit ( (@=?) )

main :: IO ()
main = $(defaultMainGenerator)

case_10_div_2 = 5 @=? (10 `div` 2)

prop_add_commutative x y = x + y == y + x
    where types = (x :: Int, y :: Int)&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;テスト実行&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;&lt;code&gt;$ runhaskell test.hs
Main:
  add commutative: [OK, passed 100 tests]
  10 div 2: [OK]

        Properties  Test Cases  Total
Passed  1           1           2
Failed  0           0           0
Total   1           1           2&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;プレフィックスに続く関数名をテストのラベルとして表示してくれる． これは便利だし，手早くテストコードを書く際はこれで良いと思う&lt;/p&gt;&lt;h3&gt;例2 自作のユーティリティ関数&lt;/h3&gt;&lt;p&gt;ただ，TH を使ったこれらだと，たくさんテストケースを記述したりするのが億劫でもある．よくコンパクトに設計したライブラリであれば，テストしたい対象は2つのパターンに収まる&lt;/p&gt;&lt;ul&gt;&lt;li&gt;ライブラリで宣言した代数データ型に，いくつかの関数を適用するテスト&lt;/li&gt;
&lt;li&gt;ライブラリで宣言した関数に，いくつかの引数を与えて検証するテスト&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;そこで，自分は以下のユーティリティ関数を自作して使ってる&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module Util.Test (
  subject, should,
  (&amp;lt;-?), (&amp;lt;=?), (+:), (-:), (*:), (%:), (/:)
 ) where

import Test.Framework ( testGroup, Test, TestName )
import Test.Framework.Providers.HUnit ( testCase )
import Test.Framework.Providers.QuickCheck2 ( testProperty )
import Test.HUnit ( (@=?), Assertion )
import Test.QuickCheck ( Testable )

infixr 5 +:, -:, *:, %:, /:
infixr 6 &amp;lt;-?, &amp;lt;=?

subject :: TestName       -- test label
           -&amp;gt; a           -- subject function
           -&amp;gt; [a -&amp;gt; Test] -- tests about subject
           -&amp;gt; Test
subject label target = testGroup label . map ($ target)

should :: TestName                 -- test label
          -&amp;gt; t                     -- expected value
          -&amp;gt; (t -&amp;gt; b -&amp;gt; Assertion) -- test assertion operator
          -&amp;gt; (a -&amp;gt; b)              -- function return actual value
          -&amp;gt; a                     -- subject
          -&amp;gt; Test
should label expected op fn = testCase label . op expected . fn

(&amp;lt;-?) :: (Show a, Show b, Eq b) =&amp;gt;
          b           -- expected value
          -&amp;gt; a        -- argument for subject function
          -&amp;gt; (a -&amp;gt; b) -- subject function
          -&amp;gt; Test
expected &amp;lt;-? arg = testCase detail . ( expected @=? ) . ($ arg) where
  detail = unwords
           [ &amp;quot;λ&amp;quot;, show arg, &amp;quot;\t-&amp;gt;&amp;quot;, show expected ++ &amp;quot;\t&amp;quot; ]

(&amp;lt;=?) :: (Show b, Eq b) =&amp;gt;
         b           -- expected value
         -&amp;gt; (a -&amp;gt; b) -- function return actual value
         -&amp;gt; a        -- argument for subject function
         -&amp;gt; Assertion
expected &amp;lt;=? fn = (expected @=?) . fn

(-:) :: TestName -&amp;gt; Assertion -&amp;gt; Test
n -: t  = testCase n t

(+:) :: TestName -&amp;gt; (a -&amp;gt; Assertion) -&amp;gt; a -&amp;gt; Test
n +: f  = testCase n . f

(*:) :: Testable a =&amp;gt; TestName -&amp;gt; a -&amp;gt; Test
n *: p  = testProperty n p

(%:) :: Testable b =&amp;gt; TestName -&amp;gt; (a -&amp;gt; b) -&amp;gt; a -&amp;gt; Test
n %: f  = testProperty n . f

(/:) :: TestName -&amp;gt; [Test] -&amp;gt; Test
l /: ts = testGroup l ts&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;リファクタリング -- 見やすいテストコード&lt;/h4&gt;&lt;p&gt;これがどう便利かというと，今まで書いていた quotF, remF などのテストコードがこのように記述できる&lt;/p&gt;&lt;pre&gt;&lt;code&gt;import Util.Test

testSuite :: [Test]
testSuite =
  [ &amp;quot;example&amp;quot; /:
    [ subject &amp;quot;\\(x, y) -&amp;gt; x `quotRem&amp;#39;` y&amp;quot; (uncurry quotRem&amp;#39;)
      [ Left DivideByZero &amp;lt;-? (   1,   0)
      , Right ( 12,  2)   &amp;lt;-? ( 122,  10)
      , Right (-12,  3)   &amp;lt;-? ( 123, -10)
      , Right (-13,  7)   &amp;lt;-? (-123,  10)
      , Right ( 13,  7)   &amp;lt;-? (-123, -10)
      , Right (  4,  0)   &amp;lt;-? (  10,   2.5)
      , Right (  1,  1.5) &amp;lt;-? ( 4.5,   3)
      , Right ( -1, 29.9) &amp;lt;-? (-0.1,  30)
      ]
    , subject &amp;quot;\\(x, y) -&amp;gt; x `quot&amp;#39;` y&amp;quot; (uncurry quot&amp;#39;)
      [ Left DivideByZero &amp;lt;-? ( 1, 0)
      , Right          12 &amp;lt;-? ( 123, 10)
      ]
    , subject &amp;quot;\\(x, y) -&amp;gt; x `rem&amp;#39;` y&amp;quot; (uncurry rem&amp;#39;)
      [ Right 3    &amp;lt;.? (123, 10)
      , Right 0.1  &amp;lt;.? (4.7, 2.3)
      ]
    ]
  , &amp;quot;specification&amp;quot; /:
    [ &amp;quot;(q, r) = (quot&amp;#39; x y, rem&amp;#39; x y)&amp;quot; /:
      [ &amp;quot;x - y * q + r (0 &amp;lt;= r &amp;lt; |y|)&amp;quot; *: prop_quotRem&amp;#39;
      , &amp;quot;(q, r) = quotRem&amp;#39; x y&amp;quot;        *: prop_synonym
      ]
    ]
  ]

prop_quotRem&amp;#39; :: Double -&amp;gt; Double -&amp;gt; Bool
prop_quotRem&amp;#39; x y =
  case (x `quot&amp;#39;` y, x `rem&amp;#39;` y) of
    (Left _, Left _)   -&amp;gt; y == 0
    (Right q, Right r) -&amp;gt;
      if r == 0
      then (x .= (fromIntegral q * y)) tolerance
      else 
       (x .= (fromIntegral q * y + r)) tolerance
       &amp;amp;&amp;amp; 0 &amp;lt; r &amp;amp;&amp;amp; r &amp;lt; abs y
    _ -&amp;gt; False -- any mistake definition

prop_synonym :: Double -&amp;gt; Double -&amp;gt; Bool
prop_synonym x y =
  case (x `quot&amp;#39;` y, x `rem&amp;#39;` y, x `quotRem&amp;#39;` y) of
    (Left  _, Left  _, Left   _)       -&amp;gt; y == 0
    (Right q, Right r, Right (q&amp;#39;, r&amp;#39;)) -&amp;gt; (q, r) == (q&amp;#39;, r&amp;#39;)「
    _ -&amp;gt; False -- any mistake definition

-- adhock utility
(&amp;lt;.?) :: (Show b, Show e, Ord e) =&amp;gt;
          Either e Double -&amp;gt; b -&amp;gt; (b -&amp;gt; Either e Double) -&amp;gt; Test
expected &amp;lt;.? arg = should label expected eq&amp;#39; ($ arg) where
  label = intercalate &amp;quot; &amp;quot; keys
  keys =
    [ &amp;quot;λ&amp;quot;, show arg
    , &amp;quot;\t-&amp;gt;&amp;quot;, show expected
    , &amp;quot;(±&amp;quot;, show tolerance, &amp;quot;)&amp;quot;
    ]
  eq&amp;#39; (Right x) (Right y) =
    unless ( abs (x - y) &amp;lt; tolerance ) $
    assertFailure $ msg x y tolerance
  msg x y t = intercalate &amp;quot;\n&amp;quot;
  [ &amp;quot;\n expected\t : &amp;quot;   ++ show x
  , &amp;quot; but got\t : &amp;quot;      ++ show y
  , &amp;quot; out of trancate: &amp;quot; ++ show t
  ]

tolerance :: Double
tolerance = 1e-10&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;testSuite&lt;/code&gt; の宣言とか，RSpec にも負けない可読性だと思うのだけど，どうだろう．このユーティリティを使うユーザーのテストコードは，&lt;code&gt;subject&lt;/code&gt;にテストの対象となる関数あるいはデータ型を第二引数に渡し，第三引数に「(期待値)&lt;-? (引数)」のリストを列挙する仕組みになる．&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;テスト実行後の出力&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;&lt;code&gt;example:
  \(x, y) -&amp;gt; x `quotRem&amp;#39;` y:
    λ (1.0,0.0)       -&amp;gt; Left divide by zero   : [OK]
    λ (122.0,10.0)    -&amp;gt; Right (12,2.0)        : [OK]
    λ (123.0,-10.0)   -&amp;gt; Right (-12,3.0)       : [OK]
    λ (-123.0,10.0)   -&amp;gt; Right (-13,7.0)       : [OK]
    λ (-123.0,-10.0)  -&amp;gt; Right (13,7.0)        : [OK]
    λ (10.0,2.5)      -&amp;gt; Right (4,0.0)         : [OK]
    λ (4.5,3.0)       -&amp;gt; Right (1,1.5)         : [OK]
    λ (-0.1,30.0)     -&amp;gt; Right (-1,29.9)       : [OK]
  \(x, y) -&amp;gt; x `quot&amp;#39;` y:
    λ (1,0)     -&amp;gt; Left divide by zero: [OK]
    λ (123,10)  -&amp;gt; Right 12: [OK]
  \(x, y) -&amp;gt; x `rem&amp;#39;` y:
    λ (123.0,10.0)    -&amp;gt; Right 3.0 (± 1.0e-10 ): [OK]
    λ (4.7,2.3)       -&amp;gt; Right 0.1 (± 1.0e-10 ): [OK]
specification:
  (q, r) = (quot&amp;#39; x y, rem&amp;#39; x y):
    x - y * q + r (0 &amp;lt;= r &amp;lt; |y|): [OK, passed 100 tests]
  (q, r) = quotRem&amp;#39; x y: [OK, passed 100 tests]

        Properties  Test Cases   Total
Passed  2           12           14
Failed  0           0            0
Total   2           12           14&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;テストケースのラベルが「引数 -&amp;gt; 期待値」というパターンになり，&lt;code&gt;subject&lt;/code&gt; に渡した関数が具体的に何をテストしたかが一目瞭然になる．&lt;/p&gt;&lt;p&gt;TDD/BDD で一番煩わしいのって，テストケースを書き足していく時なので，こうやって 関数に対する 引数 と戻りの期待値を列挙するだけで済むようなユーティリティを書いていくとラクになるし，テスト結果も，開発者以外の目で見ても，何が出来ているかチェックしやすくなる．&lt;/p&gt;&lt;p&gt;TemplateHaskell を利用しなくても，このような DSL Like なユーティリティを自作できるので，色々試してみると良い．ある程度まとまったら，更にそれらをより簡潔に記述できるように TemplateHaskell を利用するのも良い．&lt;/p&gt;&lt;p&gt;自分も今後，これをもっと整理して Hackage で公開しようかと思う&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/5734015084616102756/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/03/bdd-on-haskell-5.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5734015084616102756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5734015084616102756'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/03/bdd-on-haskell-5.html' title='BDD on Haskell その5 : テスト用ユーティリティ関数など'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-1098681124183715135</id><published>2012-01-31T20:27:00.000+09:00</published><updated>2012-01-31T20:27:02.258+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その4 : リファクタリングとパフォーマンスチューニング</title><content type='html'>&lt;p&gt;前回までのエントリーはこちら&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html&quot;&gt;BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html&quot;&gt;BDD on Haskell チュートリアル その3-1 catch できない哀しみ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html&quot;&gt;BDD on Haskell チュートリアル その3-2 throw Exception を使わず基本は Either を使おう&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a  href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-3.html&quot;&gt;BDD on Haskell チュートリアル その3-3 : 仕様変更はテストから&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;今回のテーマ 「テスト済みのモジュールのパフォーマンス改善」&lt;/h3&gt;&lt;p&gt;前回までで一通りの TDD / BDD によるアプローチで，Scheme RSR6 の &lt;code&gt;div-and-mod&lt;/code&gt; に相当する関数群のモジュールは実装できた．&lt;br /&gt;
ゼロ除算時におけるエラーも，&lt;code&gt;Either&lt;/code&gt; モナドで包む事で，より正確な型による関数の表現もでき，テストもほぼ完璧．&lt;/p&gt;&lt;p&gt;これでもう一切の問題も無いのでは… と思いきやまだ一つ懸念すべきことが残されている．&lt;/p&gt;&lt;p&gt;パフォーマンスとスケールである．&lt;/p&gt;&lt;h3&gt;現時点のパフォーマンス&lt;/h3&gt;&lt;p&gt;今の時点での &lt;code&gt;quotRemF&lt;/code&gt;, &lt;code&gt;quotF&lt;/code&gt;, &lt;code&gt;remF&lt;/code&gt; のコードは以下のようになっている&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotRemF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; Either ArithException (a, b)
quotRemF _ 0 = divZeroError
quotRemF x y = Right $ iter x y 0 where
 iter r k q
  | r == 0 = (q, 0)
  | 0 &amp;lt; r &amp;amp;&amp;amp; r &amp;lt; abs k = (q, r)
  | otherwise = iter (r `step` k) k (next q)
 step a b
  | x * y &amp;gt;= 0 = a - b
  | otherwise  = a + b
 next n
  | x * y &amp;gt;= 0 = n + 1
  | otherwise  = n - 1

quotF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; Either ArithException a
quotF x y = fmap fst $ quotRemF x y

remF :: (Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; Either ArithException b
remF x y = fmap snd $ quotRemF x y

divZeroError :: Either ArithException a
divZeroError = Left DivideByZero&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt; で，あくまでも「テストが通ること」を主眼において実装されていたのだけど，この実装では 「巨大な数と，ごく小さい数から商と余剰を計算する」場合とてもコストがかかる．この関数のオーダーは &lt;code&gt;O(n)&lt;/code&gt; になっているからだ&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci Util.Numeric
Util.Numeric&amp;gt; 1e20 `remF` 0.3
# いつまでたっても計算が終わらない
&lt;/pre&gt;&lt;p&gt;もう一つは，浮動小数点をそのまま再帰で加算させ続けているので，たとえば &lt;code&gt;10000&lt;/code&gt; という数に対して，&lt;code&gt;0.3&lt;/code&gt; の余剰を求めようとすると，33333回誤差が広がる事になる．これではモジュールとしての完成度は低い&lt;/p&gt;&lt;h3&gt;テストを担保にリファクタリングする&lt;/h3&gt;&lt;p&gt;HUnit, QuickCheck でのテストは成功している状態なので，それを担保にしてリファクタリングをする．&lt;br /&gt;
まず，今現在の関数の関係を見ると&lt;/p&gt;&lt;pre&gt;quotRemF :: 独立
quotF :: quotRemF に依存
remF  :: quotRemF に依存
&lt;/pre&gt;&lt;p&gt;という実装になっている．そして，テストの方で&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotRemF x y&lt;/code&gt; が 返す &lt;code&gt;(q, r)&lt;/code&gt; は， &lt;code&gt;q = quotF x y&lt;/code&gt; と同じ
&lt;/pre&gt;&lt;p&gt;ことを保証している．&lt;/p&gt;&lt;p&gt;これを利用して，浮動小数点小数の誤差が出ない関数 &lt;code&gt;quotF&lt;/code&gt; をリファクタリングして，テストが通り次第 &lt;code&gt;quotRemF&lt;/code&gt; の実装に置き換えれば良さそうである&lt;/p&gt;&lt;h4&gt;&lt;code&gt;Data.Fixed.div&#39;&lt;/code&gt; を参考にする&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;-- Data.Fixed

-- | generalisation of &#39;div&#39; to any instance of Real
div&#39; :: (Real a,Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; b
div&#39; n d = floor ((toRational n) / (toRational d))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;標準ライブラリの &lt;code&gt;Data.Fixed&lt;/code&gt; にある &lt;code&gt;div&#39;&lt;/code&gt; (浮動小数点小数含む実数同士の商) はこのように実装されていた．&lt;code&gt;floor&lt;/code&gt; は小数を小さい方向に丸めるので，たとえば &lt;code&gt;-0.1&lt;/code&gt; は &lt;code&gt;-1&lt;/code&gt; に丸められる (&lt;code&gt;0.1&lt;/code&gt; は &lt;code&gt;0&lt;/code&gt; に丸める)．この性質を利用すると，必ず「余りの絶対値が0により近い方」で出せる．&lt;br /&gt;
これを参考にして，「検算して，余剰がマイナスだったら調整して結果を出す」ようにしてみれば，必ず定数時間内に計算が終わるように出来るのではないだろうか．あ，あと今気づいたけど &lt;code&gt;quotF&lt;/code&gt; の戻りの型が正しくない．商は整数である事をちゃんと型で示しておこう&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- 修正
quotF :: (Real a, Integral b) =&amp;gt;
         a -&amp;gt; a -&amp;gt; Either ArithException b
quotF _ 0 = divZeroError
quotF x y = Right . adjust . floor $ rate x y
  where
  rate n m = toRational n / toRational m
  adjust n = if prove n &amp;lt; 0 then n + 1 else n
  prove  n = x - fromIntegral n * y&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;こうやって書き換えた後に，テストで検証してみる&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci UnitTest.Util.NumericTest
*UnitTest.Util.NumericTest&amp;gt; :m + Test.QuickCheck
*UnitTest.Util.NumericTest Test.QuickCheck&amp;gt; quickCheck prop_quotF
++ OK, passed 100 tests
*UnitTest.Util.NumericTest Test.QuickCheck&amp;gt; quotF 1e+50 0.3
Right 333333333333333371101710855030252968956534903824135
&lt;/pre&gt;&lt;p&gt;リファクタリングに成功しているようだ．数が巨大すぎるので，もう誤差そのものはどうしようもないけど，計算は定数時間 &lt;code&gt;O(1)&lt;/code&gt; で出る&lt;/p&gt;&lt;h4&gt;テストで外堀を埋めつつリファクタリング&lt;/h4&gt;&lt;p&gt;リファクタリングに成功してる &lt;code&gt;quotF&lt;/code&gt; を足がかりに， 残りの &lt;code&gt;quotRemF&lt;/code&gt;, &lt;code&gt;remF&lt;/code&gt; をリファクタリングする．&lt;br /&gt;
再度 &lt;code&gt;quotF&lt;/code&gt;, &lt;code&gt;remF&lt;/code&gt; の関係をおさらいすると&lt;/p&gt;&lt;pre&gt;&lt;code&gt;prop_quotF_and_remF x y = case (quotF x y, remF x y) of
  (Left _, Left _)   -&amp;gt; y == 0
  (Right q, Right r) -&amp;gt;
      if r == 0 then
         x .== fromIntegral q * y
      else
         x .== fromIntegral q * y + r
  _ -&amp;gt; False -- そもそも実装が失敗している
  where
  n .== m = abs (n - m) &amp;lt;= 1e-5
  -- 誤差が 0.00001 以下 で「近似している」とみなす&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;このテストが通る事を維持しながら，&lt;code&gt;remF&lt;/code&gt; をリファクタリングする&lt;/p&gt;&lt;pre&gt;&lt;code&gt;remF x y = fmap prove $ quotF x y
  where
  prove n = x - fromIntegral n * y&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;リファクタリング後のコードを見てみると，&lt;code&gt;where&lt;/code&gt; 節で定義した検算の為の関数 &lt;code&gt;prove n = x - fromIntegral n * y&lt;/code&gt; の宣言が &lt;code&gt;quotF&lt;/code&gt; にも &lt;code&gt;remF&lt;/code&gt; にもある，これは共有化できそうだ．&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotRemF _ 0 = divZeroError
quotRemF x y = Right . pair . adjust . floor $ rate x y
  where
  rate n m = toRational n / toRational m
  adjust n = if prove n &amp;lt; 0 then n + 1 else n
  prove  n = x - y * fromIntegral n
  pair   n = (n, prove n)&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;quotRemF&lt;/code&gt; の宣言は 「&lt;code&gt;x&lt;/code&gt; と &lt;code&gt;y&lt;/code&gt; の比率を計算し，より小さい整数へ丸め，検算した結果で商を調整し，再度検算した余りと対にして返す」と読める&lt;br /&gt;
&lt;code&gt;quotRemF&lt;/code&gt; が効率のよい実装になったので，quotF, remF はそこから参照するようにする&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotF x y = fmap fst $ quotRemF x y
remF  x y = fmap snd $ quotRemF x y&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これは元の実装と同じですね&lt;/p&gt;&lt;h3&gt;まとめ&lt;/h3&gt;&lt;p&gt;実装の仕様を変更する際は&lt;/p&gt;&lt;ol&gt;&lt;li&gt;テストから変更する&lt;/li&gt;
&lt;li&gt;テストに失敗したのを確認する&lt;/li&gt;
&lt;li&gt;再度テストが通るように実装を修正する&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;リファクタリングの際は&lt;/p&gt;&lt;ol&gt;&lt;li&gt;実装を修正する&lt;/li&gt;
&lt;li&gt;テストに失敗せず，実装を修正してもテストが通る事を確認する&lt;/li&gt;
&lt;li&gt;もしテストが失敗しても，テストに手を加えず，実装を見直す&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;実装の仕様を変更する時は，テストから変更し，リファクタリングはテストを固定させ「最終的な挙動は変わってない」ことを保持できるようにする．&lt;/p&gt;&lt;p&gt;次回はテストをより効率よく，手早くやるためのユーティリティ関数の自作を紹介&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/1098681124183715135/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-4.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/1098681124183715135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/1098681124183715135'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-4.html' title='BDD on Haskell チュートリアル その4 : リファクタリングとパフォーマンスチューニング'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-6270755379326372064</id><published>2012-01-10T09:23:00.000+09:00</published><updated>2012-01-10T09:23:22.221+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その3-3 : 仕様変更はテストから</title><content type='html'>&lt;p&gt;前回までのエントリーはこちら&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html&quot;&gt;BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html&quot;&gt;BDD on Haskell チュートリアル その3-1 catch できない哀しみ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html&quot;&gt;BDD on Haskell チュートリアル その3-2 throw Exception を使わず基本は Either を使おう&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h3&gt;今回のテーマ - 「テスト済みのモジュールの仕様変更」&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html&quot;&gt;前回書いたように&lt;/a&gt;，IO が絡まないはずの関数群に &lt;code&gt;Exception&lt;/code&gt; を &lt;code&gt;throw&lt;/code&gt; させるよりは &lt;code&gt;Either &amp;lt;Error&amp;gt; &amp;lt;Success&amp;gt;&lt;/code&gt; というパターンで型をつけてあげた方が設計としては親切だ．&lt;br /&gt;
というわけで今回は，「テスト済みのモジュールに仕様変更を加える」実例として， &lt;code&gt;quotF&lt;/code&gt; &lt;code&gt;remF&lt;/code&gt; の返す型を変更し，もっと良いモジュールへ書き換え，テストもより完璧なものにする取り組みをやってみる&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;&lt;code&gt;throw Exception&lt;/code&gt; する関数に対するテスト&lt;/h3&gt;&lt;p&gt;前回「例外はテストしずらい」と書いた．一応テストする術はある&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;使うモジュールたち&lt;/h4&gt;&lt;p&gt;テストモジュールに以下をインポートする ( test-framework, HUnit, QuickCheck2 がインストールされている前提 )&lt;/p&gt;&lt;pre&gt;&lt;code&gt;import Test.Framework.Providers.HUnit ( testCase )
import Test.Framework.Providers.QuickCheck ( testProperty )
import Test.HUnit ( &amp;#64;=? )
import Test.QuickCheck ( Property, (==&amp;gt;) )
import Control.Exception
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;例外 テスト - Hunit&lt;/h4&gt;&lt;p&gt;HUnit では &lt;code&gt;assertTrue&lt;/code&gt; などを使って，スローされた例外が，求めている型と等しいかチェックするように書く&lt;/p&gt;&lt;pre&gt;&lt;code&gt;testCase &amp;quot;throw Exception&amp;quot; testDivZero
-- ...
testDivZero = IO (Left DivideByZero) &amp;#64;=? divZero where
 divZero = try (evaluate (1 `div` 0) :: ArithException )&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;例外テスト - QuickCheck&lt;/h4&gt;&lt;p&gt;QuickCheck では，例外がスローされる条件と，スローされたものが期待する例外型と同じ事をコードで示せば良い&lt;/p&gt;&lt;pre&gt;&lt;code&gt;testProperty &amp;quot;if denom is 0, throw Exception&amp;quot; prop_divZero

prop_divZero :: Int -&amp;gt; Int -&amp;gt; Property
prop_divZero x y =
  y == 0 ==&amp;gt;
  IO (Left DivideByZero) == (try (evaluate (x `div` y) :: ArithException))
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;IO が不要なのに throw を検証する事の不快な理由&lt;/h3&gt;&lt;p&gt;まぁ， こんな風に例外を投げる関数も，その投げる例外についても検証できるから「これでも別に良いのでは」と思うかもしれない．けれど，&amp;quot;純粋&amp;quot;という言葉が好きなはずの Haskeller 諸氏はこれで甘んじてはいけないだろう．理由は以下である&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;「何をテストしたいんだか」純粋でなくなる&lt;/h4&gt;&lt;p&gt;上記の例で見るように，例外を捕捉する為に目的の関数を &lt;code&gt;evaluate&lt;/code&gt; で &lt;code&gt;IO&lt;/code&gt; モナドに持ち上げてテストすると，結果として見た場合「これは本来 IO によらない」モジュールである事がわかりづらくなる．&lt;/p&gt;&lt;p&gt;そして， &lt;code&gt;try&lt;/code&gt; で例外をとらえて &lt;code&gt;assertEqual&lt;/code&gt; で，期待している値と比較するわけだけど，元々検証したい関数は &lt;code&gt;&amp;quot;div&amp;quot;&lt;/code&gt; であって，&lt;code&gt;evaluate&lt;/code&gt; や &lt;code&gt;try&lt;/code&gt; が「完璧」である事に依存しているし，何を本当に検証したいか簡潔明確でない&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;モジュールの目的の曖昧化&lt;/h4&gt;&lt;p&gt;そして，仮に例外を投げうる関数をこのようにテストしきった後のテストコードは，そのコードを直接さわった開発者以外から見た時に，モジュールの目的が曖昧に見える．「ん? IO に関係あるの?」「これって整数以外にも何か別の型返すの?」というように．&lt;/p&gt;&lt;p&gt;もちろん，IO に関係する，HTTP Request, Response などに使うユーティリティのモジュールであれば，&lt;code&gt;try&lt;/code&gt;, &lt;code&gt;catch&lt;/code&gt; はあって良いだろう (というか，それが本来の正しい使い方)&lt;/p&gt;&lt;p&gt;けれど，IO に関係なく動作するはずの純粋な関数を束ねるモジュールであれば,出来るだけ「呼ぶことが無かったはずの外部のモジュール，モナド」を組み込んでテストしたのでは，モジュール自体の設計にミスがあるようなものだ．&lt;br /&gt;
(だって，そのモジュールだけじゃマトモに使えないって言ってるようなものだしね)&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;テストから修正&lt;/h3&gt;&lt;p&gt;TDD / BDD の基本にのっとり，テストの方から仕様を変更しよう&lt;br /&gt;
今までのコードは &lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html&quot;&gt;BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト&lt;/a&gt; を参照&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;UnitTest/Util/NumericTest.hs&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;-- ...
  testCase &amp;quot;y = 0&amp;quot; -: ((Left DivideByZero) &amp;#64;=? (1 `quotF` 0)
, testCase &amp;quot;0 &amp;lt; y &amp;lt; x&amp;quot; -: ((Right 3) &amp;#64;=? (4.5 `quotF` 1.5))
-- ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;こんな風に 「ゼロ除算をしていたら &lt;code&gt;(Left DivideZero)&lt;/code&gt;, していなければ &lt;code&gt;(Right &amp;lt;期待する計算結果の値&amp;gt;)&lt;/code&gt;」という風にテストコードを書き換えていく．&lt;br /&gt;
QuickCheck の為のテストコードも以下のように変更する&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- ...
prop_quotRemF :: Double -&amp;gt; Double -&amp;gt; Bool
prop_quotRemF x y = case x `quotRemF` y of
  Left e -&amp;gt; y == 0 &amp;amp;&amp;amp; e == DivideByZero
  Right (q, r) -&amp;gt;
      (x .= q * y + r) 1e-5
    &amp;amp;&amp;amp; 0 &amp;lt;= r
    &amp;amp;&amp;amp; r &amp;lt; abs y
-- ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;こちらは，&lt;/p&gt;&lt;pre&gt;&lt;code&gt;x&lt;/code&gt; と &lt;code&gt;y&lt;/code&gt; を &lt;code&gt;quotRemF&lt;/code&gt; の引数に渡し，結果が
&lt;code&gt;Left e&lt;/code&gt; であれば -&amp;gt;
  &lt;code&gt;y&lt;/code&gt; は必ず &lt;code&gt;0&lt;/code&gt; で，&lt;code&gt;e&lt;/code&gt; は &lt;code&gt;DivideByZero&lt;/code&gt;
&lt;code&gt;Right (q, r)&lt;/code&gt; であれば -&amp;gt;...&lt;/pre&gt;&lt;p&gt;という風に読めるはず．&lt;/p&gt;&lt;p&gt;さきほどの &lt;code&gt;evaluate, try&lt;/code&gt; で 例外をやりこめてテストするよりも綺麗だしわかりやすくなる．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;再度検証して，実装を修正&lt;/h3&gt;&lt;p&gt;そしてテストを実行しようとすると，確実にコンパイルエラーで実行さえ出来なくなっているはず．&lt;/p&gt;&lt;p&gt;TDD / BDD に馴染みの無い人だったら，ここでかなりの抵抗感があるだろうけど，こうやって「テストから手を加える」→ 「テストを何かしら失敗する」→ 「テストを成功させるよう実装を修正する」は大事なファクターなので徹底しよう．&lt;/p&gt;&lt;p&gt;間違いの存在を見つけようとするよりも，間違いを正そうとする方がかかる労苦は圧倒的に少なく，そして確実に実りがある&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;Util/Numeric.hs&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;import Control.Exception ( ArithException(..) )
import Control.Monad.Instances

quotRemF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; Either ArithException (a, b)
quotRemF _ 0 = divZeroError
quotRemF x y = Right $ iter x y 0 where
 -- ...

quotF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; Either ArithException a
quotF x y = fmap fst $ quotRemF x y

remF :: (Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; Either ArithException b
remF x y = fmap snd $ quotRemF x y

-- private functions
divZeroError :: Either ArithException a
divZeroError = Left DivideByZero
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;前回までのコードに対して，変更するのはこれだけ．やった事は&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;quotRemF, quotF, remF&lt;/code&gt; の返す値を &lt;code&gt;Either ArithException &amp;lt;a&amp;gt;&lt;/code&gt; に変更&lt;/li&gt;
&lt;li&gt;&lt;code&gt;divZeroError&lt;/code&gt; の 実装を &lt;code&gt;throw DivideByZero&lt;/code&gt; から &lt;code&gt;Left DivideByZero&lt;/code&gt; に変更&lt;/li&gt;
&lt;li&gt;&lt;code&gt;quotRemF&lt;/code&gt; の成功した場合の結果に &lt;code&gt;Right&lt;/code&gt; コンストラクタを加えただけ&lt;/li&gt;
&lt;li&gt;&lt;code&gt;quotF, remF&lt;/code&gt; は &lt;code&gt;fmap&lt;/code&gt; を加えただけ&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fmap&lt;/code&gt; の性質を利用して &lt;code&gt;quotF, remF&lt;/code&gt; の条件分岐は省略できた&lt;/li&gt;
&lt;/ol&gt;&lt;pre&gt;&lt;code&gt;fmap f $ Left x  = Left x
fmap f $ Right x = Right $ f x&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Control.Monad.Instances&lt;/code&gt; モジュールをロードした後は &lt;code&gt;Either&lt;/code&gt; 型は &lt;code&gt;Functor&lt;/code&gt; のインスタンスになるので， &lt;code&gt;fmap&lt;/code&gt; を適用すると上記のような処理になる．&lt;code&gt;Left Error&lt;/code&gt; ならそのまま何もせず，&lt;code&gt;Right x&lt;/code&gt; であれば内部の &lt;code&gt;x&lt;/code&gt; に &lt;code&gt;f&lt;/code&gt; を適用して， &lt;code&gt;Right&lt;/code&gt; に包み直す&lt;/p&gt;&lt;p&gt;コンパイルエラーで何も出来なくなって焦ったかもしれないけど，修正はごくわずかで済んだ&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;テストしてみよう&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ runhaskell UnitTest/Util/NumericTest.hs
quot and rem for Fractional:
  example:
    y == 0: [OK]
    x = qy: [OK]
    x = qy + r: [OK]
    x &amp;lt; 0, 0 &amp;lt; y: [OK]
    x &amp;lt; 0, y &amp;lt; 0: [OK]
    0 &amp;lt; x, y &amp;lt; 0: [OK]
    0 &amp;lt; x, 2 &amp;lt; y &amp;lt; 3: [OK]
    x &amp;lt; 0, 2 &amp;lt; y &amp;lt; 3: [OK]
    2 &amp;lt; y &amp;lt; 3, 4 &amp;lt; x &amp;lt; 5: [OK]
  specification:
    x = qy + r (0 &amp;lt;= r &amp;lt; |y|): [OK, passed 100 tests] #|a|
    quotF = fst (q, r): [OK, passed 100 tests]
    remF  = snd (q, r): [OK, passed 100 tests]

         Properties  Test Cases  Total
 Passed  3           9           12
 Failed  0           0           0
 Total   3           9           12
&lt;/pre&gt;&lt;p&gt;ちゃんとテストに成功している事がわかる&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;まとめ&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;モジュールの仕様を変更する時はテストを直す&lt;/li&gt;
&lt;li&gt;テストに通るようモジュールを修正する&lt;/li&gt;
&lt;li&gt;IO を併わないモジュールは &lt;code&gt;Either &amp;lt;Error&amp;gt; &amp;lt;Success&amp;gt;&lt;/code&gt; 型パターンで&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;これが今回のまとめ．次回はリファクタリングとパフォーマンスチューニング&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;おまけ&lt;/h3&gt;&lt;p&gt;よく考えてみたら この連載用にソースを GitHub に上げて，コミットのチェンジログを貼るのが一番良いような気がした…&lt;/p&gt;&lt;p&gt;再現するの時間かかるけどそっちの方が見やすいわな…&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/6270755379326372064/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-3.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/6270755379326372064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/6270755379326372064'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-3.html' title='BDD on Haskell チュートリアル その3-3 : 仕様変更はテストから'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-5197561177077885018</id><published>2012-01-06T12:44:00.000+09:00</published><updated>2012-01-06T13:44:42.475+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その3-2 : throw Exception を使わず基本は Either を使おう</title><content type='html'>&lt;p&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html&quot;&gt;前回&lt;/a&gt;の続き．前回までのエントリーはこちら&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html&quot;&gt;BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html&quot;&gt;BDD on Haskell チュートリアル その3-1 : catch できない哀しみ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h3&gt;「どちらか」を表現する &lt;code&gt;Either&lt;/code&gt; 型&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Control.Exception&lt;/code&gt; の &lt;code&gt;try catch finally&lt;/code&gt; はそもそも &lt;code&gt;IO&lt;/code&gt; モナドで利用される事を想定している．つまり 外部からの入力や Haskell の実行結果を出力する境界で発生する予期しない事態に対応する為のものだ．&lt;/p&gt;&lt;p&gt;なので，IO に本当は依存してないはずの論理計算や算術演算で Haskell の &lt;code&gt;try throw&lt;/code&gt; をもっぱら使うのは妙な話．ではどうすれば良いか．&lt;/p&gt;&lt;p&gt;例えば &lt;code&gt;x / 0&lt;/code&gt; は数学的に無意味，または未定義とされている．あともっと具体的な例だと，色彩計画における HSL, HSV 色空間の無彩色(彩度が0%，黒から灰，白の色彩の無い色) での色相の角度 (Hue) なども実際は未定義の値．&lt;br /&gt;
このような「何かを求めるが，場合によっては別のモノを結果として出さないといけないかも」という計算を型で表現するには &lt;code&gt;Either&lt;/code&gt; 型を使う．&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;data Either a b = Left a | Right b             -- Defined in Data.Either
instance (Eq a, Eq b) =&amp;gt; Eq (Either a b)       -- Defined in Data.Either
instance (Ord a, Ord b) =&amp;gt; Ord (Either a b)    -- Defined in Data.Either
instance (Read a, Read b) =&amp;gt; Read (Either a b) -- Defined in Data.Either
instance (Show a, Show b) =&amp;gt; Show (Either a b) -- Defined in Data.Either
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Either&lt;/code&gt; は「この関数は任意型 &lt;code&gt;a&lt;/code&gt; か任意型 &lt;code&gt;b&lt;/code&gt; を返す」という事を表現できる．慣例として，よく &lt;code&gt;Left&lt;/code&gt; コンストラクタにエラーや未定義を示す型を， &lt;code&gt;Right&lt;/code&gt; コンストラクタに正常動作した場合の結果を返すように利用する．&lt;/p&gt;&lt;p&gt;&lt;code&gt;throw Exception&lt;/code&gt; のパターンを使って，特定の条件で例外を投げるようにするよりも，最初から返り値を &lt;code&gt;Either&lt;/code&gt; 型で宣言した方が，ライブラリの利用者にとって「あ，この関数は場合によってエラーになりうるんだな」という事が明確になる．&lt;/p&gt;&lt;p&gt;前回で取り上げた&lt;code&gt;Control.Exception&lt;/code&gt; の &lt;code&gt;try&lt;/code&gt; は，汎用的に その関数の評価結果を エラー か 正常動作の結果かを &lt;code&gt;Either&lt;/code&gt; モナドで包む関数となっている．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;&lt;code&gt;throw&lt;/code&gt; しないで &lt;code&gt;Either&lt;/code&gt; で異常値，未定義値を包む&lt;/h3&gt;&lt;p&gt;前回哀しい事実をつきつけてくれた&lt;code&gt;Data.Fixed&lt;/code&gt; の &lt;code&gt;div&#39; mod&#39;&lt;/code&gt; のあの憎い感じを &lt;code&gt;Either&lt;/code&gt; で解決してみよう&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- Sample.Fixed.hs

module Sample.Fixed where

import qualified Data.Fixed as F
import Control.Exception
import Prelude hiding ( div, mod, divMod )
import qualified GHC.Real as R

divZero :: Either ArithException a
divZero = Left DivideByZero

div&#39; :: (Real a, Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; Either ArithException b
div&#39; _ 0 = divZero
div&#39; x y = Right $ F.div&#39; x y

mod&#39; :: Real a =&amp;gt; a -&amp;gt; a -&amp;gt; Either ArithException a
mod&#39; _ 0 = divZero
mod&#39; x y = Right $ F.mod&#39; x y

divMod&#39; :: (Real a, Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; Either ArithException (b, a)
divMod&#39; = pairEither div&#39; mod&#39; divZero

div :: Integral a =&amp;gt; a -&amp;gt; a -&amp;gt; Either ArithException a
div _ 0 = divZero
div x y = Right $ R.div x y

mod :: Integral a =&amp;gt; a -&amp;gt; a -&amp;gt; Either ArithException a
mod _ 0 = divZero
mod x y = Right $ R.mod x y

divMod :: Integral a =&amp;gt; a -&amp;gt; a -&amp;gt; Either ArithException (a, a)
divMod = pairEither div mod divZero

pairEither f g e x y = case (f x y, g x y) of
  (Right n, Right m) -&amp;gt; Right (n, m)
  _ -&amp;gt; e
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これで，&lt;code&gt;div div&#39;&lt;/code&gt; はそれぞれ，ゼロ除算に至った場合 &lt;code&gt;ArithException&lt;/code&gt; を持つ &lt;code&gt;Either&lt;/code&gt; 型の関数になった．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;Either と Maybe&lt;/h3&gt;&lt;p&gt;Either 型と Maybe は似てる．違いは&lt;/p&gt;&lt;dl&gt;&lt;dt&gt;Either&lt;/dt&gt;
&lt;dd&gt;「B か A か」&lt;/dd&gt;
&lt;dt&gt;Maybe&lt;/dt&gt;
&lt;dd&gt;「何も無い か A」&lt;/dd&gt; &lt;/dl&gt;&lt;p&gt;二つとも 「期待しているのが無ければ引数のデフォルトを使って，任意型&lt;code&gt;a&lt;/code&gt; の値を返す」関数が用意されている． &lt;code&gt;either&lt;/code&gt; と &lt;code&gt;maybe&lt;/code&gt; だ&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- Data.Either
either :: (b -&amp;gt; c) -&amp;gt; (a -&amp;gt; c) -&amp;gt; Either b a -&amp;gt; c

either (const 0) id $ Left &amp;quot;error&amp;quot; -- 0
either (const 0) id $ Right 10     -- 10

-- やってることはこれと同じ
myEither f g (Left x)  = f x
myEither f g (Right x) = g x


-- Data.Maybe
maybe :: b -&amp;gt; (a -&amp;gt; b) -&amp;gt; Maybe a -&amp;gt; b

-- Data.List
find :: (a -&amp;gt; Bool) -&amp;gt; [a] -&amp;gt; Maybe a

maybe (-1) id $ find (&amp;lt; 2) [3..100] -- -1
maybe (-1) id $ find (&amp;lt; 4) [3..100] --  3

-- やってることはこれと同じ
myMaybe y f Nothing  = y
myMaybe y f (Just x) = f x
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;「試しにやってみて，失敗したらデフォルト値でいいや」っていう場合ならこの関数で &lt;code&gt;Either&lt;/code&gt; や &lt;code&gt;Maybe&lt;/code&gt; で包んだ元の型が取れる&lt;/p&gt;&lt;p&gt;&lt;code&gt;Control.Monad.Instances&lt;/code&gt; モジュールをインポートすれば，&lt;code&gt;Either&lt;/code&gt; も &lt;code&gt;Maybe&lt;/code&gt; も &lt;code&gt;Monad , Functor&lt;/code&gt; のインスタンスとして利用できる. つまり &lt;code&gt;Either&lt;/code&gt; も &lt;code&gt;Maybe&lt;/code&gt; もモナドの一つだし，ファンクタ でもある&lt;/p&gt;&lt;pre&gt;&lt;code&gt;find (&amp;lt; 2) [3..100] &amp;gt;&amp;gt;= return . (\x -&amp;gt; x + x) -- Nothing
find (&amp;lt; 4) [3..100] &amp;gt;&amp;gt;= return . (\x -&amp;gt; x + x) -- Just 6

fmap (\x -&amp;gt; x + x) $ find (&amp;lt; 2) [3..100] -- Nothing
fmap (\x -&amp;gt; x + x) $ find (&amp;lt; 4) [3..100] -- Just 6

Left &amp;quot;error&amp;quot; &amp;gt;&amp;gt;= return . (\x -&amp;gt; x + x) -- Left &amp;quot;error&amp;quot;
Right 3 &amp;gt;&amp;gt;= return . (\x -&amp;gt; x + x)      -- Right 6

fmap (\x -&amp;gt; x + x) $ Left &amp;quot;error&amp;quot;  -- Left &amp;quot;error&amp;quot;
fmap (\x -&amp;gt; x + x) $ Right 3       -- Right 6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;「試しにやってみて，失敗しても今はスルーして処理続行させよう」という場合ならこの関数で，型の情報そのままに正常系動作の継続処理を記述できる&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;例外 とか 例外処理とか要らなかったのでは&lt;/h3&gt;&lt;p&gt;このように &lt;code&gt;IO&lt;/code&gt; の境界の内側での処理を &lt;code&gt;Maybe&lt;/code&gt; や &lt;code&gt;Either&lt;/code&gt; で 「失敗もする関数の型」として扱う事で他の言語のような 「どこで例外起きてるの…」と苦悩することなく「絶対うまくいく」処理と 「うまくいかないかも」という処理を型で表現できる．&lt;/p&gt;&lt;p&gt;そして，「うまくいかないかも」という処理を型で表現した時に「うまくいかなかったら代替使う」「うまくいった場合にこれをさせる」という高階関数がすでに標準ライブラリで用意されてるので，これらを使って設計する事でハナから「例外とか無くて良いのでは」と思えるほど余計なエラーでストレスなくプログラミングが出来る．&lt;/p&gt;&lt;p&gt;次回は &lt;code&gt;Either&lt;/code&gt; を使って，今まで書いた &lt;code&gt;quot&#39; rem&#39;&lt;/code&gt; の テストと実装を改良する&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/5197561177077885018/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5197561177077885018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5197561177077885018'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html' title='BDD on Haskell チュートリアル その3-2 : throw Exception を使わず基本は Either を使おう'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-8249375446998711337</id><published>2012-01-06T12:30:00.000+09:00</published><updated>2012-01-21T17:36:26.364+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その3-1 : catch できない哀しみ</title><content type='html'>&lt;p&gt;前回までのエントリーはこちら&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html&quot;&gt;BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;前回までで，ほぼ TDD BDD の基本は書いたのだけど，今回は例外について．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;Haskell の例外処理&lt;/h3&gt;&lt;p&gt;Haskell も普通の言語よろしく，例外処理は &lt;code&gt;throw try catch&lt;/code&gt; 使える．けれど &lt;code&gt;IO&lt;/code&gt; が絡んでくる場合でないとあまり有用ではない印象．&lt;/p&gt;&lt;h4&gt;throw try catch&lt;/h4&gt;&lt;p&gt;Haskell の &lt;code&gt;try catch throw finally&lt;/code&gt; は &lt;code&gt;Control.Exception&lt;/code&gt; モジュールで定義されている．それぞれの型はこのようになっている&lt;/p&gt;&lt;pre&gt;&lt;code&gt;try :: Exception e =&amp;gt; IO a -&amp;gt; IO (Either e a)

-- Prelude で宣言されてる catch
catch :: IO a -&amp;gt; (IOError -&amp;gt; IO a) -&amp;gt; IO a

-- Control.Exception で宣言されてる catch
catch :: Exception e =&amp;gt; IO a -&amp;gt; (e -&amp;gt; IO a)

throw :: Exception e =&amp;gt; e -&amp;gt; a

finally :: IO a -&amp;gt; IO b -&amp;gt; IO a

evaluate :: a -&amp;gt; IO a
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;こんな風に使う&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;-- Sample/TryCatchArith.hs
module Sample.TryCatchArith where

import Control.Exception
import Prelude hiding ( catch ) -- Prelude の catch は不可視にしておく

-- ゼロ除算例外を投げる場合
divZeroError = throw DivideByZero

-- スローされた例外を catch したい場合
catchArith :: Num a =&amp;gt; a -&amp;gt; a -&amp;gt; IO a
catchArith proc alt = catch (evaluate proc) handler
  where
    handler e = print (e :: ArithException) &amp;gt;&amp;gt; return alt

-- Haskell 的 try
tryArith :: Num a =&amp;gt; a -&amp;gt; IO (Either ArithException a)
tryArith proc = try (evaluate proc)
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;&lt;code&gt;try catch&lt;/code&gt; はそれぞれ &lt;code&gt;IO&lt;/code&gt; モナド を引数に取って， 規定の例外型 &lt;code&gt;Exception&lt;/code&gt; が投げられた時に何かしらのハンドリングをする．&lt;/p&gt;&lt;p&gt;&lt;code&gt;catch&lt;/code&gt; はユーザー側でその時の処理を関数で渡して，&lt;code&gt;IO&lt;/code&gt; モナドの中身の型と同じ型の代替値を返せば良く， &lt;code&gt;try&lt;/code&gt; は &lt;code&gt;Either&lt;/code&gt; モナドに包んで，例外だったら &lt;code&gt;Left&lt;/code&gt; へ，正常に動作した結果であれば &lt;code&gt;Right&lt;/code&gt; に値を格納する． &lt;code&gt;Either&lt;/code&gt; については後述．&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;試してみる&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci Sample.TryCatchArith
*Sample.TryCatchArith&amp;gt; catchArith ( 1 `div` 0 ) 0
divide by zero
0
*Sample.TryCatchArith&amp;gt; catchArith ( 6 `div` 3 ) 0
2
*Sample.TryCatchArith&amp;gt; tryArith $ 1 `div` 0
Left divide by zero
*Sample.TryCatchArith&amp;gt; tryArith $ 6 `div` 3
Right 3
*Sample.TryCatchArith&amp;gt; divZeroError
*** Exception: divide by zero
*Sample.TryCatchArith&amp;gt; :t divZeroError
divZeroError :: a
&lt;/pre&gt;&lt;p&gt;Haskell の &lt;code&gt;try catch throw&lt;/code&gt; はいずれも単なる関数で，他の言語のようなシンタックスキーワードではない． 経緯として &lt;code&gt;Control.Exception&lt;/code&gt; は後発のモジュールだったりする．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;&lt;code&gt;Data.Fixed div&#39; mod&#39;&lt;/code&gt; に見る &lt;code&gt;try catch&lt;/code&gt; の問題点&lt;/h3&gt;&lt;p&gt;Haskell &lt;code&gt;base&lt;/code&gt; ライブラリには浮動小数点小数の型向けの余剰を計算するモジュールがある． &lt;code&gt;Data.Fixed&lt;/code&gt; モジュールの &lt;code&gt;div&#39;, mod&#39;, divMod&#39;&lt;/code&gt; がそれにあたる．この関数の挙動は Scheme RSR6 の &lt;code&gt;div0-and-mod0&lt;/code&gt; と同じ．つまり，余剰の絶対値が0により近い方で計算をする．&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci
Prelude&amp;gt; :m + Data.Fixed
Prelude Data.Fixed&amp;gt; :t div&#39;
div&#39; :: (Real a, Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; b
Prelude Data.Fixed&amp;gt; :t mod&#39;
mod&#39; :: Real a =&amp;gt; a -&amp;gt; a -&amp;gt; a
Prelude Data.Fixed&amp;gt; :t divMod&#39;
divMod&#39; :: (Real a, Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; (b, a)
Prelude Data.Fixed&amp;gt; divMod&#39; 4.7 2.3
(2,0.10000000000000053)
Prelude Data.Fixed&amp;gt; divMod&#39; (-4.7) 2.3
(-3,2.1999999999999993)
Prelude Data.Fixed&amp;gt; divMod&#39; 4.7 (-2.3)
(-3,-2.1999999999999993)
Prelude Data.Fixed&amp;gt; divMod&#39; 1 0
(*** Exception: Ratio.%: zero denominator
&lt;/pre&gt;&lt;p&gt;divMod&#39; でのゼロ除算で投げられる例外は 単純にメッセージを投げられているので型が無い&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci
Prelude&amp;gt; :m + Data.Fixed
Prelude Data.Fixed&amp;gt; let x = 1 `div&#39;` 0
Prelude Data.Fixed&amp;gt; :t x
x :: Integer
Prelude Data.Fixed&amp;gt; x
*** Exception: Ratio.%: zero denominator
&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;ソース見てみる&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;http://hackage.haskell.org/packages/archive/base/4.2.0.1/doc/html/src/Data-Fixed.html#div%27&quot;&gt;最新の GHC のソース&lt;/a&gt;でどうなってるか見てみると&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- | generalisation of &#39;div&#39; to any instance of Real
div&#39; :: (Real a,Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; b
div&#39; n d = floor ((toRational n) / (toRational d))

-- | generalisation of &#39;divMod&#39; to any instance of Real
divMod&#39; :: (Real a,Integral b) =&amp;gt; a -&amp;gt; a -&amp;gt; (b,a)
divMod&#39; n d = (f,n - (fromIntegral f) * d) where
    f = div&#39; n d

-- | generalisation of &#39;mod&#39; to any instance of Real
mod&#39; :: (Real a) =&amp;gt; a -&amp;gt; a -&amp;gt; a
mod&#39; n d = n - (fromInteger f) * d where
    f = div&#39; n d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;div&#39;&lt;/code&gt; は 引数を &lt;code&gt;toRational&lt;/code&gt; で &lt;code&gt;Ratio&lt;/code&gt; 型に変換してから除算している. で， &lt;code&gt;Ratio&lt;/code&gt; 型の実装で， &lt;code&gt;(/)&lt;/code&gt; の 右辺に &lt;code&gt;0&lt;/code&gt; が渡された場合に&lt;/p&gt;&lt;pre&gt;&lt;code&gt;error &amp;quot;Ratio.%: zero denominator&amp;quot;&lt;/code&gt;
&lt;/pre&gt;&lt;p&gt;でエラーメッセージのみの例外がスローされている，という仕組み．この為標準の &lt;code&gt;ArithException&lt;/code&gt; で例外をキャッチできない (!!)&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci Sample.TryCatchArith
*Sample.TryCatchArith&amp;gt; tryArith $ 1 `div` 0 -- 例外キャッチできる
Left divide by zero
*Sample.TryCatchArith&amp;gt; :m + Data.Fixed
*Sample.TryCatchArith Data.Fixed&amp;gt; tryArith $ 1 `div&#39;` 0 -- 例外キャッチできない
*** Exception: Ratio.%: zero denominator
&lt;/pre&gt;&lt;p&gt;そういうの，ほんとやめてほしい．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;例外処理はテストしずらい&lt;/h3&gt;&lt;p&gt;上記の例で見てわかる通り， &lt;code&gt;throw&lt;/code&gt; や &lt;code&gt;error&lt;/code&gt; でもって例外を投げるのは書く方こそ簡単ではあるけれど，利用側にとっては&lt;/p&gt;&lt;ol&gt;&lt;li&gt;ソース読まない限り例外が来るかどうかわからない&lt;/li&gt;
&lt;li&gt;遅延評価で，その値が必要な時が来るまで例外発生しない&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;という問題がある．たとえば開発中にどこかで &lt;code&gt;Exception&lt;/code&gt; でひっかかって，調査してみようとして REPL で適当に関数を叩いたりしても，&lt;/p&gt;&lt;ol&gt;&lt;li&gt;型情報に例外が明記されないので例外の型がわからない&lt;/li&gt;
&lt;li&gt;どんな例外型が来るか，プログラマが頭で推定して対処しないといけない&lt;/li&gt;
&lt;li&gt;例外が投げられている関数がコールスタックの深部にあると探すのが大変&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;などが考えられる．あと単純にテストコードも &lt;code&gt;try&lt;/code&gt; 使ったりして面倒この上ない&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;というわけで，&lt;a href=&quot;http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-2-throw-exception.html&quot;&gt;次回は「テストしやすくする為の」&lt;code&gt;throw Exception&lt;/code&gt;の代替として &lt;code&gt;Either&lt;/code&gt;型を使ってモナディックに解決する&lt;/a&gt;という事をやる．&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;おお，単純なライブラリ開発を初心者向けに書いてみるって趣旨でちゃんとモナド出てきたぞ&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/8249375446998711337/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/8249375446998711337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/8249375446998711337'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-3-1-catch.html' title='BDD on Haskell チュートリアル その3-1 : catch できない哀しみ'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-2512440441167052915</id><published>2012-01-01T08:00:00.000+09:00</published><updated>2012-01-04T18:29:08.193+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="HUnit"/><category scheme="http://www.blogger.com/atom/ns#" term="QuickCheck"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト</title><content type='html'>&lt;h3&gt;新年のご挨拶&lt;/h3&gt;&lt;p&gt;あけましておめでとうございます．&lt;/p&gt;&lt;p&gt;2012年は Schemer, Haskeller にとって飛躍の年でありますよう心から願う所存であります．&lt;/p&gt;&lt;p&gt;デザインについてはあと最終勧告まで2年を切った HTML 5 がそびえたつクソにならない事を切に祈り，ユーザビリティ，アクセシビリティ，ユースケース，UX をガン無視した「CSS3だけで出来たなんちゃらかんちゃら」「美麗なビジュアルエフェクトを実現する jQuery プラグイン」で衆目を集めてなんちゃってクリエイティブ気分を味わってる人たちが滅亡してくれる事を期待しています&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;前回までのエントリー&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;BDD on Haskell チュートリアル その1 : HUnit で TDD を&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;今回は QuickCheck を使ってランダムテストを取り入れた BDD について&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;前回までのおさらい&lt;/h3&gt;&lt;p&gt;前回までのエントリーで，プロジェクトはこのようになった．テスト結果を受けて型情報を追加していたり，テストコードを読みやすくする為のユーティリティ演算子を追加している．&lt;/p&gt;&lt;h4&gt;ソースツリー&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;app/
 |- src/
 |   |- Util/
 |   |   `- Numeric.hs
 |   `- UnitTest/
 |       `- Util/
 |           `- NumericTest.hs
 |- app.cabal
 |- Setup.hs
 |- LICENCE
 `- README/
&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;app/src/Util/Numeric.hs&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;module Util.Numeric where

quotRemF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; (a, b)
quotRemF x y = iter x y 0 where
 iter r k q
  | r == 0 = (q, 0)
  | 0 &amp;lt; r &amp;amp;&amp;amp; r &amp;lt; abs k = (q, r)
  | otherwise = iter (r `step` k) k (next q)
 step a b
  | x * y &amp;gt;= 0 = a - b
  | otherwise  = a + b
 next n
  | x * y &amp;gt;= 0 = n + 1
  | otherwise  = n - 1

quotF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; a
quotF x y = fst $ quotRemF x y

remF :: (Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; b
remF x y = snd $ quotRemF x y
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;app/src/UnitTest/NumericTest.hs&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;module UnitTest.Util.Numeric where

import Test.Framework ( defaultMain, testGroup )
import Test.Framework.Providers.HUnit ( testCase )
import Test.HUnit ( (&amp;#64;=?) )

import Util.Numeric

main = defaultMain testSuite

testSuite = [
    &amp;quot;quot and rem for Fractional&amp;quot; /: [
      &amp;quot;example&amp;quot; /: [
          &amp;quot;x = qy&amp;quot;           -: (( 12, 0)  &amp;#64;=? (  120  `quotRemF` 10))
        , &amp;quot;x = qy + r&amp;quot;       -: (( 12, 7)  &amp;#64;=? (  127  `quotRemF` 10))
        , &amp;quot;x &amp;lt; 0, 0 &amp;lt; y&amp;quot;     -: ((-13, 7)  &amp;#64;=? ((-123) `quotRemF` 10))
        , &amp;quot;x &amp;lt; 0, y &amp;lt; 0&amp;quot;     -: (( 13, 7)  &amp;#64;=? ((-123) `quotRemF` (-10)))
        , &amp;quot;0 &amp;lt; x, y &amp;lt; 0&amp;quot;     -: ((-12, 3)  &amp;#64;=? (  123  `quotRemF` (-10)))
        , &amp;quot;0 &amp;lt; x, 2 &amp;lt; y &amp;lt; 3&amp;quot; -: (( 1, 0.5) &amp;#64;=? (    3  `quotRemF` 2.5))
        , &amp;quot;x &amp;lt; 0, 2 &amp;lt; y &amp;lt; 3&amp;quot; -: ((-1, 0.5) &amp;#64;=? (  (-2) `quotRemF` 2.5))
      ]
    ]
  ]

(-:) = testCase
(/:) = testGroup
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;単体テスト実行&lt;/h4&gt;&lt;p&gt;コンソールで&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ cd ~/app/src/
~/app/src/
$ runhaskell ./UnitTest/Util/NumericTest.hs
quot and rem for Fractional:
  example:
    x = qy: [OK]
    x = qy + r: [OK]
    x &amp;lt; 0, 0 &amp;lt; y: [OK]
    x &amp;lt; 0, y &amp;lt; 0: [OK]
    0 &amp;lt; x, y &amp;lt; 0: [OK]
    0 &amp;lt; x, 2 &amp;lt; y &amp;lt; 3: [OK]
    x &amp;lt; 0, 2 &amp;lt; y &amp;lt; 3: [OK]

         Properties  Test Cases  Total
 Passed  0           7           7
 Failed  0           0           0
 Total   0           7           7
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;「ユニットテストで自分で考えてたユースケースは全部テストしたよ!」&lt;/h3&gt;&lt;p&gt;今回の主なテーマは↑のような，開発者が TDD でありがちな油断，ケアレスミスに対して，ランダムテストは有効かつ，プロダクトコードをより堅牢にするよ，という話．&lt;/p&gt;&lt;p&gt;勘のいい，というかマトモに開発をやってた人だったら，Haskell に馴染みが無くても，現状の実装には抜けがあるのに気づく．&lt;/p&gt;&lt;p&gt;例えば...&lt;/p&gt;&lt;ol&gt;&lt;li&gt;商を計算する関数なのに，ゼロ除算の事を考えてない&lt;/li&gt;
&lt;li&gt;浮動小数点小数を扱っているのに，誤差について考えてない&lt;/li&gt;
&lt;li&gt;INT_MAX と INT_MIN とかが引数に入った場合大丈夫なの?&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;などなど．まだまだ「これで十分テストはしてあるよ」とは言いづらい実装である．けれど，ここで2つ問題．&lt;/p&gt;&lt;ul&gt;&lt;li&gt;「どこまでやったら大丈夫だって言えるの?」&lt;/li&gt;
&lt;li&gt;「開発者が具体的なテスト例を思いつくのを待つの?」&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;h4&gt;どこまでやったら大丈夫だって言えるの?&lt;/h4&gt;&lt;p&gt;上記の問題が実はあるけれど，カバレッジをとってみると &lt;code&gt;quotRemF&lt;/code&gt; に関しては実はもうカバレッジが 100% になっている．&lt;/p&gt;&lt;p&gt;カバレッジレポート 100% はあくまで&lt;strong&gt;記述したソースコードの中の条件分岐やループを評価したか否か&lt;/strong&gt;であって，&lt;strong&gt;異常系と正常系を網羅したという事ではない．&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;だから開発者はテストを充実させていくわけだけど，ある意味で「キリが無い」といえばキリが無い&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;開発者が具体的なテスト例を思いつくのを待つの?&lt;/h4&gt;&lt;p&gt;だから開発手法としてペアプログラミングだったり，コードリーディングをプラクティスとして取り込んで，多様な視点を持ってコードを精査する事で上記の問題を克服してきたのだけど，それもそれで限界があって．&lt;/p&gt;&lt;p&gt;例えば，誰も思いつかなかったらやはり問題は「実際にプロダクトを動かした後で顕在化」するだろうし，その対策として「経験や知識を積め」というのは科学的手法では無くて，割とスピリチュアルな結論である．&lt;/p&gt;&lt;p&gt;ちゃんとコードの問題はコードで解決したいし，そう出来た方がモダンなエンジニアリングだと言える&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;ランダムテストを導入しよう&lt;/h3&gt;&lt;p&gt;前回の冒頭で，今実装したい &lt;code&gt;quotRemF&lt;/code&gt; は Scheme の RSR6 の &lt;code&gt;div-and-mod&lt;/code&gt; だと書いた．そして，その返り値の特性は以下の通り&lt;/p&gt;&lt;pre&gt;y != 0 の時
(div-and-mod x y) =&amp;gt; n m
x = y * n + m
0 &amp;lt;= m &amp;lt; |y|
&lt;/pre&gt;&lt;p&gt;つまり，どんな引数を取ろうが 上記の「性質」が担保できれば要求仕様を満足させた事になる．そして &lt;code&gt;y == 0&lt;/code&gt; の時に上記の性質が成立しない事も見てわかる．( &lt;code&gt;n&lt;/code&gt; にどんな数を代入しても &lt;code&gt;x = m&lt;/code&gt; となるので，&lt;code&gt;n&lt;/code&gt; は無限に選択がある. また， &lt;code&gt;x &amp;lt; 0&lt;/code&gt; であったら， &lt;code&gt;m&lt;/code&gt; の性質と矛盾する )&lt;/p&gt;&lt;p&gt;入力値は適当にコンピュータにまかせて，ランダムで引数をブチこんで，それでも常に上記の性質が成立しているのを確認できればよさそうである．ランダムだから運良くテストが失敗する例に当たるかどうかはわからない．けれど，100回，1000回試行したら，それに遭遇する確率は高くなるし，10000回試行してもテストが成功していたら，「ほぼほぼ大丈夫だ」という事が言えるのでは．&lt;/p&gt;&lt;p&gt;Haskell には &lt;a href=&quot;http://www.cse.chalmers.se/~rjmh/QuickCheck/&quot;&gt;QuickCheck&lt;/a&gt; という，こうした要求を叶えるランダムテストライブラリが用意されている．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;QuickCheck を使う&lt;/h3&gt;&lt;p&gt;テストに次のように &lt;code&gt;testProperty&lt;/code&gt; 関数を使ってランダムテスト用のテストを追加しよう&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- import モジュールを追加 ...
import Test.Framework.Providers.QuickCheck2 ( testProperty )
import Test.QuickCheck ( (==&amp;gt;), Property )
-- ...

  -- 略 ...
  &amp;quot;quot and rem for Fractional&amp;quot; /: [
      &amp;quot;example&amp;quot; /: [
        -- 略 ...
      ]
    , &amp;quot;specification&amp;quot; /: [
        testProperty &amp;quot;x = qy + r (0 &amp;lt;= r &amp;lt; |y|)&amp;quot; prop_quotRemF
      ]
    ]
  -- ...

  prop_quotRemF :: Double -&amp;gt; Double -&amp;gt; Property
  prop_quotRemF x y =
    y /= 0 ==&amp;gt;
    let (q, r) = x `quotRemF` y
    in x == q * y + r &amp;amp;&amp;amp; 0 &amp;lt;= r &amp;amp;&amp;amp; r &amp;lt; abs y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;prop_quotRemF&lt;/code&gt; は 「&lt;code&gt;Double&lt;/code&gt; 型の引数を二つ取って，&lt;code&gt;y&lt;/code&gt; が &lt;code&gt;0&lt;/code&gt; でない時なら，&lt;code&gt;quotRemF&lt;/code&gt; の返り値と &lt;code&gt;x&lt;/code&gt; と &lt;code&gt;y&lt;/code&gt; で以下のような関係が成立する」と読める．&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;テスト実行&lt;/h4&gt;&lt;p&gt;QuickCheck をよくわからない，ランダムテストってそもそも知らない人の為に，GHCi でどんな事をしてくれるのか見てみよう．&lt;/p&gt;&lt;br /&gt;
&lt;pre class=&quot;console&quot;&gt;$ ghci UnitTest/Util/NumericTest.hs
# ...
*UnitTest.Util.Numeric&amp;gt; :m + Test.QuickCheck
*UnitTest.Util.Numeric Test.QuickCheck&amp;gt; :t verboseCheck
verboseCheck :: Testable prop =&amp;gt; prop -&amp;gt; IO ()
*UnitTest.Util.Numeric Test.QuickCheck&amp;gt; verboseCheck prop_quotRemF
Skipped (precondition false):
0.0
0.0
Skipped (precondition false):
0.0
0.0
Passed:
-0.5785404360888579
1.4050607149068028
Passed:
7.0219393237978736
2.5971907610723477
Passed:
1.7946454896303736
-6.527731796226503
Passed:
-10.994215492628914
32.166637725883405
Failed:
5.877469666395628
-1.519039404965895
*** Failed! Passed:
5.0
-1.519039404965895
Failed:
5.877469666395628
1.519039404965895
Passed:
5.877469666395628
1.0
Falsifiable (after 5 tests and 1 shrink):
5.877469666395628
1.519039404965895
*UnitTest.Util.Sample Test.QuickCheck&amp;gt; # テストは失敗したらしい
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;verboseCheck&lt;/code&gt; は QuickCheck の基本テストの &lt;code&gt;quickCheck&lt;/code&gt; 関数の視覚化版で，実際にどんな入力値を与えているかを見る事ができる．&lt;/p&gt;&lt;p&gt;QuickCheck はどうやら，「テストプロパティの型情報から，その型の値をランダムに生成して突っ込みまくって，結果が &lt;code&gt;False&lt;/code&gt; だったらテストを失敗したとみなして中断，失敗が無かったらデフォルトの件数 &lt;code&gt;(n = 100)&lt;/code&gt; までテストを繰り返す」みたいだ．&lt;/p&gt;&lt;p&gt;テスト失敗時は QuickCheck の IO なのか，処理を並列化させている故かわからないけど，ヘンな位置でメッセージの差し込みが入るので，崩れるのだけど，これは ライブラリの開発者にがんばって修正してもらいたい…&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;ランダムテストの失敗を，ユニットテストに引き上げる&lt;/h4&gt;&lt;p&gt;さて，今の時点でテストはコケることがわかったわけだ&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ runhaskell UnitTest/Util/NumericTest.hs
quot and rem for Fractional:
  example:
    x = qy: [OK]
    x = qy + r: [OK]
    x &amp;lt; 0, 0 &amp;lt; y: [OK]
    x &amp;lt; 0, y &amp;lt; 0: [OK]
    0 &amp;lt; x, y &amp;lt; 0: [OK]
    0 &amp;lt; x, 2 &amp;lt; y &amp;lt; 3: [OK]
    x &amp;lt; 0, 2 &amp;lt; y &amp;lt; 3: [OK]
  specification:
4.7
2.3
    x = qy + r (0 &amp;lt;= r &amp;lt; |y|): [Failed]
Falsifiable with seed 3539781479325104925, after 5 tests. Reason: Falsifiable

         Properties  Test Cases  Total
 Passed  0           8           8
 Failed  1           0           1
 Total   1           8           9
&lt;/pre&gt;&lt;p&gt;コケた入力例は &lt;code&gt;x = 4.7, y = 2.3&lt;/code&gt; らしい，これをコピペして，&lt;code&gt;testCase&lt;/code&gt; に追加して検証してみよう．&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;quot;2 &amp;lt; y &amp;lt; 3, 4 &amp;lt; x &amp;lt; 5&amp;quot; -: ((2, 0.1) &amp;#64;=? (4.7 `quotRemF` 2.3))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;追加してテストを実行すると，その原因が推測できる&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;2 &amp;lt; y &amp;lt; 3, 4 &amp;lt; x &amp;lt; 5: [Failed]
Failed: expected: (2,0.1)
 but got: (2,0.10000000000000053)
&lt;/pre&gt;&lt;p&gt;はい，見事に 浮動小数点小数の演算による誤差問題でした．&lt;/p&gt;&lt;h4&gt;Nearly 型クラスを利用してテストに近似かどうかをチェックできるように&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;で実装した &lt;code&gt;Nearly&lt;/code&gt;型クラスを利用して HUnit のアサーション，QuickCheck の テストプロパティに組み込んでテストしてみよう．&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- UnitTest/Util/NumericTest.hs
-- ...
import Test.HUnit ( (&amp;#64;=?), assert ) -- assert のインポートを追加
-- ...

-- tv - tolerance value
tv :: Double
tv = 1e-5

-- tp - tolerance pair
tp :: (Double, Double)
tp = (1e-5, 1e-5)

testSuite = [
    &amp;quot;quot and rem for Fractional&amp;quot; /: [
        &amp;quot;example&amp;quot; /: [
         -- ...
         -- &amp;#64;=? の代わりに &amp;#64;.=? を使う
       , &amp;quot;2 &amp;lt; y &amp;lt; 3, 4 &amp;lt; x &amp;lt; 5&amp;quot;-: (((2, 0.1) &amp;#64;.=? ( 4.7  `quotRemF` 2.3)) tp)
       ]
       -- ...
     ]
  ]

-- prop_quotRemF を変更
prop_quotRemF :: Double -&amp;gt; Double -&amp;gt; Property
prop_quotRemF x y =
  y /= 0 ==&amp;gt;
  let (q, r) = x `quotRemF` y
  in
     (x .= (q * y + r)) 1e-5 -- 誤差 0.00001 以内で近似しているかどうか
  &amp;amp;&amp;amp; 0 &amp;lt;= r &amp;amp;&amp;amp; r &amp;lt; abs y
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;テストを実行してみる&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ runhaskell UnitTest/Util/NumericTest.hs
quot and rem for Fractional:
  example:
    x = qy: [OK]
    x = qy + r: [OK]
    x &amp;lt; 0, 0 &amp;lt; y: [OK]
    x &amp;lt; 0, y &amp;lt; 0: [OK]
    0 &amp;lt; x, y &amp;lt; 0: [OK]
    0 &amp;lt; x, 2 &amp;lt; y &amp;lt; 3: [OK]
    x &amp;lt; 0, 2 &amp;lt; y &amp;lt; 3: [OK]
    2 &amp;lt; y &amp;lt; 3, 4 &amp;lt; x &amp;lt; 5: [OK]
  specification:
    x = qy + r (0 &amp;lt;= r &amp;lt; |y|): [OK, passed 100 tests]

         Properties  Test Cases  Total
 Passed  1           8           9
 Failed  0           0           0
 Total   1           8           9
&lt;/pre&gt;&lt;p&gt;テストが全件通ったみたいだ. 見た目9件のテストなんだけど，実際には108件テストケースを噛ませた事になる. (QuickCheck のランダムテスト分)&lt;/p&gt;&lt;p&gt;ちなみに，書いてないけど &lt;code&gt;Nearly&lt;/code&gt; 型クラスについてもちゃんとテストを書かないとダメだよね．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;テスト済みの関数を基準にテストプロパティを書く&lt;/h3&gt;&lt;p&gt;&lt;code&gt;quotRemF&lt;/code&gt; の関数の性質がテストで保証されたので，同じ条件で &lt;code&gt;quotF, remF&lt;/code&gt; もそれぞれの性質が担保できる事をテストプロパティで示そう．&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- UnitTest/Util/NumericTest.hs
testSuite = [
  &amp;quot;quot and rem for Fractional&amp;quot; /: [
-- ...
    , &amp;quot;specification&amp;quot; /: [
        &amp;quot;x = qy + r (0 &amp;lt;= r &amp;lt; |y|)&amp;quot; *: prop_quotRemF
      , &amp;quot;quotF = fst (q, r)&amp;quot;        *: prop_quotF
      , &amp;quot;remF  = snd (q, r)&amp;quot;        *: prop_remF
      ]
    ]
  ]
-- ...

-- testProperty 用のユーティリティ演算子
(*:) = testProperty

-- 追加のテストプロパティ
prop_quotF :: Double -&amp;gt; Double -&amp;gt; Property
prop_quotF x y =
  y /= 0 ==&amp;gt;
  let (q, r) = x `quotRemF` y
  in q == x `quotF` y

prop_remF :: Double -&amp;gt; Double -&amp;gt; Property
prop_remF x y =
  y /= 0 ==&amp;gt;
  let (q, r) = x `quotRemF` y
  in r == x `remF` y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;要は「&lt;code&gt;quotF&lt;/code&gt; は &lt;code&gt;quotRemF&lt;/code&gt; の結果の，最初の要素と値が同じ， &lt;code&gt;remF&lt;/code&gt; は &lt;code&gt;quotRemF&lt;/code&gt; の最後の要素と値が同じ」というだけの事をテストしている．&lt;/p&gt;&lt;p&gt;こちらは別にユニットテストで沢山の具体的なテストをしなくても良く，&lt;code&gt;quotRemF&lt;/code&gt; の性能が保証されていれば，あとはテストプロパティが常に正しくテストに成功する事が確認できれば良い．&lt;/p&gt;&lt;p&gt;ポイントは，このテストのおかげで， &lt;code&gt;quotF&lt;/code&gt; &lt;code&gt;remF&lt;/code&gt; は &lt;code&gt;quotRemF&lt;/code&gt; に依存する実装じゃなくても良くなるという事．リファクタリング等で，&lt;code&gt;quotF&lt;/code&gt; を &lt;code&gt;quotRemF&lt;/code&gt; と独立した実装にしたいと考えた時に，このテストが生きていれば性能が担保できる．&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;テストを実行&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ runhaskell UnitTest/Util/NumericTest.hs
quot and rem for Fractional:
  example:
    x = qy: [OK]
    x = qy + r: [OK]
    x &amp;lt; 0, 0 &amp;lt; y: [OK]
    x &amp;lt; 0, y &amp;lt; 0: [OK]
    0 &amp;lt; x, y &amp;lt; 0: [OK]
    0 &amp;lt; x, 2 &amp;lt; y &amp;lt; 3: [OK]
    x &amp;lt; 0, 2 &amp;lt; y &amp;lt; 3: [OK]
    2 &amp;lt; y &amp;lt; 3, 4 &amp;lt; x &amp;lt; 5: [OK]
  specification:
    x = qy + r (0 &amp;lt;= r &amp;lt; |y|): [OK, passed 100 tests]
    quotF = fst (q, r): [OK, passed 100 tests]
    remF  = snd (q, r): [OK, passed 100 tests]

         Properties  Test Cases  Total
 Passed  3           8           11
 Failed  0           0           0
 Total   3           8           11
&lt;/pre&gt;&lt;p&gt;それぞれランダムテストで100遍やっても成功できているのが確認できた．&lt;br /&gt;
合計で 308 回テストした事になる&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;[わき道] 例外どうすんの&lt;/h3&gt;&lt;p&gt;残った問題は ゼロ除算だけになったんだけど，これについては 1エントリーちゃんと立てられるくらい話が出来るので，今回は「 QuickCheck でのテストプロパティを書いている時点で気づいて実装した 」 という事でお茶を濁す．&lt;/p&gt;&lt;p&gt;一旦， Util/Numeric.hs をこのように実装しなおす&lt;/p&gt;&lt;pre&gt;&lt;code&gt;module Util.Numeric (
  quotRemF,
  quotF,
  remF
  ) where

import Control.Exception ( throw, ArithException(..) )

quotRemF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; (a, b)
quotRemF _ 0 = divZeroError
quotRemF x y = iter x y 0 where
 iter r k q
  | r == 0 = (q, 0)
  | 0 &amp;lt; r &amp;amp;&amp;amp; r &amp;lt; abs k = (q, r)
  | otherwise = iter (r `step` k) k (next q)
 step a b
  | x * y &amp;gt;= 0 = a - b
  | otherwise  = a + b
 next n
  | x * y &amp;gt;= 0 = n + 1
  | otherwise  = n - 1

quotF :: (Num a, Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; a
quotF _ 0 = divZeroError
quotF x y = fst $ quotRemF x y

remF :: (Num b, Ord b) =&amp;gt; b -&amp;gt; b -&amp;gt; b
remF _ 0 = divZeroError
remF x y = snd $ quotRemF x y

-- private functions
divZeroError = throw DivideByZero
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これで， &lt;code&gt;quotRemF&lt;/code&gt;, &lt;code&gt;quotF&lt;/code&gt;, &lt;code&gt;remF&lt;/code&gt; の2番目の引数に0を渡した際には &lt;code&gt;DivideByZero Exception&lt;/code&gt; がスローされるようになる．&lt;/p&gt;&lt;p&gt;ただし，確かにこれでこのモジュールの関数は無限ループに陥って計算が止まらない等の異常は回避できているのだけど，ゼロ除算したら例外が投げられるという情報はライブラリのソースを見た人間にしかわからないし，テストでの確認もしずらい．&lt;/p&gt;&lt;p&gt;次の回で TDD/BDD とは 話題が違うのだけど， Haskell の例外について，より Haskeller っぽいアプローチでこれを解決しよう．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;ユニットテスト TDD =&amp;gt; ランダムテスト BDD&lt;/h3&gt;&lt;p&gt;ここまでが Haskell での TDD/BDD の基本ステップになる．&lt;/p&gt;&lt;ol&gt;&lt;li&gt;HUnit で，ユースケースを，テストケースとして書く&lt;/li&gt;
&lt;li&gt;テストに失敗してから，テストが通るように実装を進める&lt;/li&gt;
&lt;li&gt;HUnit の テストケースを増やす&lt;/li&gt;
&lt;li&gt;追加したテストに失敗したら実装を見直す&lt;/li&gt;
&lt;li&gt;思いつく限りのテストケースを書いては (1-4) の繰り返し&lt;/li&gt;
&lt;li&gt;QuickCheck のテストプロパティを書く&lt;/li&gt;
&lt;li&gt;テストプロパティで失敗した入力をメモしてテストケースに追加して検証する&lt;/li&gt;
&lt;li&gt;実装を見直す&lt;/li&gt;
&lt;li&gt;満足できるまで (6-8) の繰り返し&lt;/li&gt;
&lt;li&gt;リファクタリング&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;このワークフローは (1-5) までがいわゆる TDD で，(6-9) が BDD または QA テスト的な性格を持つ．&lt;/p&gt;&lt;p&gt;結果的に出来上がるテストコードは，こんなに有用なリソースになる&lt;/p&gt;&lt;ol&gt;&lt;li&gt;HUnit のテストケースで，入力と戻りの実例が列挙され，実装コードを見るより何をしてくれるかがわかりやすい&lt;/li&gt;
&lt;li&gt;QuickCheck でのテストプロパティは対象の関数の特性が一目瞭然となる&lt;/li&gt;
&lt;li&gt;テストコードがそのままライブラリのドキュメントとして役に立つ&lt;/li&gt;
&lt;li&gt;実行できる検証ツールとしても使える&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Haskeller の Hackage 公開ライブラリって驚くほどテスト書かないか，書いてはいるけど非公開にしているのが不思議でしょうがないのだけど，HDoc 書くよりよっぽどこのアプローチでテストコードしっかり充実させた方がライブラリ利用者にとってわかりやすいのではないかと思っている．&lt;/p&gt;&lt;p&gt;ということでみなさんちゃんとテストを書きましょう&lt;/p&gt;&lt;p&gt;次回は例外処理についてとリファクタリング&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;おまけ&lt;/h3&gt;&lt;h4&gt;QuickCheck みたいなのってどうせ Haskell () だけなんでしょw&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/QuickCheck&quot;&gt;Wikipedia でちょっと検索するだけで出てくる&lt;/a&gt;んだからちゃんと調べましょう&lt;/p&gt;&lt;table&gt;&lt;thead&gt;
&lt;tr&gt; &lt;th&gt;Lang&lt;/th&gt; &lt;th&gt;Library&lt;/th&gt; &lt;/tr&gt;
&lt;/thead&gt; &lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;C&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;https://github.com/mcandre/qc&quot;&gt;qc&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;C++&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://software.legiasoft.com/quickcheck/&quot;&gt;QuickCheck++&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ObjC&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;https://github.com/mcandre/objcheck&quot;&gt;objcheck&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CommonLisp&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://www.cliki.net/cl-quickcheck&quot;&gt;cl-quickcheck&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Java&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://java.net/projects/quickcheck/pages/Home&quot;&gt;QuickCheck for Java&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ruby&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://rushcheck.rubyforge.org/&quot;&gt;RushCheck&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Python&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://pypi.python.org/pypi/paycheck/&quot;&gt;paycheck&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Perl&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://search.cpan.org/~tmoertel/Test-LectroTest-0.3600/&quot;&gt;Test::LectroTest&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scala&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://code.google.com/p/scalacheck/&quot;&gt;ScalaCheck&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;JavaScript&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;https://bitbucket.org/darrint/qc.js/&quot;&gt;qc.js&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;node.js&lt;/td&gt; &lt;td&gt;&lt;a href=&quot;http://search.npmjs.org/#/quickcheck&quot;&gt;quickcheck&lt;/a&gt;&lt;/td&gt; &lt;/tr&gt;
&lt;/tbody&gt; &lt;/table&gt;&lt;p&gt;というか，ランダムテストの有用性と意義について &lt;a href=&quot;http://rushcheck.rubyforge.org/&quot;&gt;RushCheck&lt;/a&gt; の作者の ikemgami さんが5年以上も前にこういうエントリーを上げている．5年と半年経過した今だと，メジャーどころだと PHP 以外はみんなそれなりにランダムテストライブラリ公開されてる．もちろん，XUnit系に対してバージョンはとても若いのが多いけれど&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://madscientist.jp/~ikegami/diary/20060906.html&quot;&gt;プログラマがテストも書くならば lightweight なランダムテストを試すといいと思う&lt;/a&gt;&lt;/p&gt;&lt;p&gt;QuickCheck () , Haskell () とか失笑してないでちゃんと自分の第一言語の動向チェックしておくべきだし，ぜひランダムテストを導入しましょう&lt;/p&gt;&lt;p&gt;今回までに書いた事は何も Haskell 独特のモノではなくて，あらゆる言語で言える事だし，XUnit系のライブラリと QuickCheck 系のライブラリを共存できるラッパーフレームワークを書けば同じ事が出来るはず&lt;/p&gt;&lt;p&gt;そのうち XUnit と対比して XCheck なんてランダムテストのデザインパターンが提唱されそうね&lt;/p&gt;&lt;h3&gt;n参考リンク&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.cse.chalmers.se/~rjmh/QuickCheck/&quot;&gt;QuickCheck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/haskell.html&quot;&gt;Haskell の浮動小数点小数の同値比較について&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://madscientist.jp/~ikegami/diary/20060906.html&quot;&gt;プログラマがテストも書くならば lightweight なランダムテストを試すといいと思う&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/2512440441167052915/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/2512440441167052915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/2512440441167052915'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2012/01/bdd-on-haskell-2-quickcheck.html' title='BDD on Haskell チュートリアル その2 : QuickCheck でランダムテスト'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-3921196291089985315</id><published>2011-12-31T13:00:00.000+09:00</published><updated>2012-01-04T18:46:33.500+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><title type='text'>Haskell の浮動小数点小数の同値比較について</title><content type='html'>&lt;h3&gt;&lt;code&gt;4.7 - 2.3 * 2 == 0.1&lt;/code&gt; は &lt;code&gt;False&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;↑の式は，算数的には &lt;code&gt;True&lt;/code&gt; が返ってほしいもの．でも，プログラムの実行結果は &lt;code&gt;False&lt;/code&gt; が返ってきてしまう．原因は浮動小数点小数の演算の誤差によるもの．&lt;/p&gt;&lt;p&gt;小数を扱うライブラリを開発するにあたり，この手の小数の誤差問題はよく出てくるけれど，これに対処する為に3つアプローチがある．&lt;p&gt;&lt;ol&gt;&lt;li&gt;ライブラリ側で丸めてしまう&lt;/li&gt;
&lt;li&gt;ライブラリの利用側で丸める&lt;/li&gt;
&lt;li&gt;テストでのみ，「だいたい合ってる」というテストにする&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;(1) のアプローチはオススメしない．ライブラリで丸める，という事は，その丸め誤差をライブラリの利用者が知ってないといけないし，またライブラリの利用者はその丸め誤差を自分でコントロールできない．&lt;/p&gt;&lt;p&gt;また，この類は小数だけでなく，巨大な整数でもケースが考えられる．「だいたい百より下の桁の数は別にどうだっていい…」っていう時とかね．&lt;/p&gt;&lt;p&gt;だから (2),(3) のアプローチを取る．&lt;/p&gt;&lt;p&gt;テストの際に，誤差を与えて，そのテスト結果が誤差の範囲内で等しい，「nearly equal」という比較演算が出来る型クラスを定義しよう．汎用的な型クラスを定義すれば，リスト &lt;code&gt;[a]&lt;/code&gt; や タプル &lt;code&gt;(a, b)&lt;/code&gt; などの複合型にも対応できる．&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;Nearly 型クラス&lt;/h3&gt;&lt;p&gt;&lt;code&gt;isNearly&lt;/code&gt; を実装すれば，同じ型同士で近似しているかどうかが判定できる &lt;code&gt;Nearly&lt;/code&gt; 型クラスを作る．新たに Data, Data/Eq ディレクトリを作り，そこに Nearly.hs を書こう&lt;/p&gt;&lt;pre&gt;&lt;code&gt;  -- Data/Eq/Nearly.hs
  module Data.Eq.Nearly where

  class (Eq a, Ord a) =&gt; Nearly a where
    isNearly     :: a -&gt; a -&gt; a -&gt; Bool
    (.=)         :: a -&gt; a -&gt; (a -&gt; Bool)

    -- default Implementation
    x .= y = isNearly x y

  instance Nearly Int where
    isNearly = isNearlyNum

  instance Nearly Integer where
    isNearly = isNearlyNum

  instance Nearly Float where
    isNearly = isNearlyNum

  instance Nearly Double where
    isNearly = isNearlyNum

  isNearlyNum :: (Num a, Ord a) =&gt; a -&gt; a -&gt; a -&gt; Bool
  isNearlyNum x y t = abs (x - y) &lt;= abs t

  instance Nearly a =&gt; Nearly [a] where
    isNearly xs ys ts = and $ zipWith3 isNearly xs ys ts

  instance (Nearly a, Nearly b) =&gt; Nearly (a, b) where
    isNearly (p, q) (r, s) (t1, t2) =
      isNearly p r t1 &amp;&amp; isNearly q s t2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これで，この型クラスを定義しているモジュールをインポートすれば，&lt;code&gt;Int, Integer, Float, Double&lt;/code&gt; と，そのそれぞれの型のタプルとリストは近似している事を示す事ができる．&lt;/p&gt;&lt;p&gt;使い方は&lt;/p&gt;&lt;pre&gt;&lt;code&gt;  -- isNearly x y t -- t = x と y の誤差の許容上限
  isNearly 0.1 (4.7 - 4.6) 1e-5 -- True
  -- x .= y --「誤差の許容上限 t を引数に取って近似かどうかを返す関数」を返す
  (0.1 .= (4.7 - 4.6)) 1e-5     -- True

  -- 誤差±0 も出来る
  (1 .= 0.5 + 0.5) 0 -- True

  -- タプルも比較できる
  import Data.Fixed ( divMod&#39; )
  (((2, 0.1) .= (4.7 `divMod&#39;` 2.3)) (0, 1e-5)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;テストに利用する場合&lt;/p&gt;&lt;pre&gt;&lt;code&gt;  import Test.HUnit ( assert )

  x @.= y = assert . ( x .= y )

  ((1.01 @.= 1) 1e-1) -- Assertion (Test case will pass)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Data.Ratio&lt;/h3&gt;&lt;p&gt;Haskell の base ライブラリである &lt;a href=&quot;http://hackage.haskell.org/packages/archive/base/4.4.1.0/doc/html/Data-Ratio.html&quot;&gt;Data.Ratio&lt;/a&gt; には，任意の誤差を丸めて小数を有理数に変換する &lt;code&gt;approxRational&lt;/code&gt; 関数が用意されている&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci
 Prelude&gt; :m + Data.Ratio
 Prelude Data.Ratio&gt; :t approxRational
 approxRational :: RealFrac a =&gt; a -&gt; a -&gt; Rational
 Prelude Data.Ratio&gt; approxRational (1/3) 1e-1
 1 % 3
 Prelude Data.Ratio&gt; approxRational 0.3334 1e-5
 1450 % 4349
&lt;/pre&gt;&lt;p&gt;1番目の引数に分数に変換したい小数を，二番目の引数に閾値となる許容値を渡す&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;approximate-equaly&lt;/h3&gt;&lt;p&gt;Hackage に &lt;a href=&quot;http://hackage.haskell.org/package/approximate-equality&quot;&gt;approximate-equaly&lt;/a&gt; というのも公開されている． &lt;a href=&quot;http://hackage.haskell.org/packages/archive/approximate-equality/1.0/doc/html/Data-Eq-Approximate.html&quot;&gt;ドキュメントはこちら&lt;/a&gt;&lt;br /&gt;
&lt;pre class=&quot;console&quot;&gt;$ ghci
  Prelude&gt; :m + Data.Eq.Approximate
  Prelude Data.Eq.Approximate&gt; 1 == (1+10^^(-6) :: ApproximateDouble)
  True
&lt;/pre&gt;&lt;p&gt;こちらは &lt;code&gt;0.00001&lt;/code&gt; の誤差を許容した型で数値型を包み，&lt;code&gt;Eq&lt;/code&gt; で同値性判定を出来るようにしたモノ． テスト毎に誤差の範囲を指定しなくてもかまわない場合でなら，こちらも便利かと思う&lt;/p&gt;&lt;br /&gt;
&lt;h3&gt;参考リンク&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://hackage.haskell.org/packages/archive/base/4.4.1.0/doc/html/Data-Ratio.html&quot;&gt;Data.Ratio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://hackage.haskell.org/packages/archive/approximate-equality/1.0/doc/html/Data-Eq-Approximate.html&quot;&gt;Data.Eq.Approximate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/3921196291089985315/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/12/haskell.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/3921196291089985315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/3921196291089985315'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/12/haskell.html' title='Haskell の浮動小数点小数の同値比較について'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-5937820655601673492</id><published>2011-12-29T16:00:00.000+09:00</published><updated>2011-12-30T02:24:31.722+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="HUnit"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その1 : HUnit で TDD を</title><content type='html'>&lt;p&gt;すこし日が空いてしまったけど，BDD on Haskell チュートリアルの続き．&lt;br /&gt;
前回までのエントリーはこちら&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;BDD on Haskell の為のディレクトリ構成を考える&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;今回は HUnit を使って，基本的な TDD (BDD でなく) の進め方について．&lt;/p&gt;&lt;h3&gt;例 商と，小数の余剰を返す関数 を定義してみる&lt;/h3&gt;&lt;p&gt;今回のチュートリアルで適当に「ある程度需要があって，あるとそれなりに便利なもの」として，Scheme の RSR6 で採択された &lt;code&gt;div&lt;/code&gt; &lt;code&gt;mod&lt;/code&gt; をやってみる．&lt;/p&gt;&lt;h4&gt;Haskell の mod について&lt;/h4&gt;&lt;p&gt;Haskell の標準実装である &lt;code&gt;mod&lt;/code&gt; &lt;code&gt;rem&lt;/code&gt; は整数型しか受付けず，また返りも整数型なので，たとえば，Javascript や Ruby のように小数の余剰を返すことは無い．&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ ghci
Prelude&amp;gt; :t mod -- modulo
mod :: Integral a =&amp;gt; a -&amp;gt; a -&amp;gt; a
Prelude&amp;gt; :t rem -- remainder
rem :: Integral a =&amp;gt; a -&amp;gt; a -&amp;gt; a
&lt;/pre&gt;&lt;p&gt;これは &lt;em&gt;「整数である事を示す Integral 型クラスのインスタンスの引数を2つ受けて，その引数と同じ型の返り値を返す」&lt;/em&gt;という事を示している．つまり二つとも整数でないとダメだということ.&lt;/p&gt;&lt;p&gt;よって &lt;code&gt;mod 3 2.5&lt;/code&gt; のような計算は出来ない．( &lt;code&gt;2.5&lt;/code&gt; は Fractinal, つまり 分数化できる数の型クラスのリテラルと解釈される )&lt;/p&gt;&lt;p&gt;Ruby では 余剰計算の結果は小数も受けつけている&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ irb
ruby-1.9.2-p180 :001 &amp;gt; 3 % 2.5
  =&amp;gt; 0.5
&lt;/pre&gt;&lt;h4&gt;div-and-mod&lt;/h4&gt;&lt;p&gt;Scheme の RSR6 では商と余剰計算の為の関数をいくつか追加していて, &lt;code&gt;div-and-mod&lt;/code&gt; と &lt;code&gt;div0-and-mod0&lt;/code&gt; である．前者は余剰が常に非負数であるのに対し，後者は余剰の絶対値が0により近いものを返す&lt;/p&gt;&lt;pre class=&quot;concept&quot;&gt;x = y * n + m
div-and-mod の考え方
0 &amp;lt;= m &amp;lt; |y|
(div-and-mod 127 10) =&amp;gt; 12 7

div0-and-mod0 の考え方
-|y|/2 &amp;lt;= m &amp;lt; |y|/2
(div0-and-mod0 127 10) =&amp;gt; 13 -3
&lt;/pre&gt;&lt;p&gt;これに倣う形で，TDD でこれらを実装してみよう&lt;br /&gt;
Haskell には &lt;code&gt;quot&lt;/code&gt; &lt;code&gt;rem&lt;/code&gt; が商と余剰を計算する関数として用意されているので，「Fractional な型も受けつける &lt;code&gt;quotRemF&lt;/code&gt; &lt;code&gt;quotF&lt;/code&gt; &lt;code&gt;remF&lt;/code&gt;」と仮に名前をつけておく&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotRemF :: (Num t1, Num t, Ord t) =&amp;gt; t1 -&amp;gt; t1 -&amp;gt; (t, t1)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;quotRemF は 数値型ふたつの値を取って &lt;em&gt;(商, 余り)&lt;/em&gt; のタプルを返す関数．これがあれば&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotF x y = fst $ quotRemF x y
remF x y  = snd $ quotRemF x y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;で，それぞれ小数同士でも商や余剰を取れるようになる&lt;/p&gt;&lt;h3&gt;まず テストコードから書く&lt;/h3&gt;&lt;p&gt;まずテストコードを書こう. 汎用的に使える関数なので，Util.Numeric モジュールとして纏めると予定しておき，以下のように構成しておく．&lt;/p&gt;&lt;h4&gt;ファイル構成&lt;/h4&gt;&lt;p&gt;Numeric.hs と NumericTest.hs は touch コマンドでとりあえずファイルを置くだけにしておく&lt;/p&gt;&lt;pre&gt;app/
 |- src/
 |   |- Util/
 |   |   `- Numeric.hs
 |   `- UnitTest/
 |       `- Util/
 |           `- NumericTest.hs
 |- app.cabal
 |- Setup.hs
 |- LICENCE
 `- README
&lt;/pre&gt;&lt;h4&gt;app/src/UnitTest/Util/NumericTest.hs を書く&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;module UnitTest.Util.Numeric where

import Test.Framework ( defautMain, testGroup )
import Test.Framework.Providers.HUnit ( testCase )
import Test.HUnit ( (&amp;#64;=?) )

import Util.Numeric

main = defaultMain tests

tests = [
  testGroup &amp;quot;quot and rem for Fractional&amp;quot; [
      testCase &amp;quot;(q, r) of 127 10&amp;quot; ((12, 7) &amp;#64;=? (127 `quotRemF` 10))
    ]
  ]
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;テストを失敗したところから実装を始める&lt;/h3&gt;&lt;p&gt;当然，テストコードだけを書いてもテスト対象のモジュールが無かったり，テスト対象の API がそもそも宣言されてないとコンパイルエラーで実行さえできない．だけど，TDD で大事なステップはこういう当たり前のような事を確認しながらやる事が重要で．&lt;/p&gt;&lt;h4&gt;とにかくテストを通せるように app/src/Util/Numeric.hs を編集する&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;module Util.Numeric (
  quotRemF
  ) where

quotRemF x y = (12, 7)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;もうこれでテストは通る．実行してみよう&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;$ pwd
app/
$ cd ./src
app/src
$ runhaskell UnitTest/Util/NumericTest.hs
quot and rem for Fractional:
(q, r) of 127 10: [OK]

       Test Cases  Total
Passed  1           1
Failed  0           0
Total   1           1
&lt;/pre&gt;&lt;p&gt;無事テストは実行されてることがわかる&lt;/p&gt;&lt;h3&gt;テストで失敗しながら実装を確実にしていく&lt;/h3&gt;&lt;p&gt;ただ，これは確かにテストを通しているものの，全然要求と違う事はわかっている．どうすれば良いか．答えは単純で．テストケースを増やせばよい&lt;/p&gt;&lt;pre&gt;&lt;code&gt;testGroup &amp;quot;quot and rem for Fractional&amp;quot; [
    testCase &amp;quot;(q, r) of 120 10&amp;quot; ((12, 0) &amp;#64;=? (120 `quotRemF` 10))
  , testCase &amp;quot;(q, r) of 127 10&amp;quot; ((12, 7) &amp;#64;=? (127 `quotRemF` 10))
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;こうやってテストケースを増やしてテストを実行する事で，テストが失敗し，「今の実装は要求を満たせてなかった」という事が確実に判明する．&lt;/p&gt;&lt;h4&gt;実装を修正しよう&lt;/h4&gt;&lt;p&gt;とにかく，テストが通る事だけを考える&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotRemF x y = iter x y 0 where
  iter x y n
   | x == 0 = (n, 0)
   | 0 &amp;lt; x &amp;amp;&amp;amp; x &amp;lt; (abs y) = (n, x)
   | otherwise = iter (x - y) y (n + 1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これで，前のような 「取り敢えずテストが通るように値を返すのではなく，引数を計算して返すよう実装した」ことになる．&lt;/p&gt;&lt;h4&gt;更にテストを増やそう&lt;/h4&gt;&lt;pre class=&quot;literal-block&quot;&gt;testGroup &amp;quot;quot and rem for Fractional&amp;quot; [
    testCase &amp;quot;(q, r) of 120 10&amp;quot; ((12, 0) &amp;#64;=? (120 `quotRemF` 10))
  , testCase &amp;quot;(q, r) of 127 10&amp;quot; ((12, 7) &amp;#64;=? (127 `quotRemF` 10))
  , testCase &amp;quot;(q, r) of -123 10&amp;quot; ((-13, 7) &amp;#64;=? ((-123) `quotRemF` 10))
]
&lt;/pre&gt;&lt;p&gt;すると今度はテストが終了せず，無限ループにハマってしまっているケースに遭遇している．あわてず Ctrl + D 等でテストを中断し，実装コードを修正&lt;/p&gt;&lt;pre&gt;&lt;code&gt;quotRemF x y = iter x y 0 where
  iter x y n
   | x == 0 = (n, 0)
   | 0 &amp;lt; x &amp;amp;&amp;amp; x &amp;lt; (abs y) = (n, x)
   | otherwise = iter (x `step` y) y (next n)
  step x y
   | x * y &amp;gt;= 0 = x - y
   | otherwise  = x + y
  next n
   | x * y &amp;gt;= 0 = n + 1
   | otherwise  = n - 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;やや不恰好ではあっても，リファクタリングはテストが通ってから&lt;/p&gt;&lt;h3&gt;テストケースを増やして要求仕様を見えるようにしていく&lt;/h3&gt;&lt;p&gt;さて，この実装でかなり求めている事が出来るようになっているようなので，テストをどんどん増やして，「自分のやりたい事」が客観的に見えるように進めていく&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- ... 前略
  testCase &amp;quot;x = qy&amp;quot;     (( 12, 0) &amp;#64;=? (  120  `quotRemF`   10))
, testCase &amp;quot;x = qy + r&amp;quot; (( 12, 7) &amp;#64;=? (  127  `quotRemF`   10))
, testCase &amp;quot;x &amp;lt; 0 &amp;lt; y&amp;quot;  ((-13, 7) &amp;#64;=? ((-123) `quotRemF`   10))
, testCase &amp;quot;x &amp;lt; y &amp;lt; 0&amp;quot;  (( 13, 7) &amp;#64;=? ((-123) `quotRemF` (-10)))
, testCase &amp;quot;0 &amp;lt; y &amp;lt; x&amp;quot;  ((-12, 3) &amp;#64;=? (  123  `quotRemF` (-10)))
, testCase &amp;quot;y :: Fractional&amp;quot; ((1, 0.5) &amp;#64;=? (3 `quotRemF` 2.5))
-- ... 後略
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;testCase の1番目の引数はテストケースの概要を示すラベルであり，テストを実行した時にどのテストが成功または失敗かを示す目印になるので，上のように書いてあると後で見てもわかりやすくなる．&lt;/p&gt;&lt;p&gt;このようにして，自分が意図するテストケース，そのモジュールの関数のユースケースを書きつらねるようにテストを書き，失敗を見つけた際に実装を見直すのを繰り返していこう．&lt;/p&gt;&lt;h3&gt;次回，「ユースケースで想定していないパターンはどうする?」&lt;/h3&gt;&lt;p&gt;実は，一見これ完璧のように見えるけれど，実は現状の実装ではまだまだ抜けがあり，たとえば演算の基本である ゼロ除算に対して対策していなかったり，また &lt;code&gt;(x &amp;lt; y)&lt;/code&gt; な条件下のテストをしていない．&lt;/p&gt;&lt;p&gt;これが次に出てくる QuickCheck を使う意味に通じる．&lt;br /&gt;
HUnit による具体的な入力値を，実装している自分たちがちゃんと全部類推できればそれに越したことは無いけれど，今のような数値ですら沢山のパターンを列挙する事になるし，数値でない，たとえば 文字列入力などを考えたら入力例はそれでこそ膨大になる．&lt;/p&gt;&lt;p&gt;というわけで，今回はここまでにして，次回， QuickCheck を導入して &lt;em&gt;「関数の特性」&lt;/em&gt; を記述して手当たり次第データを自動的につっこむテストの仕方について解説していく．&lt;/p&gt;&lt;h3&gt;おまけ&lt;/h3&gt;&lt;h4&gt;いちいち testCase とか書くのダルイ&lt;/h4&gt;&lt;p&gt;正直コードも見にくいわな．ということでユーティリティ関数作れば良いのでは&lt;/p&gt;&lt;pre&gt;&lt;code&gt;(-:) = testCase&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;正直これだけでだいぶテストコードの見通しよくなる&lt;/p&gt;&lt;pre&gt;&lt;code&gt;  &amp;quot;x = qy&amp;quot;     -: (( 12, 0) &amp;#64;=? (  120  `quotRemF` 10))
, &amp;quot;x = qy + r&amp;quot; -: (( 12, 7) &amp;#64;=? (  127  `quotRemF` 10))
, &amp;quot;x &amp;lt; 0 &amp;lt; y&amp;quot;  -: ((-13, 7) &amp;#64;=? ((-123) `quotRemF` 10))
, &amp;quot;x &amp;lt; y &amp;lt; 0&amp;quot;  -: (( 13, 7) &amp;#64;=? ((-123) `quotRemF` (-10)))
, &amp;quot;0 &amp;lt; y &amp;lt; x&amp;quot;  -: ((-12, 3) &amp;#64;=? (  123  `quotRemF` (-10)))
, &amp;quot;y :: Fractional&amp;quot; -: ((1, 0.5) &amp;#64;=? (3 `quotRemF` 2.5))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;こういうのもアリ&lt;/p&gt;&lt;pre&gt;&lt;code&gt;should label expect assert =
  testCase label . assert expect

shouldEq label expect =
  should label expect (&amp;#64;=?)

-- テストケースをこう書ける
shouldEq &amp;quot;x = qy&amp;quot; (12, 0) (120 `quotRemF` 10)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;後々のエントリーで test-framework 用のユーティリティ関数も紹介する&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/5937820655601673492/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5937820655601673492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/5937820655601673492'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html' title='BDD on Haskell チュートリアル その1 : HUnit で TDD を'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-4654502912227882754</id><published>2011-12-13T11:00:00.000+09:00</published><updated>2011-12-29T20:27:24.858+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="cabal"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell チュートリアル その0</title><content type='html'>&lt;p&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;前回の記事&lt;/a&gt;で Haskell で BDD する基本的な構成は出来たので，これから実際に簡単なチュートリアルを書いてみる．あと HPC を Cabal に設定させてビルドテストからテストカバレッジレポートも生成させる要領もわかったので，そっちの準備等も書く&lt;/p&gt;&lt;p&gt;その前に&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell.html&quot;&gt;前回の記事&lt;/a&gt;の追記&lt;/h3&gt;&lt;p&gt;公開したら，まさに「もっとスマートな方法ありますよ」という反応が．インターネットってすばらしい&lt;/p&gt;&lt;h3&gt;GHC 7.0.2 以降，Cabal 10.2 だと Test-Suite 指定が出来る&lt;/h3&gt;&lt;p&gt;自分の環境が GHC7.0.2 なのに cabal-install が 0.8.2 というあべこべな状態だったので，前回のような方法でしかビルド構成できなかったのだけど，元々，GHC 7.0.x 同梱の cabal-install 0.10.x 以降は &lt;code&gt;exitcode-stdio&lt;/code&gt; でちゃんとテスト構成を指定する事ができる，とのこと．っていうか &lt;a href=&quot;http://www.haskell.org/cabal/users-guide/#test-suites&quot;&gt;Cabal User Guide&lt;/a&gt; に書いてあったね．&lt;/p&gt;&lt;h4&gt;というわけで MacOSX Lion に GHC7.2.2 インストール&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ curl -O http://www.haskell.org/ghc/dist/7.2.2/ghc-7.2.2-x86_64-apple-darwin.tar.bz2
$ tar -xjvf ghc-7.2.2-x86_64-apple-darwin.tar.bz2
$ cd ghc-7.2.2
$ ./configure --with-gcc-42
$ make install
&lt;/pre&gt;&lt;p&gt;もし古い GHC や Cabal などが入ってたら，前もって アンインストールしておいた方が無難&lt;/p&gt;&lt;h4&gt;インストールできたか確認&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.2.2
$ cabal --version
cabal-install version 0.10.2
using version 1.12.0 of the Cabal library
&lt;/pre&gt;&lt;p&gt;よし．ただ，この時点で以前の cabal でインストールされていたものは使えなくなるので，改めて必要なライブラリをインストールしなおす手間は要る&lt;/p&gt;&lt;h3&gt;app.cabal の書き直し&lt;/h3&gt;&lt;p&gt;最新の Cabal が入ったので，それに合わせて cabal ファイルを書き直す&lt;/p&gt;&lt;script src=&quot;https://gist.github.com/1470421.js?file=app.cabal&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;p&gt;最後の &lt;code&gt;GHC-Options: -fhpc -hpcdir dist/hpc/${cabal_name}-${cabal_ver}&lt;/code&gt; を今回更に追加してある．これは HPC でテストのカバレッジ結果を出力する為の GHC オプション&lt;/p&gt;&lt;h3&gt;何もしてないんじゃないかってくらいさっぱりした Setup.hs&lt;/h3&gt;&lt;p&gt;カスタマイズした UserHook を入れなくても良くなったので，極端にシンプルになった．&lt;/p&gt;&lt;script src=&quot;https://gist.github.com/1470421.js?file=Sethp.hs&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;p&gt;通常は &lt;code&gt;cabal init&lt;/code&gt; でこれとまったく同じファイルが出来るので，最初から構成する場合は何も気にしなくともよい&lt;/p&gt;&lt;p&gt;前回からの大きな変更箇所は以上．テストランナー，ディレクトリ構成等は変更なし&lt;/p&gt;&lt;h3&gt;テストカバレッジレポートから「必要な情報だけ抜く」スクリプト&lt;/h3&gt;&lt;p&gt;これで基礎は改めて整った．以降は以下のフローが繰り返される&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;# テスト，またはテスト対象の実装をする -&gt;
$ cabal clean # いちいちする必要もなし，大きくディレクトリ構成が変化した時, 外部ライブラリを新たに必要とした時など
$ cabal configure --enable-test # --enable-test をつけるとテストスイート込みでビルド
$ cabal build # 文字通りビルド
$ cabal test
$ ./test-report.sh&lt;/pre&gt;&lt;p&gt;&lt;code&gt;app/dist/test/unit-test/&lt;/code&gt;以下に，設定したテストのレポートファイル(unit-test.tix)が生成されるのだけど，このレポートには テスト用のモジュールまでカバレッジに含まれているので，今回 test-report.sh ファイルを用意した．これはテスト用モジュールを find してカバレッジレポートから除外して，レポートの html ファイルと xml ファイルを再生成するモノ&lt;/p&gt;&lt;h4&gt;test-report.sh&lt;/h4&gt;&lt;p&gt;実行すると，&lt;code&gt;dist/test/unit-test/&lt;/code&gt; ディレクトリに，テスト用モジュールをカバレッジレポートから除外した結果を改めてファイルに書き出して，その中の hpc_index.html をブラウザで開く．同時に result.xml ファイルを書き出すので， Jenkins とかでビルド管理してテストの結果を監視するような利用方法も想定してる&lt;/p&gt;&lt;script src=&quot;https://gist.github.com/1470421.js?file=test-report.sh&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;p&gt;思いの他長くなってしまったので，今回はここまで. &lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-1-hunit-tdd.html&quot;&gt;次回に続く&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;[再掲] ディレクトリ構成例&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;app
|- LICENSE
|- README
|- app.cabal
|- Setup.hs
|- test-report.sh
`- src
   |- Main.hs
   |- Test.hs 
   |- Hoge
   |  `- Foo.hs
   |- Huga
   |  `- Bar.hs
   `- UnitTest
      |- Hoge.hs
      |- Huga.hs
      |- Hoge
      |  `- FooTest.hs
      `- Huga
         `- BarTest.hs&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;cabal configure&lt;/code&gt; すると &lt;code&gt;dist&lt;/code&gt; ディレクトリが生成されるのだけど，これは &lt;code&gt;.gitignore&lt;/code&gt; なり &lt;code&gt;.hgignore&lt;/code&gt; なり &lt;code&gt;.darcs/boring&lt;/code&gt; なりで，バージョン管理から外しておく&lt;/p&gt;&lt;h3&gt;参考&lt;/h3&gt;&lt;dl&gt;&lt;dt&gt;&lt;a href=&quot;https://gist.github.com/1169332&quot;&gt;Installing GHC 7.2 on Mac OS X&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;ちょっと古いけど MacOSX Lion に GHC7.2.1 をインストールする手順&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.haskell.org/cabal/users-guide/#test-suites&quot;&gt;Cabal Users Guide&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;よく見りゃちゃんと書いてあったわ…&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;https://sites.google.com/site/klovelab/Home/cabal&quot;&gt;Cabal パッケージの作成&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;今回の記事はこちらの構成を参考にした&lt;/dd&gt; &lt;/dl&gt;&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4873114233&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/4654502912227882754/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4654502912227882754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4654502912227882754'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html' title='BDD on Haskell チュートリアル その0'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-1537282664893534501</id><published>2011-12-12T08:00:00.000+09:00</published><updated>2012-01-04T19:25:07.514+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><category scheme="http://www.blogger.com/atom/ns#" term="HUnit"/><category scheme="http://www.blogger.com/atom/ns#" term="QuickCheck"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>BDD on Haskell の為のディレクトリ構成を考える</title><content type='html'>&lt;p&gt;ひさしぶりの Haskell ネタ&lt;/p&gt;&lt;h3&gt;2011/12/13 追記&lt;/h3&gt;&lt;p&gt;下の例で app.cabal の設定や Setup.hs の記述などの情報が古かったので，こちらも一読してください → &lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;BDD on Haskell チュートリアル その0&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;経緯&lt;/h3&gt;&lt;p&gt;Haskell でそれなりのプログラムを書いてみようとしたので，HUnit や QuickCheck を使って BDD でやろうとしたのだけど，&lt;a href=&quot;http://www.haskell.org/haskellwiki/How_to_write_a_Haskell_program&quot;&gt;How to write a Haskell program&lt;/a&gt; 以上の事を誰も書いてないし，HUnit や QuickCheck の実例を書いた日本語圏のブログ記事も，その単体モジュールのテストのみ書いていて，肝心の全体テストを通しで実行するなどのネタが書かれていない．&lt;/p&gt;&lt;p&gt;じゃあ実際に公開してるリポジトリでも見てみるかと思い，&lt;a href=&quot;http://www.patch-tag.com/&quot;&gt;patchtag&lt;/a&gt; を見ても今度は公開してる人の殆どがテスト書くとかしてなかった．Haskeller って文化としてテスト書かないの?&lt;/p&gt;&lt;p&gt;これでは単体テストは確かに出来ても全体テストを一発で実行するような事が出来ないし，また，Haskell.org 内で挙げられているケースでも複数のモジュールがあるような場合が記述されておらず(たとえばルートディレクトリにそのまま モジュール.hs を置くようなカタチ)，どうやって構成してやれば良いのかわからなかった．&lt;/p&gt;&lt;p&gt;自分なりに工夫してみて，とりあえず単体テストとビルドテストが出来るようになったので，それをメモする&lt;/p&gt;&lt;p&gt;これよりももっと遥かに簡単で，かつスマートな方法があるのならトラバください．真剣に知りたい&lt;/p&gt;&lt;h3&gt;必要なモノとか，使ったモノ&lt;/h3&gt;&lt;dl&gt;&lt;dt&gt;GHC&lt;/dt&gt;
&lt;dd&gt;言わずもがな Glasgow Haskell Compiler. 無いと始まらない&lt;/dd&gt;
&lt;dt&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://hackage.haskell.org/package/test-framework-0.4.2.0&quot;&gt;test-framework&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;HUnit, QuickCheck を両方使えるようにするフレームワーク．&lt;a href=&quot;https://github.com/batterseapower/test-framework/&quot;&gt;GitHub で公開されている．&lt;/a&gt; &lt;code&gt;cabal install test-framework&lt;/code&gt; で入る&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://hackage.haskell.org/package/HUnit-1.2.4.2&quot;&gt;HUnit&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;xUnit系の皮をかぶった xSpec系の Haskell テストライブラリ&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://hackage.haskell.org/package/QuickCheck-2.4.1.1&quot;&gt;QuickCheck&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;「テストケースとそのアサーションをハードコーディングするなんてバカのすることよねー」と言わんばかりの 自動テストライブラリ&lt;/dd&gt; &lt;/dl&gt;&lt;h3&gt;ディレクトリ構成&lt;/h3&gt;&lt;p&gt;以下のような構成にする&lt;/p&gt;&lt;pre class=&quot;console&quot;&gt;app
|- LICENSE
|- README
|- app.cabal
|- Setup.hs
`- src
   |- Main.hs
   |- Test.hs 
   |- Hoge
   |  `- Foo.hs
   |- Huga
   |  `- Bar.hs
   `- UnitTest
      |- Hoge.hs
      |- Huga.hs
      |- Hoge
      |  `- FooTest.hs
      `- Huga
         `- BarTest.hs
&lt;/pre&gt;&lt;p&gt;しばらく Java の開発手法のトレンドとか追ってなくて，自分が知ってたころは src と test が並列していたが，最近は src の中に test ディレクトリがある方がトレンドだと聞いている (maven とかがそうなのかな?)．このように構成する事で，UnitTest モジュール内の *Test.hs は単体で，本来 Main から参照されるはずの Hoge, Huga モジュールを &lt;code&gt;import&lt;/code&gt; しつつ，スタンドアロンで実行する事ができる．また，ビルドした際に Hoge, Huga モジュールの中に *.o *.hi が入って汚される事が無くなる．(*Test な .o .hi は UnitTest/Hoge などのディレクトリに集約される)&lt;/p&gt;&lt;h3&gt;.cabal でのパッケージ情報・ビルド情報記述&lt;/h3&gt;&lt;p&gt;&lt;code&gt;cabal init&lt;/code&gt; 使って基本情報を埋めてしまって，その後で直接 &lt;/code&gt;app.cabal&lt;/code&gt; を編集し，&lt;code&gt;cabal test&lt;/code&gt; の時にビルドテストを走らせるように書く&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-- ...
Name:        app    -- アプリケーションでもライブラリでも良いけど，その名前
Builld-type: Custum -- Build-type を Simple から Custum に変更
-- ...
Executable:  app
  hs-source-dirs:   src
  Main-is:          Main.hs -- こちらが本来のメインモジュール
  Build-depends:    base, -- メインで必要な cabal ライブラリを列挙．ここにはユニットテスト用のライブラリは記載しない
-- .cabal の末尾に以下のように Executable を追加する
Executable:  test
  hs-source-dirs:   src
  Main-is:          Test.hs -- テストランナーの Main
  Build-depends:    test-framework, test-framework-hunit,
                    test-framework-quickcheck2, HUnit, QuickCheck
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Main-is に Test.hs を指定しているのがキモ．これに全体テストが走るように書く．&lt;code&gt;Executable&lt;/code&gt; に &lt;code&gt;test&lt;/code&gt; と指定しているので，これで &lt;code&gt;cabal build&lt;/code&gt; すると &lt;code&gt;app/dist/build/&lt;/code&gt;以下にテストランナー用の &lt;code&gt;test&lt;/code&gt;ディレクトリが作られ，その中に実行ファイルの &lt;code&gt;test&lt;/code&gt; がコンパイルされる&lt;/p&gt;&lt;h3&gt;Setup.hs&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;import Distribution.Simple
import Distribution.Simple.UserHooks
import Distribution.Simple.LocalBuildInfo
import Distribution.PackageDescription
import System.Cmd
import System.FilePath

main = defaultMainWithHooks hooks
 where hooks = simpleUserHooks { runTests = runTests&#39; }

runTests&#39; :: Args -&gt; Bool -&gt; PackageDescription -&gt; LocalBuildInfo -&gt; IO ()
runTests&#39; _ _ _ lbi = system testprog &gt;&gt; return ()
 where testprog = buildDir lbi &lt;/&gt; &quot;test&quot; &lt;/&gt; &quot;test&quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Setup.hs は &lt;code&gt;cabal configure&lt;/code&gt; コマンドで， &lt;code&gt;cabal build&lt;/code&gt; 時にどうビルドするかを設定する．この &lt;code&gt;simpleUserHooks&lt;/code&gt; のコンストラクタに テストランナーの挙動を書く&lt;/p&gt;&lt;h3&gt;Test.hs&lt;/h3&gt;&lt;pre&gt;&lt;code&gt;import System.Environment ( getArgs )
import Test.Framework

import qualified UnitTest.Hoge as Hoge ( testSuite )
import qualified UnitTest.Huga as Huga ( testSuite )

main :: IO ()
main = runTests

runTests :: IO ()
runTests =
 do 
  args &lt;- filter (/= &quot;--tests&quot;) `fmap` getArgs
  ( defaultMainWithArgs testSuite args )

test :: [Test]
testSuite = foldl (++) [] [
   Hoge.testSuite
 , Huga.testSuite
 ]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;テストランナーのやっている事はいたって簡単で，各モジュールレベルのテストをまとめあげて，&lt;code&gt;test-framework&lt;/code&gt; の &lt;code&gt;defaultMainWithArgs&lt;/code&gt; にテストケースを渡しているだけ．&lt;/p&gt;&lt;p&gt;あえて &lt;code&gt;testSuite&lt;/code&gt; の宣言を &lt;code&gt;foldl&lt;/code&gt; で束ねているかというと，こうする事で最後の引数のリストに，追加すべきテストモジュールが増えてもコードの変更をそこまでしなくても良いから&lt;/p&gt;&lt;h3&gt;各モジュールのテスト&lt;/h3&gt;&lt;h4&gt;UnitTest/Hoge.hs&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;module UnitTest.Hoge where

import Test.Framework
import qualified UnitTest.Hoge.Foo as Foo ( testSuite )

main :: IO ()
main = defaultMain testSuite

testSuite :: [Test]
testSuite = foldl (++) [] [
    Foo.testSuite
  ]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;こちらも，モジュールの各単体テストをまとめあげて，&lt;code&gt;test-framework&lt;/code&gt; の &lt;code&gt;defaultMainWithArgs&lt;/code&gt; にテストケースを渡しているだけ．この時点で，「ライブラリ全体のテスト」「モジュール単位のテスト」「モジュールの更に細分化された単体テスト」がそれぞれ実行できる環境が整う．ビルドせずに &lt;code&gt;runghc UnitTest/Hoge.hs&lt;/code&gt; を実行する事で，モジュール単位でのテストを通すことができる．&lt;code&gt;import qualified&lt;/code&gt; で各サブモジュールを &lt;code&gt;import&lt;/code&gt; するので &lt;code&gt;UnitTest.Hoge.Foo.testSuite&lt;/code&gt; と，ここで宣言されている &lt;code&gt;testSuite&lt;/code&gt; とで名前空間が衝突するような事は無くなる&lt;/p&gt;&lt;h4&gt;UnitTest/Hoge/Foo.hs&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;module UnitTest.Hoge.Foo where
import Test.Framework
import Test.Framework.HUnit
import Test.Framework.QuickCheck
import Test.HUnit
import Test.QuickCheck

import Hoge.Foo -- テスト対象のモジュール

main :: IO ()
main = defaultMain testSuite

testSuite :: [Test]
testSuite = [
    testGroup &quot;...&quot; [ 
      -- テスト対象のトップレベル宣言関数の testCase, testProperty を列挙
    ]
   , testGroup &quot;...&quot; [
      -- ...
    ]
  ]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;こちらがサブモジュールの単体テスト．というか各テストの本体にあたる．ビルドせずに &lt;code&gt;runghc UnitTest/Hoge.hs&lt;/code&gt; を実行する事で，テストを通すことができる．&lt;/p&gt;&lt;p&gt;たぶん初心者的にはこちらの方を重点的に解説した方が良いのだけど，そこも含めると長いので別途エントリーをあげる&lt;/p&gt;&lt;h3&gt;テスト実行&lt;/h3&gt;&lt;p&gt;すべて app/ ディレクトリ以下で実行されているものだとして&lt;/p&gt;&lt;h4&gt;単体テスト&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ pushd src &amp;&amp; runghc UnitTest/Hoge/Foo.hs &amp;&amp; popd
&lt;/pre&gt;&lt;h4&gt;モジュールレベルテスト&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ pushd src &amp;&amp; runghc UnitTest/Hoge.hs &amp;&amp; popd
&lt;/pre&gt;&lt;h4&gt;全体テスト&lt;/h4&gt;&lt;pre class=&quot;console&quot;&gt;$ cabal configure
$ cabal build
$ cabal test
&lt;/pre&gt;&lt;p&gt;はい，これで Haskell で BDD する手筈が整った．具体的な BDD のステップについては&lt;a href=&quot;http://voqn.blogspot.com/2011/12/bdd-on-haskell-0.html&quot;&gt;次のエントリー&lt;/a&gt;で．&lt;/p&gt;&lt;h3&gt;参考&lt;/h3&gt;&lt;dl&gt;&lt;dt&gt;&lt;a href=&quot;http://www.haskell.org/haskellwiki/How_to_write_a_Haskell_program&quot;&gt;How to write a Haskell program&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;なんか日本語のブログとかでこれより詳細な Haskell 入門とか見たことが無い気がする…&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.haskell.org/cabal/users-guide/#test-suites&quot;&gt;Cabal Users Guide&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;そういえば Cabal の解説も見たことがない&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://koweycode.blogspot.com/2009/07/some-ideas-for-practical-quickcheck.html&quot;&gt;some ideas for practical QuickCheck&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;今回の記事はこちらの構成を参考にした&lt;/dd&gt; &lt;/dl&gt;&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4873114233&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/1537282664893534501/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/12/bdd-on-haskell.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/1537282664893534501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/1537282664893534501'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/12/bdd-on-haskell.html' title='BDD on Haskell の為のディレクトリ構成を考える'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-7765484505837641362</id><published>2011-10-02T03:50:00.000+09:00</published><updated>2012-04-19T04:54:00.948+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Design"/><category scheme="http://www.blogger.com/atom/ns#" term="GUI"/><category scheme="http://www.blogger.com/atom/ns#" term="Web"/><title type='text'>GUI ボタンのデザイン・ストラテジーについて</title><content type='html'>&lt;h3&gt;GUI デザインをする上で考えていること&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;http://stocker.jp/&quot;&gt;Stocker.jp&lt;/a&gt; さんのところで &lt;a href=&quot;http://stocker.jp/diary/web-design-3/&quot;&gt;[連載]Webデザイン入門（3:光とボタン）&lt;/a&gt; というWebデザイン初学者向けの連載が行われているのだけど，それに触発されて「自分だったら (こうする/こうしてる)」というのを実例を持って書いてみる．&lt;/p&gt;&lt;p&gt;ここでいう UIデザインは GUI のスタイリングであって，実際にはデザインと呼ぶには領域が挟すぎるのだけど，おそらく多くの人が認識している&quot;デザイン&quot;と呼ぶものに合わせる．実際には DBモデル, API設計, URI設計, CallbackFunction まで実装して，はじめてデザインと呼べるものだと思う&lt;/p&gt;&lt;h3&gt;サマリー&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;機能について把握すること&lt;/li&gt;
&lt;li&gt;機能の記号化(ピクトグラム) ・ そのシルエットのデザイン&lt;/li&gt;
&lt;li&gt;「押せそうな」アフォーダンス・「明暗」と「光沢」「質感」を切り離して考える&lt;/li&gt;
&lt;li&gt;「押せそうな」アフォーダンス・「明暗」のデッサン&lt;/li&gt;
&lt;li&gt;表現の変更に強いレイヤー構成&lt;/li&gt;
&lt;li&gt;ベクターデータ形式 で UI のイメージを作る時は「実際に利用されるサイズ」を考える&lt;/li&gt;
&lt;li&gt;スタイルとトレンド&lt;/li&gt;
&lt;li&gt;まとめ・デザインする目的を&lt;/li&gt;
&lt;/ol&gt;&lt;h3&gt;機能について把握すること&lt;/h3&gt;&lt;p&gt;Ajax 等を使って API を再度リクエストする Reload ボタンだという想定．最近は Async な非同期処理で，ユーザーがわざわざ更新の操作をする機会は減ったものの，まだまだ「マッシュアップサービス なので，API のリクエスト先はストリーミングAPIを実装していない外部ドメイン，よって頻繁にリクエストを投げにくい」など，そんなケースが存在する．また，分単位で解析結果が更新されるようなサービス (Blogger のアクセスログや Amazon アフィリエイトなど) にも，そうした需要がある．&lt;/p&gt;&lt;p&gt;今回の作例はそうした背景のある機能を GUI ボタンとしてデザインする&lt;/p&gt;&lt;h3&gt;機能の記号化(ピクトグラム) そのシルエットのデザイン&lt;/h3&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://lh5.googleusercontent.com/-_C4Epp5u_uY/TobVg3svHhI/AAAAAAAAAaE/5jcHkmf_DVw/w343/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B17.53.59.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;224&quot; width=&quot;343&quot; src=&quot;https://lh5.googleusercontent.com/-_C4Epp5u_uY/TobVg3svHhI/AAAAAAAAAaE/5jcHkmf_DVw/w343/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B17.53.59.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;まずは機能の記号化したものを，ユーザーに記憶してもらうように，ピクトグラムのシルエットで考える．&lt;/p&gt;&lt;p&gt;ここで間違ってたり効果が薄かったら 上からグラデーションやらメタリック/グラスな質感とかにこだわっても結局何のボタンなのかわからない&lt;/p&gt;&lt;p&gt;ここで，仮にピクトグラムではなく，「Reload」「再読み込み」など，ボタンのラベルをテキストにしなければならない，あるいは想定しているユーザーにとって，その方が理解しやすいと判断できる場合は，そのタイポグラフィをまず全力でやる&lt;/p&gt;&lt;p&gt;手短に書いてはいるけれど，このフローが一番大事で，それが分かっていればダラダラと長いこのエントリーを最後まで見なくてもよい&lt;/p&gt;&lt;h3&gt;「押せそうな」アフォーダンス・「明暗」と「光沢」「質感」を切り離して考える&lt;/h3&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://lh6.googleusercontent.com/-kBMIzMi161o/TobiCycwT6I/AAAAAAAAAaY/QS5z6jz9578/w402/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B18.24.19.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;205&quot; width=&quot;402&quot; src=&quot;https://lh6.googleusercontent.com/-kBMIzMi161o/TobiCycwT6I/AAAAAAAAAaY/QS5z6jz9578/w402/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B18.24.19.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;で，次に&lt;strong&gt;「UI 以外の文書や画像，動画コンテンツとは一線を画す」ための差別化として，「押せそうな」アフォーダンスを提供する&lt;/strong&gt;ために，立体的な陰影を付けたりするのがポピュラーな方法なのだけど，件の記事の作例ではいきなりメタリック/グロスな質感を与えてしまっている．&lt;/p&gt;&lt;p&gt;作例のグラデーションは上図のような感じだった．(注：実際にはちゃんと滑らかな曲面に仕上げていたのだけど，後述の為にやや誇張している) しかし，この表現では横から見たら，右のような断面図に見えてしまう&lt;/p&gt;&lt;p&gt;(「でっぱり」の再現であればこれで良いとは思う)&lt;/p&gt;&lt;p&gt;質感表現は，その全体的なスタイリングとマッチするかどうかや，機能のプライオリティ (ユーザーにどれくらい優先的に使ってもらいたいか)，に影響を大きく左右されるし，明暗，陰影とはレイヤを別にしないとリテイクを受けた時がツラくなる．実務で安易にやってしまった場合一番リテイクが来るのもココだ．&lt;/p&gt;&lt;p&gt;もっと言えば&lt;strong&gt;「質感表現はそもそもそのデザインに必要不可欠なのか?」&lt;/strong&gt;って問いに明確な回答が無い．先に述べたように&lt;q&gt;次に地の「UI 以外の文書や画像，動画コンテンツとは一線を画す」ための差別化として，「押せそうな」アフォーダンスを提供するため&lt;/q&gt;の目的に対してはオマケのようなモノでしかない&lt;/p&gt;&lt;h3&gt;「押せそうな」アフォーダンス・「明暗」のデッサン&lt;/h3&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://lh5.googleusercontent.com/-BWjYl38PP0I/TobjTCXWsMI/AAAAAAAAAas/wmnQUUFl0ok/w402/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B18.47.37.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;222&quot; width=&quot;402&quot; src=&quot;https://lh5.googleusercontent.com/-BWjYl38PP0I/TobjTCXWsMI/AAAAAAAAAas/wmnQUUFl0ok/w402/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B18.47.37.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;質感を追求する前に，立体形状をグラフィックで再現する事の方をまずトレーニングした方が良い．本当にカマボコ状の出っ張りのあるボタンであれば，上の図のようなライティングになる．&lt;/p&gt;&lt;p&gt;デッサンには環境光をいくらか無視して光源が基本一点，そこから地面や他の物体が反射光を照り返してる，という想定をわざとする事で，描く対象の立体感をより強く描画する，という基本がある．&lt;/p&gt;&lt;p&gt;質感，ボタンのベースカラーはとりあえず保留して「形状に光と影があった場合はこのようになるはず」というデッサンをまずやる&lt;/p&gt;&lt;h3&gt;表現の変更に強いレイヤー構成&lt;/h3&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://lh3.googleusercontent.com/-UnxRi6ozAWI/Tob1h7udxrI/AAAAAAAAAbA/cCKKejbfwBc/w402/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B20.10.35.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;229&quot; width=&quot;402&quot; src=&quot;https://lh3.googleusercontent.com/-UnxRi6ozAWI/Tob1h7udxrI/AAAAAAAAAbA/cCKKejbfwBc/w402/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B20.10.35.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;試しに(ほどよく磨かれたギラギラとまではいかない程度の)メタリックな質感をつけてやってみたのが上&lt;/p&gt;&lt;ol&gt;&lt;li&gt;ピクトグラム : Symbol (通常透過)&lt;/li&gt;
&lt;li&gt;オマケ的な 質感表現 : gloss (スクリーン透過)&lt;/li&gt;
&lt;li&gt;ボタンの光源によって光る部分 : HighLight (スクリーン透過)&lt;/li&gt;
&lt;li&gt;陰影: Shadow (乗算透過)&lt;/li&gt;
&lt;li&gt;ボタンのベースカラー : Base&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;こうすれば基本ベースカラーをWebサイトのカラーマネジメントや，機能的に色を意味づけしたいなどの要望に手早く応答できる．&lt;/p&gt;&lt;p&gt;基本はベクターデータ形式 (.ai .svg) で原本を管理しておき，デプロイ時に png フォーマットに出力するのが楽な管理方法．サイズ変更にもPhotoshopで直に作成するよりは比較的強い．&lt;/p&gt;&lt;p&gt;色空間モデルは最終的にラスターデータ形式の画像ファイルに出力してしまうのだから，デザイナー的に色を操作しやすい HSL/HSV でやった方が良い&lt;/p&gt;&lt;h3&gt;ベクターデータ形式 で UI のイメージを作る時は「実際に利用されるサイズ」を考える&lt;/h3&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://lh6.googleusercontent.com/-k7wBq3HlgOA/TocIeWXJWdI/AAAAAAAAAbo/cxLrbUG3E1s/w56/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B21.32.30.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;98&quot; width=&quot;56&quot; src=&quot;https://lh6.googleusercontent.com/-k7wBq3HlgOA/TocIeWXJWdI/AAAAAAAAAbo/cxLrbUG3E1s/w56/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B21.32.30.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;ここまでは「実際に利用されるサイズ」の事を度外視していたわけだけど，そもそもWebでもPC用アプリケーションでもスマートフォンアプリでも，限られた画面の中で，「他のジャマにならず，かつユーザーが欲する時にすぐ目につくような」スケールを考えてデザインはしないといけない&lt;/p&gt;&lt;p&gt;作例の一つ一つのボタンの高さは 18px ．これが今考えられるスマートフォン等で指で押しやすい最小と思われるサイズであることから．文字とのジャンプ率的にもこれくらいが妥当&lt;/p&gt;&lt;p&gt;ベクターデータ形式はスケーラブルなのが強みなのだけど，現在 .svg ファイルは img src=&quot;... で受けつけられないフォーマットなのでラスターデータ形式にする．jpeg では圧縮アルゴリズムの関係でエッジの部分やボーダーラインに色のノイズが入り，汚ない変換になりがちなので，透過GIF, 透過PNG を選ぶ．&lt;/p&gt;&lt;p&gt;また，その圧縮時にピクセルの色がアンチエイリアス等でにじんでしまうので，基本的には「UI のベースに貼りつけたい原寸のサイズ」でデザイン作業をやる．&lt;/p&gt;&lt;p&gt;特にボーダーライン ピクトグラムのディテールを 元を大きなスケールで作って満足してしまうと，「実際に貼りつけるサイズ」に縮小した時に残念な事になる．ディテールが潰れて見えない，ボーダー，ハイライトにアンチエイリアスがかかって想像した物より薄い色になる，等々&lt;/p&gt;&lt;p&gt;また，原寸のサイズでデザインをやると，Webデザインの場合 CSS スプライトを作成するのが簡単になる．&lt;/p&gt;&lt;p&gt;作例の 100% はこのような png に出力される．ボーダーラインやハイライトもキレイに変換できている．&lt;/p&gt;&lt;h3&gt;スタイルとトレンド&lt;/h3&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://lh4.googleusercontent.com/-Q4OVIfSJBqs/TocF7rHu-uI/AAAAAAAAAbU/yMH1p43NtdI/h301/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B21.21.38.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;301&quot; width=&quot;167&quot; src=&quot;https://lh4.googleusercontent.com/-Q4OVIfSJBqs/TocF7rHu-uI/AAAAAAAAAbU/yMH1p43NtdI/h301/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B21.21.38.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;近年までは MacOS の Aqua に始まり Window Vista, iOS, Android のUIは図の真ん中のような立体感を強調するグロスの効いて，角丸の大きくとったスタイルが好まれ，どこもかしこも Web デザインにこのような UI スタイルが流行っていたのだけど，最近は Twitter bootstrap Facebook Google+ など，Window 8 (Windows Phone) で Metro UI と称されるシンプルなスタイルの方向性 (一番上)  がトレンドになってきている．&lt;/p&gt;&lt;p&gt;これを書く殴り書きの段階で，DanKogai さんから「Metroは一番下じゃない?」ってツッコミが来たんだけど，純粋なMetroは角丸なし，枠線なし，特徴的な背景色に，白(あるいは黒) の前景色のシンボルのパターン．「Metroっぽい(?)」くらいが表現として妥当かと思われる&lt;/p&gt;&lt;p&gt;UI というか，ヴィジュアルデザインの基礎は図と地の明度差による認識であり，シルエット，シンボルカラーの二つが最も影響度高いので，なんというか基本に立ち返った感がする．&lt;/p&gt;&lt;h3&gt;まとめ・デザインする目的を&lt;/h3&gt;&lt;p&gt;タイトルを「デザイン・ストラテジーについて」とした理由は，ここで書いた一連はグラフィックツールの操作など，実際の作業手順より一層抽象的に記述されているからで．おそらく初学者からしたら，それよりは具体的に「次にグラデーションツールでどうするか」などの細かい&quot;手順&quot;が書かれていた方がてっとり早いかと思われる&lt;/p&gt;&lt;p&gt;それらを省いて，あくまで手順に向かう際の「指針」だけなのは，それらを書きはじめると，本筋が埋没してしまうし，本件で言いたいトコロではない&lt;/p&gt;&lt;p&gt;重要なのは，仮に些細なボタンを作成する時であっても，&lt;br /&gt;
&lt;strong&gt;&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;「それは誰の為に」&lt;/li&gt;
&lt;li&gt;「それは何の為に」&lt;/li&gt;
&lt;li&gt;「それはどれくらい重要で」&lt;/li&gt;
&lt;li&gt;「それはどのくらいの頻度で」&lt;/li&gt;
&lt;/ul&gt;&lt;/strong&gt;&lt;br /&gt;
を考えながら具体的な作業をする事が大事で，その目的がしっかりしていれば「どんなスタイリングにするか」「あるべき大きさ」「あるべき位置」は自ずと絞られてくるからだ．&lt;/p&gt;&lt;p&gt;逆にそれらが念頭に置かれてなく，グラフィックツールを使いこなして，(えーと…) 例えばはてなブックマークでよく「デザイン」タグでのホットエントリーに上がってくる「クールでフリー利用可能なGUIボタンの Photoshop データ 10選」みたいなのに目移りして，そのような &quot;流行りの&quot; スタイリングをしても，&lt;strong&gt;誰の為のデザインもしてない&lt;/strong&gt;．&lt;strong&gt;何の為のデザインもしてない&lt;/strong&gt;．&lt;/p&gt;&lt;p&gt;それらを踏まえた上で，実際に実務でデザイン(正確に言うとスタイリング)する際に，後々の作業のしやすさ，本質的なデザインの追求のしやすさを意識しやすくする事について書いた．&lt;/p&gt;&lt;p&gt;そういえば肩書き一応デザイナも兼ねてるエンジニアなのだけど，始めてデザインについてエントリー書いたな…&lt;/p&gt;&lt;h3&gt;参考にすると良い書籍&lt;/h3&gt;&lt;div style=&quot;float:left;&quot;&gt;&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4873113164&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4873114349&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4861007194&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4861005140&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;clear:both;&quot;&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/7765484505837641362/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/10/gui.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/7765484505837641362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/7765484505837641362'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/10/gui.html' title='GUI ボタンのデザイン・ストラテジーについて'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://lh5.googleusercontent.com/-_C4Epp5u_uY/TobVg3svHhI/AAAAAAAAAaE/5jcHkmf_DVw/s72-w343-c/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588%2B2011-10-01%2B17.53.59.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-1015600043387703961</id><published>2011-09-08T11:51:00.001+09:00</published><updated>2011-09-08T11:51:28.983+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gauche"/><title type='text'>フォントサイズを軽く調べるGaucheスクリプト</title><content type='html'>&lt;p&gt;デザイナー向けスクリプトシリーズ&lt;/p&gt;&lt;h4&gt;経緯&lt;/h4&gt;&lt;p&gt;きっかけは友人のこんな発言から．&lt;/p&gt;&lt;blockquote&gt;&lt;a href=&quot;https://twitter.com/#!/orange21/status/109924881512144896&quot;&gt;Twitter / @orange21: Qで作業しながらPt分かるようになりたいんだけどさー&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;Q っていうのは活字の級数で，要するに感覚的に文字のサイズは級数で考えたいんだが，その文字サイズが実際 ポイントにしてどれくらいかを確認しながら作業したい，という事．自分の場合 pt よりも pt, mm, px でそれぞれ，そういう風にチマチマとチェックしながらデザイン作業やってたりしてたなーと思い返す．&lt;/p&gt;&lt;p&gt;今日び，その手のフォントサイズ変換表とか，JavaScript 変換表作ってみましたーってやつ，ググればいっぱい出てくるんだけど，わざわざブラウザの大きな画面を必要とするものじゃないし，モニタのすみっこに小さくコンソール開いておいて，ちょいちょい打って確認できるくらいがちょうどいいんだよなー，ということで，ちゃっちゃと簡単なスクリプトを書いてみた．あまり Lisp 的な高度な事はやってないので結構冗長である&lt;/p&gt;&lt;h4&gt;使い方&lt;/h4&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;$ gosh font-size-conv.scm -dpi 300 -p 144&lt;/code&gt;&lt;/pre&gt;とかにすると，DPI=300 の状態での 144pt のフォントサイズが級数Q，mm, px ではどれくらいになるのかを標準出力で変換表を出してくれる．illustrator とか InDesign で印刷物やってる時とかは dpi 直接入力できると便利だと思う&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;デフォルトのDPIはWindows標準の96にしてあるので，Web系であれば，&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;$ gosh font-size-conv.scm -x 100&lt;/code&gt;&lt;/pre&gt;などで見れば標準的なモニタ解像度での 100px になるフォントサイズ を調べられる．&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;ツッコミ所は「なんで Perl Ruby Python みたいに標準でOSに入ってる言語じゃなくて Gauche なん」っていうところでしょうか (だって Lisp/Scheme が好きなんだもん)&lt;/p&gt;&lt;p&gt;はい，「Gaucheって何? いんすとーるどうすればいいのん」って人はググってコンソールアプリケーションの世界にようこそ&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;font-size-conv.scm&lt;/h4&gt;&lt;script src=&quot;https://gist.github.com/1194476.js?file=font-size-conv.scm&quot;&gt;&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;参考図書&lt;/h4&gt;&lt;iframe src=&quot;http://rcm-jp.amazon.co.jp/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=voqn-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=ss_til&amp;asins=4873113482&quot; style=&quot;width:120px;height:240px;&quot; scrolling=&quot;no&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/1015600043387703961/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/09/gauche.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/1015600043387703961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/1015600043387703961'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/09/gauche.html' title='フォントサイズを軽く調べるGaucheスクリプト'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-7309068584257962218</id><published>2011-09-07T03:18:00.000+09:00</published><updated>2011-09-07T03:18:26.414+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gauche"/><category scheme="http://www.blogger.com/atom/ns#" term="OOP"/><category scheme="http://www.blogger.com/atom/ns#" term="Scheme"/><title type='text'>Type Safe(?) MetaClass for Gauche MOP</title><content type='html'>&lt;p&gt;仕事はもっぱら PHP HTML CSS JavaScript なのですが，ここ最近は一年に一度の Scheme 熱に浮かれております．特に思い付いたアイデアをスケッチする感覚でEmacs立ち上げてアルゴリズムの実装とかやるのに，とても Gauche は気持ちが良い．みんな Schemer になりましょう．&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;CLOS をもう少し使いこなしてより自分好みにしたい&lt;/h4&gt;&lt;p&gt;Gauche の CLOS 風オブジェクトシステムってとても良い感じなのだけど，クラス設計時に Perl の Moose のようにある程度メンバ変数の型を固定しておきたいなぁと思う事がしばしばあり，試しにこんな風なメタクラスを作ったら，完全に型安全というわけではないけれど，そこそこ自分が求めているレベルの安心が担保できた．というのを書きとめておく.&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;要求&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;スロット定義時に &lt;code&gt;&amp;lt;hoge&amp;gt;&lt;/code&gt; またはそのサブクラスのインスタンスのみにしたい&lt;/li&gt;
&lt;li&gt;スロットに許容限界値を設けたい．&lt;/li&gt;
&lt;li&gt;許容限界値を超えた場合の動作を，フィルタリングによって丸めこんでしまえるように&lt;/li&gt;
&lt;li&gt;許容限界値を超えた場合の動作を，エラーとして投げるようにする場合も欲しい&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;h4&gt;考えてみたメタクラス仕様&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;:is-a &amp;lt;hoge&amp;gt;&lt;/code&gt; で &lt;code&gt;&amp;lt;hoge&amp;gt;&lt;/code&gt; またはそのサブクラスのインスタンスのみ &lt;code&gt;set!&lt;/code&gt; できる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:filter (&amp;lt;closure&amp;gt; な関数)&lt;/code&gt; で &lt;code&gt;set!&lt;/code&gt; 時に値をフィルタリング&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:validate (&amp;lt;closure&amp;gt; な関数)&lt;/code&gt; で &lt;code&gt;set!&lt;/code&gt; 時に許容範囲外の値を検出してエラー投げる &lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;p&gt;運用上，2 と 3 は &lt;code&gt;:filter :validate&lt;/code&gt; のどちらかを持たせるだけで本当は良いのだけど，ソースを後で見た時に「&lt;code&gt;:validate&lt;/code&gt; だからこれは異常系でエラー出るのな」「&lt;code&gt;:filter&lt;/code&gt; だからこれは異常系丸めこむのか」と分かり易くする為にあえて分けている．ただし，実装上，両方あった場合も &lt;code&gt;:filter&lt;/code&gt; が2つ合成関数になった状態でスロットに &lt;code&gt;set!&lt;/code&gt; したい値に適用されたようなものになる．&lt;/p&gt;&lt;br /&gt;
&lt;h4&gt;サンプルコード(実装編)&lt;/h4&gt;&lt;br /&gt;
&lt;p&gt;詰めが甘そうではあるけど，こんな風に実装してみた．実際にやってる事は &lt;code&gt;slot-set-using-accessor!&lt;/code&gt; の処理の前に &lt;code&gt;:is-a&lt;/code&gt;が指定してあれば，そのサブクラスかを &lt;code&gt;is-a?&lt;/code&gt; でチェックして，&lt;code&gt;:validate &lt;/code&gt; &lt;code&gt;:filter &lt;/code&gt; を記述してあれば，その合成関数をかけておくようにする&lt;/p&gt;&lt;br /&gt;
&lt;pre class=&quot;scheme&quot; name=&quot;code&quot;&gt;(define-class &amp;lt;type-safe-meta&amp;gt; (&amp;lt;class&amp;gt;) (()))

(define-method compute-get-n-set ((class &amp;lt;type-safe-meta&amp;gt;) slot)
  (let* ([has? (cut slot-definition-option slot &amp;lt;&amp;gt; #f)]
	 [acc (compute-slot-accessor class slot (next-method))]
	 [type-error
	  (^ (value type)
	     (error
	      #`&quot;Type Error : require type ,|type| but ,(class-of value)&quot;
	      value))]
	 [validate-type
	  (^v (if-let1 t (has? :is-a)
		(if (is-a? v t) v (type-error v t))
		v))]
	 [validate-value
	  (^v (if-let1 validate (has? :validate) (validate v) v))]
	 [filter-value
	  (^v (if-let1 f (has? :filter) (f v) v))])
    (if (or (has? :is-a) (has? :validate) (has? :filter))
	(let1 filter/validate (.$ filter-value validate-value validate-type)
	  (list (^ (o) (slot-ref-using-accessor o acc))
		(^ (o v) (slot-set-using-accessor! o acc (filter/validate v)))
		(^ (o) (slot-bound-using-accessor? o acc))
		#t))
	(next-method))))
&lt;/pre&gt;&lt;br /&gt;
&lt;h4&gt;サンプルコード(応用編)&lt;/h4&gt;&lt;br /&gt;
&lt;p&gt;今，趣味でCSSマクロ言語みたいなのを自前で開発したくて，その前に，それの為の RGB HSL HSV なんかの色空間を扱うライブラリを書いていて (&lt;a href=&quot;https://github.com/VoQn/Gauche-Color&quot;&gt;VoQn/Gauche-Color - GitHub&lt;/a&gt;)，それぞれのモデルを記述する時に，こんな風に書いて簡略化，というか，「スロットの中身があらかじめどんなもんか見当つく」クラスを設計できる．これなら公開モジュールとして &lt;code&gt;export&lt;/code&gt; しても，想定してない使われ方はしなさそう&lt;/p&gt;&lt;br /&gt;
&lt;pre class=&quot;scheme&quot; name=&quot;code&quot;&gt;(define (loop-mod$ max) 
  (^x ((^v (if (= v (floor v)) (x-&gt;integer v) v))
       (cond [(&lt;= max x) (fmod x max)]
	     [(&gt; 0 x) (fmod (+ x max) max)]
	     [else x]))))

(define (inner$ min max) (cut clamp min &lt;&gt; max))

;;; HSL Color Model
(define-class &amp;lt;hsl&amp;gt; (&amp;lt;color&amp;gt;)
  ([hue :init-value 0 :init-keyword :hue :is-a &amp;lt;real&amp;gt; :filter (loop-mod$ 360)]
   [saturation :init-value 0 :init-keyword :saturation  :is-a &amp;lt;real&amp;gt; :filter (inner$ 0 1)]
   [luminance :init-value 0 :init-keyword :luminance :is-a &amp;lt;real&amp;gt; :filter (inner$ 0 1)]))
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;更に&lt;code&gt;define-class&lt;/code&gt;もマクロ使って &lt;code&gt;init-value :init-keyword :accessor&lt;/code&gt; とかを補完したりできるので，もっとクラス定義を自分が特記したい部分だけの記述にできる．&lt;/p&gt;&lt;br /&gt;
&lt;pre class=&quot;scheme&quot; name=&quot;code&quot;&gt;(define-class* &amp;lt;hsl&amp;gt; (&amp;lt;color&amp;gt;)
  ([hue :is-a &amp;lt;real&amp;gt; :filter (loop-mod$ 360)]
   [saturation :is-a &amp;lt;real&amp;gt; :filter (inner$ 0 1)]
   [luminance :is-a &amp;lt;real&amp;gt; :filter (inner$ 0 1)]))
&lt;/pre&gt;&lt;br /&gt;
&lt;p&gt;自分のリポジトリの中だと，これだけで良い．MOP 駆使したら Ruby のメタプログタミングよろしくけっこうな事が出来る気がする．もう少し研究したい&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/7309068584257962218/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/09/type-safe-metaclass-for-gauche-mop.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/7309068584257962218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/7309068584257962218'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/09/type-safe-metaclass-for-gauche-mop.html' title='Type Safe(?) MetaClass for Gauche MOP'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-6795661564139296462</id><published>2011-05-30T00:55:00.001+09:00</published><updated>2011-05-30T00:56:26.092+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="wtfjs"/><title type='text'>wtf!? JavaScript Empty Array Programming</title><content type='html'>&lt;p&gt;昨日，&lt;a href=&quot;http://tweetvite.com/event/y977&quot;&gt;やきとんオフ&lt;/a&gt;に参加したところ，&lt;a href=&quot;http://d.hatena.ne.jp/mizchi/&quot;&gt;mizchi&lt;/a&gt; や &lt;a href=&quot;http://d.hatena.ne.jp/ymrl/&quot;&gt;ymrl&lt;/a&gt; に「頭おかしい…」と言われたので，ちゃんと解説します．&lt;/p&gt;
&lt;div&gt;
&lt;h3&gt;0. サンプルコード&lt;/h3&gt;
&lt;p&gt;これをコピーして,ブラウザのアドレスバーに &lt;code&gt;javascript:&lt;/code&gt; と先頭につけてペーストして実行してみましょう．&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;_=-~-~-~[];__=[][(![]+[])[_-_]+([][[]]+[])[_+_-_/_]+(![]+[])[_-_/_]+(!![]+[])[_-_]+(!![]+[])[_]+(!![]+[])[_/_]]+[];___=__[_]+__[_+_]+__[_-_/_]+(![]+[])[_]+(!![]+[])[_-_]+(!![]+[])[_/_]+(!![]+[])[_-_/_]+__[_]+(!![]+[])[_-_]+__[_+_]+(!![]+[])[_/_];___[___][___]((![]+[])[_/_]+(![]+[])[(_+_)/_]+(!![]+[])[_]+(!![]+[])[_/_]+(!![]+[])[_-_]+&quot;(\&quot;&quot;+&#39;\\&#39;+(([][[]]+[])[_-_])+(_)+(_-_)+(_+_/_)+(_+_)/_+&quot;\&quot;);&quot;)();&lt;/pre&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://gyazo.com/a81c3d1d7644c393c40c84d3fdd94d90.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; width=&quot;600&quot; src=&quot;http://gyazo.com/a81c3d1d7644c393c40c84d3fdd94d90.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;このような実行結果になります．当然ですよね&lt;/p&gt;
&lt;p&gt;なんでこのコードが動くのか，の前にいくつかの &lt;a href=&quot;http://wtfjs.com/&quot;&gt;wtfjs&lt;/a&gt; な挙動の解説が必要で，まぁ wtfjs を見ている人だったら「ああなんだ，いつしかやってみた&lt;a href=&quot;http://utf-8.jp/public/jsfuck.html&quot;&gt;JsF*ck&lt;/a&gt;じゃんか」で終わる話でもあります&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;1. &#39;constructor&#39; で無名関数が作れる&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;&#39;constructor&#39;[&#39;constructor&#39;][&#39;constructor&#39;](&#39;alert(&quot;Hello, World!&quot;);&#39;)();&lt;/pre&gt;
&lt;p&gt;JavaScript のオブジェクトは，自身のプロパティを&lt;code&gt;[&#39;&lt;i&gt;&amp;lt;property&amp;gt;&lt;/i&gt;&#39;]&lt;/code&gt;で呼び出せるのはご存知かと思います.つまり &lt;strong&gt;&quot;Stringインスタンスのコンストラクタのコンストラクタを呼べばそりゃ Function クラスのコンストラクタに到達するよね&quot;&lt;/strong&gt; という話&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;// 当然 String オブジェクト
var c = &#39;constructor&#39;;

// String.constructor(), つまり Function オブジェクト
var c2 = &#39;constructor&#39;[&#39;constructor&#39;];

// Function.constructor(), なのでここに文字列渡すと Function オブジェクトが作れる
var c3 = &#39;constructor&#39;[&#39;constructor&#39;][&#39;constructor&#39;]; 

// 結果として，Hello, World がアラートできる
c[c][c](&#39;alert(&quot;Hello, World!&quot;);&#39;)();&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;2. [] にまつわる挙動&lt;/h3&gt;
&lt;p&gt;JavaScript の空配列リテラルに関する挙動には変なものが多いんだけど，おかげでだいぶ F*ck な事ができる&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;[] === [] // #=&gt; false
[] == [] // #=&gt; false
![] // #=&gt; false
[] == true // #=&gt; false

// もうこの時点で泣きそうだが

[,] // #=&gt; []
[].length // #=&gt; 0
[,].length // #=&gt; 1

+[] // #=&gt; 0
[]+[] // #=&gt; &quot;&quot;
!+[] // #=&gt; true
!+![] // #=&gt; true
+!+![] // #=&gt; 1

[][[]] // #=&gt; undefined

![]+[]    // #=&gt; &quot;false&quot;
!![]+[]   // #=&gt; &quot;true&quot;
[][[]]+[] // #=&gt; &quot;undefined&quot;
&lt;/pre&gt;
&lt;p&gt;この時点で頭がイッてる人なら&lt;strong&gt;&quot;ああ，じゃあ空配列リテラルから true, false, undefined, [0-9], a, d, e, f, i, l, n, r, s, t, u のアルファベットが取れるのか&quot;&lt;/strong&gt;と気づくでしょう&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q.\uXXXX の書式で任意の文字コードいくらでも作り放題じゃね?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A.やりましたね&lt;/strong&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;3. 結果としてなんでも出来てしまった空配列リテラル&lt;/h3&gt;
&lt;p&gt;さて，この時点でUnicodeが空配列だけでもう普通に作れる事がわかったので，ソースコード自体をほぼ空配列だけで書ける．が，もう一つ，先の &#39;constroctor&#39; の例を考えてみよう．これも空配列で作れたらいいけど，c と o が無い．これをどうするか．&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Array.filter&lt;/code&gt; を呼べばいいのでは&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;
[][(![]+[])[0]+    // f (&#39;f&#39;alse)
   ([][[]]+[])[5]+ // i (undef&#39;i&#39;ned)
   (![]+[])[2]+    // l (fa&#39;l&#39;se)
   (!![]+[])[0]+   // t (&#39;t&#39;rue)
   (!![]+[])[3]+   // e (tru&#39;e&#39;)
   (!![]+[])[1]    // r (t&#39;r&#39;ue)
] // [][&#39;filter&#39;] =&gt; Array.filter()
+[]; // &quot;function filter { [native code] }&quot; の 文字列が返ってくる
&lt;/pre&gt;
&lt;p&gt;これで &#39;function&#39; の文字列から c と o が文字としてとれてしまうので，以下のコードで&#39;constructor&#39; が作れてしまう&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;_=-~-~-~[]; // #=&gt; 3
__=[][(![]+[])[_-_]+([][[]]+[])[_+_-_/_]+(![]+[])[_-_/_]+(!![]+[])[_-_]+(!![]+[])[_]+(!![]+[])[_/_]]+[]; // #=&gt; &quot;function filter { [native code] }&quot;
___=__[_]+__[_+_]+__[_-_/_]+(![]+[])[_]+(!![]+[])[_-_]+(!![]+[])[_/_]+(!![]+[])[_-_/_]+__[_]+(!![]+[])[_-_]+__[_+_]+(!![]+[])[_/_]; // #=&gt; &quot;constructor&quot;
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-~-~-~[]&lt;/code&gt; の部分はビット反転と正負反転を繰り返して0から3を作っている&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;
~[]         // -1
-~[]        // 1
-~-~[]      // 2
_=-~-~-~[]; // #=&gt; 3
_-_         // 3 - 3         #=&gt; 0
_/_         // 3 / 3         #=&gt; 1
_-_/_       // 3 - 3 / 3     #=&gt; 2
_+_/_       // 3 + 3 / 3     #=&gt; 4
_+_-_/_     // 3 + 3 - 3 / 3 #=&gt; 5
_+_         // 3 + 3         #=&gt; 6
_+_+_/_     // 3 + 3 + 3 / 3 #=&gt; 7
_*_-_/_     // 3 * 3 - 3 / 3 #=&gt; 8
_*_         // 3 * 3         #=&gt; 9 
&lt;/pre&gt;
&lt;p&gt;これで，&lt;code&gt;&#39;constructor&#39;[&#39;constructor&#39;][&#39;constructor&#39;]&lt;/code&gt; は作れるし，中で呼ぶソースコードの任意の文字は
&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;_=-~-~-~[];
// \u3042 , &#39;あ&#39; の Unicode
&#39;&#39;+&#39;\\&#39;+(!![]+[])[_-_/_]+(_)+(_-_)+(_+_/_)+(_-_/_)
&lt;/pre&gt;
のようにする. これで JavaScript が書けるわけだけど，ものすごい時間の無駄ですね!&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;参考リンク&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://utf-8.jp/public/jsfuck.html&quot;&gt;JsF*ck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://developer.cybozu.co.jp/takesako/2010/05/python-syntaxer.html&quot;&gt;Python SyntaxError Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://wtfjs.com/2010/07/12/fail&quot;&gt;wtfjs | fail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://wtfjs.com/2010/07/11/length-of-what-now&quot;&gt;wtfjs | length of what now&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://wtfjs.com/2010/06/09/function-in-ur-string&quot;&gt;wtfjs | function in ur string&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/6795661564139296462/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/05/wtf-javascript-empty-array-programming.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/6795661564139296462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/6795661564139296462'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/05/wtf-javascript-empty-array-programming.html' title='wtf!? JavaScript Empty Array Programming'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-4414645025930501190</id><published>2011-05-17T07:28:00.002+09:00</published><updated>2011-05-17T15:57:49.913+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BDD"/><category scheme="http://www.blogger.com/atom/ns#" term="CoffeeScript"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><category scheme="http://www.blogger.com/atom/ns#" term="Vows"/><title type='text'>Vows で  クライアントサイドの CoffeeScript / JavaScript のテストをする時の Tips</title><content type='html'>&lt;h4&gt;0. &lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt; の &lt;a href=&quot;http://vowsjs.org/&quot;&gt;Vows&lt;/a&gt; フレームワークがとてもかわいい&lt;/h4&gt;
&lt;p&gt;JavaScript のTDD, BDDフレームワークはたくさんあるけど，テスト結果の見た目の良さと記述の楽さで &lt;a href=&quot;http://vowsjs.org/&quot;&gt;Vows&lt;/a&gt; というフレームワークを使っている．&lt;a href=&quot;http://relishapp.com/rspec&quot;&gt;RSpec&lt;/a&gt; からの影響を受けていて，Rack アプリケーションのテストと同じような感覚で書ける，というのが良い．&lt;/p&gt;
&lt;p&gt;たとえばこんな感じでテストコードを CoffeeScript で書く&lt;br&gt;
&lt;img src=&quot;http://cache.gyazo.com/4fab10ec061d2e81a7b922034aa1c04a.png&quot;&gt;&lt;br&gt;
で &lt;code&gt;--spec&lt;/code&gt; オプションをつけて実行するとこういう風に表示してくれる．&lt;/p&gt;
&lt;img src=&quot;http://gyazo.com/3ab3bcabc78f39d81d68f91134871b6c.png&quot;&gt;&lt;br&gt;
assertion でテストが通らないと黄色く，内部エラーの場合は赤にラベルが表示される．
しっかり全部通すと&lt;br&gt;
&lt;img src=&quot;http://gyazo.com/bd9dbadad88529e0f20f1f134eca54c7.png&quot;&gt;&lt;br&gt;
このように表示される．見た目が良いし，ラベリングを丁寧にやるとテストの内容がわかりやすい．
&lt;p&gt;元々 &lt;a href=&quot;http://nodejs.org/&quot;&gt;node.js&lt;/a&gt; のテスト用なので，&lt;code&gt;require&lt;/code&gt; &lt;code&gt;exports&lt;/code&gt; など， pure JavaScript には無いグローバル変数が必要だけど，ちょっとした工夫でクライアントサイドの JavaScript / &lt;a href=&quot;http://jashkenas.github.com/coffee-script/&quot;&gt;CoffeeScript&lt;/a&gt; もテストできるようになるよ．という話&lt;/p&gt;
&lt;h4&gt;1. JavaScript/CoffeeScriptのファイルの最後に&lt;code&gt;export&lt;/code&gt;すれば良し&lt;/h4&gt;
&lt;p&gt;テストしたいオブジェクト群やメソッド群はこうやるだけで ok. アプリケーションのルートディレクトリ(&lt;code&gt;$APP_ROOT&lt;/code&gt;)に対して，テストする対象のスクリプトは &lt;code&gt;$APP_ROOT/public/(js|coffee)/&lt;/code&gt; 以下にあるとする&lt;/p&gt;
&lt;h5&gt;1-1-a. JavaScript の場合 ($APP_ROOT/public/js/hoge.js)&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// テストしたいオブジェクト
var Hoge = function(id, name){
    this.id = id;
    this.name = name;
    return this;
};
//...

if (typeof exports != &#39;undefined&#39; &amp;&amp; exports !== null) {
   exports.Hoge = Hoge;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;1-1-b. CoffeeScript の場合 ($APP_ROOT/public/coffee/hoge.coffee)&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;# テストしたいオブジェクト
class Hoge =
    constructor: (@id, @name) -&gt; @
    #...

exports?.Hoge = Hoge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;で，テストコードには以下のように記述する. アプリケーションのルートディレクトリ(&lt;code&gt;$APP_ROOT&lt;/code&gt;)に対して，テストコードの場所が &lt;code&gt;$APP_ROOT/spec/(js|coffee)/&lt;/code&gt; 以下にあるとする&lt;/p&gt;
&lt;h5&gt;1-2-a. JavaScript の場合 ($APP_ROOT/spec/js/hoge_spec.js)&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// テストしたいコードを相対パスで指定して require する
var vows, assert, Hoge;
vows = require(&#39;vows&#39;);
assert = require(&#39;assert&#39;);
Hoge = require(&#39;../../public/js/hoge&#39;).Hoge;

vows.describe(&#39;Hoge&#39;).addBatch({
    &#39;a instance&#39;:{
        topic: new Hoge(1, &#39;hoge&#39;),
        &#39;has id&#39;: function(hoge){ assert.equal(hoge.id, 1);},
        &#39;has name&#39;: function(hoge){ assert.equal(hoge.name, &#39;hoge&#39;);}
    },
    // テストを上記と同じように書いていく
}).export(module);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;1-2-b. CoffeeScript の場合 ($APP_ROOT/spec/coffee/hoge_spec.coffee)&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;# テストしたいコードを相対パスで指定して require する
vows = require &#39;vows&#39;
assert = require &#39;assert&#39;
Hoge = require(&#39;../../public/coffee/hoge&#39;).Hoge

vows.describe(&#39;Hoge&#39;).addBatch
    &#39;a instance&#39;:
        topic: new Hoge(1, &#39;hoge&#39;)
        &#39;has id&#39;: (hoge) -&gt; assert.equal hoge.id, 1
        &#39;has name&#39;: (hoge) -&gt; assert.equal hoge.name, &#39;hoge&#39;
    # テストを上記と同じように書いていく
.export module
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;「なんで Vows なの?」&lt;/strong&gt; ってそもそもの疑問については，&lt;strong&gt;「最初っから CoffeeScriptでテスト対象もテストコードもそのまんま書いて実行できるから」&lt;/strong&gt;．他のフレームワークに &lt;a href=&quot;http://pivotal.github.com/jasmine/&quot;&gt;Jasmine&lt;/a&gt; もあるけど，あれよりも簡単にコマンドで
&lt;pre&gt;&lt;code&gt;$ vows *_spec.* -w&lt;/code&gt;&lt;/pre&gt;
と実行しておけば watch モードとして実行され，テストコードの更新の度にテストを回し直してくれるので，ガンガンテストコードが書ける．Jasmine みたくいちいちブラウザで実行結果を開いたりしなくても良い． RSpec の &lt;code&gt;.autotest&lt;/code&gt; の中にこのコマンドを回させてしまうのもアリ．ただ，watch モードではテスト対象そのものの更新はチェックされないので，TDD でガンガンやってるって感はしない．特にテスト対象側の undefined エラーで落ちたりする場合なんかだと途中で止まったりする(この場合，Ctrl+C で落とさないとダメ)．&lt;/p&gt;
&lt;p&gt;普段は
&lt;ol&gt;
&lt;li&gt;テストコードを先に書く&lt;/li&gt;
&lt;li&gt;テストのあるディレクトリで&lt;code&gt;$ vows *_test.* --spec&lt;/code&gt; でテスト走らせてみる&lt;/li&gt;
&lt;li&gt;コケたり，エラーで落ちた箇所を修正する&lt;/li&gt;
&lt;li&gt;もっかいテスト走らせる&lt;/li&gt;
&lt;/ol&gt;
を繰り返してる．
&lt;/p&gt;
&lt;h4&gt;2. クライアントサイドで，先に読みこまれているはずの JavaScript / CoffeeScript があった場合&lt;/h4&gt;
&lt;p&gt;ここまでは普通に独立した各スクリプトの場合のテストだけど，他の JavaScript ライブラリをHTML側で先読みされていて，そのグローバルオジェクトがそのスクリプトで利用されている場合．&lt;/p&gt;
&lt;p&gt;たとえば &lt;a href=&quot;http://docs.jquery.com/Main_Page&quot;&gt;jQuery&lt;/a&gt; や &lt;a href=&quot;http://documentcloud.github.com/underscore/&quot;&gt;Underscore.js&lt;/a&gt; なんかのライブラリのメソッドを利用していた場合など．&lt;/p&gt;
&lt;p&gt;この場合，Vows からはライブラリからのグローバルオブジェクト，メソッドはわからないので，実行中にコケたりする．なので，テストの方のスクリプトに &lt;code&gt;require&lt;/code&gt; を書いておかないといけない．以下，スクリプトファイルと同じディレクトリにそれらライブラリがあると仮定した場合のコード．&lt;/p&gt;
&lt;h5&gt;2-1. JavaScript の場合 (hoge.js)&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;// 使ってるライブラリを require する
if (typeof require == &#39;function&#39;) {
    if (typeof $ == &#39;undefined&#39; || $ === null) {
       $ = require(&#39;./jquery&#39;).$;
    }
    if (typeof _ == &#39;undefined&#39; || _ === null) {
        _ = require(&#39;./underscore&#39;)._;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2-2. CoffeeScript の場合 (hoge.coffee)&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;# 使ってるライブラリを require する
if require?
    $ ?= require(&#39;./jquery&#39;).$
    _ ?= require(&#39;./underscore&#39;)._
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;たったこれだけ．この場合， pure JavaScript であろうと，ブラウザ実行時には &lt;code&gt;require&lt;/code&gt;, &lt;code&gt;exports&lt;/code&gt; 共に undefined なのでそのままスルーされる，という仕組み&lt;/p&gt;
&lt;h4&gt;3. まとめ&lt;/h4&gt;
&lt;p&gt;すごく単純な話で，ブラウザ側の実行時にひっかかって困らないように &lt;code&gt;require&lt;/code&gt;, &lt;code&gt;exports&lt;/code&gt; を
&lt;pre&gt;&lt;code&gt;if (typeof ... != &#39;undefined&#39; &amp;&amp; ... !== null){ ... }&lt;/code&gt;&lt;/pre&gt;
で囲って，その中でテストの為に渡す処理を書けば良し．ちなみに，デプロイ時に &lt;abbr title=&quot;Google Closure Compiler&quot;&gt;&lt;a href=&quot;http://code.google.com/closure/compiler/&quot;&gt;GCC&lt;/a&gt;&lt;/abbr&gt; で圧縮・最適化をかける際，このあたりの記述はスルーされて圧縮後には消えるはず．&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/4414645025930501190/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/05/vows-coffeescript-javascript-tips.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4414645025930501190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4414645025930501190'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/05/vows-coffeescript-javascript-tips.html' title='Vows で  クライアントサイドの CoffeeScript / JavaScript のテストをする時の Tips'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-6972986877552678121</id><published>2011-04-22T13:43:00.000+09:00</published><updated>2011-04-22T13:43:42.457+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Animation"/><category scheme="http://www.blogger.com/atom/ns#" term="CoffeeScript"/><category scheme="http://www.blogger.com/atom/ns#" term="CSS3"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><category scheme="http://www.blogger.com/atom/ns#" term="jQuery"/><title type='text'>CSS3 Transition のための CoffeeScript / JavaScript</title><content type='html'>&lt;h3&gt;CSS3 Transition がほぼモダンブラウザで使えるようになってきた&lt;/h3&gt;
&lt;p&gt;昨年 CSS3 の仕様にアニメーションプロパティが加わって，一時的な熱狂があったわけだけど，それから一年してどのブラウザにも一応の CSS &lt;a href=&quot;http://www.w3.org/TR/css3-animations/&quot;&gt;Animation&lt;/a&gt; / &lt;a href=&quot;http://www.w3.org/TR/css3-transitions/&quot;&gt;Transition&lt;/a&gt; が出来るようになってきた．しかし，W3C の仕様のとおり &lt;code&gt;transition: &amp;lt;property&amp;gt; &amp;lt;duration&amp;gt; &amp;lt;timing-function&amp;gt; &amp;lt;deley&amp;gt;&lt;/code&gt; を指定して動くわけでなく，&lt;code&gt;-moz-&lt;/code&gt; なり &lt;code&gt;-webkit-&lt;/code&gt; なりのプレフィックスがまだ必要な現状だったりする．ん? ああ，IE お前は座ってろ&lt;/p&gt;
&lt;h3&gt;CSS プロパティを結局 jQuery で差し込む方向で&lt;/h3&gt;
&lt;p&gt;最近，やっと自分のサイトを開発しようと思って，Sinatra + mongoDB で，View を Haml Sass CoffeeScript + jQuery で作っている最中なのだけど，アニメーション効果をつけるにあたって，どうも Sass の mixin では可変長引数に対応していないため，思うような効果を出せなかった．また，&lt;code&gt;.fade-in&lt;/code&gt; &lt;code&gt;.fade-out&lt;/code&gt; とかそんなクラスをマークアップするのは HTML のモデル的に醜いとか思った．ということで jQuery#css メソッドを使ってアニメーションを制御する方向にした&lt;/p&gt;
&lt;h3&gt;ダルいポイント2つ&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;h4&gt;クロスブラウザ対応の為にたくさんプロパティを生成しないといけない&lt;/h4&gt;
&lt;ul&gt;
&lt;dt&gt;-moz-transition:&lt;/dt&gt;&lt;dd&gt;Firefox用．4になったらこういうプロパティいくらか無くて良いと期待してたのだけど…&lt;/dd&gt;
&lt;dt&gt;-webkit-transition:&lt;/dt&gt;&lt;dd&gt;Chome/Safari/WebKit用．これももうそろそろ要らなくなるだろとタカをくくっていたらまだだった&lt;/dd&gt;
&lt;dt&gt;-o-transition:&lt;/dt&gt;&lt;dd&gt;Opera用 たしか HTML5 CSS3 に力入れていたはず．が，しかし未だにグラデーション実装してくれないよね&lt;/dd&gt;
&lt;dt&gt;transition:&lt;/dt&gt;&lt;dd&gt;本来ならこれ一つでおっけーなんだけど，それはいつの日やら&lt;/dd&gt;
&lt;/ul&gt;
&lt;p&gt;これを一行一行書くのはダルい．しかも次の項のようにずらずらと設定を書く必要がある&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;トランジションを複数のプロパティに一度にかけないと変な動きする&lt;/h4&gt;
&lt;p&gt;トランジションを効かせるCSSプロパティが一つであれば，クラスを作って sass の mixin 呼ぶだけで事足りる．&lt;a href=&quot;https://github.com/madr/css3-sass-mixins sass-mixin&quot;&gt;GitHub にある madr 氏の css3-sass-mixins&lt;/a&gt; とかを &lt;code&gt;@import&lt;/code&gt; すれば幸せになる．&lt;p&gt;
&lt;p&gt;んが，こういう風に二つ以上のプロパティを同時に作用させたいって時がある
&lt;pre&gt;&lt;code&gt;
.fade-in {
    visibility: visible;
    opacity: 1;
    transition: visibility 1s linear, opacity 1s linear;
    -o-transition: visibility 1s linear, opacity 1s linear;
    -webkit-transition: visibility 1s linear, opacity 1s linear;
    -moz-transition: visibility 1s linear, opacity 1s linear;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先のmixinにはこうした複数のプロパティ適用に対して有効でなく，まぁ &lt;code&gt;transition-property:&lt;/code&gt; には &lt;code&gt;all&lt;/code&gt; って特殊キーワードが使えるんだけど，それを指定してしまうと，継承してる，トランジションが効くすべてのプロパティまでアニメ〜ションしてしまう．あとそのせいで確実に重くなる．試しに &lt;code&gt;all&lt;/code&gt; と指定してブラウザの文字を大きくしたり小さくしてみるといい．気持ち悪いから．というわけで「複数のプロパティを一度にトランジションかけたいが，必要なモンだけにしたい」というのをなんとかする．&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;jQuery#css に渡すオブジェクトを作るメソッド.coffee&lt;/h3&gt;
&lt;p&gt;雑ではあるけど，こんな風に書いた．CoffeeScript で&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/936007.js?file=animateCSSObject.coffee&quot;&gt;&lt;/script&gt;
&lt;p&gt;使い方は以下のようにやる．あ，以下のコードも Js より短く書けるから CoffeeScript で&lt;/p&gt;
&lt;pre&gt;
style =
  visibility:&#39;visible&#39;
  opacity:1
  max-height: 100%
// .fade-in に トランジションを効かせたい
$(.hoge).css animateCSS style, &#39;1s&#39;, &#39;ease-in-out&#39;, 0.5
&lt;/pre&gt;
&lt;h3&gt;jQuery#css に渡すオブジェクトを作るメソッド.js&lt;/h3&gt;
&lt;p&gt;CoffeeScript 使わねーよって人は JavaScript でコンパイルした方も記載しておくので，こちらを参考にして直して使えば良い&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/936030.js?file=animateCSSObject.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;CoffeeScript の方がやりたい事を手短に書ける分だけ気持ちが良い．ただこのまんま HTML タグで呼び出すとデバッグできないんだよね．それが課題だな&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/6972986877552678121/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/04/css3-transition-coffeescript-javascript.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/6972986877552678121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/6972986877552678121'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/04/css3-transition-coffeescript-javascript.html' title='CSS3 Transition のための CoffeeScript / JavaScript'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-9219092837104307486</id><published>2011-04-19T01:10:00.001+09:00</published><updated>2011-05-31T11:34:00.352+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Dropbox"/><category scheme="http://www.blogger.com/atom/ns#" term="vim"/><category scheme="http://www.blogger.com/atom/ns#" term="zsh"/><category scheme="http://www.blogger.com/atom/ns#" term="メモ"/><title type='text'>dotfile (~/.file) の類を Dropbox で一元管理</title><content type='html'>&lt;h3&gt;あらまし - 複数のマシンの環境を統一しておきたい&lt;/h3&gt;
&lt;p&gt;Dropbox で dotfile をまとめて持っておいて，複数のマシンでも同じ環境で作業できるようにしてる人ってもう既にたくさんいるし，新規性のあることではまったくないのだけど，ようやく自分もそこらへんの整備が終わったんで，記録として残しておく．&lt;/p&gt;
&lt;h3&gt;ドットつけないで，共有するファイルやディレクトリを Dropbox へ&lt;/h3&gt;
&lt;p&gt;Dropbox に共有するファイル群を入れる dotfiles ディレクトリを作る．&lt;/p&gt;
&lt;p&gt;たとえば SKK だったら .skk-jisyo を my-jisyo とかにして，~/Dropbox/dotfiles/skk/my-jisyo としてつっこむ．どうせ後でドットファイルとしてシンボリックリンクしてしまうので気にせずどんどんやる．例として自分の場合を &lt;a href=&quot;https://github.com/VoQn/dotfiles&quot;&gt;GitHub に晒しておく&lt;/a&gt;．ここには別途必要なプラグインとか，プライベートな設定ファイルとかは &lt;code&gt;gitignore&lt;/code&gt; で弾いてるからそのままだと使えないのでご了承を&lt;/p&gt;
&lt;h3&gt;個別の環境用の設定の余地を残しておく&lt;/h3&gt;
&lt;p&gt;vim しかり zsh しかり，個別で環境変数や設定が別に必要な場合があるので，それ用に個別環境をロードする仕組みを各々のドットファイルに記述しておく．また，個別環境の下の履歴やバックアップまで共有するとおかしな事になるし，Dropbox も後々圧迫される．ので，履歴やバックアップを個々で持つように設定しておく．&lt;/p&gt;
&lt;p&gt;たとえば zshrc の場合
&lt;pre class=&quot;shell&quot; name=&quot;code&quot;&gt;
# ... 先頭あたり
export ZSH=$HOME/.zsh # バラけないようにこんな風にまとめてしまう
source $ZSH/env_common.sh # 共通の環境変数を env_common.sh に書く

# ... 補完とか
autoload -U compinit
compinit -d $ZSH/compdump 

# ... どこでも良いけど，HISTSIZE を指定する前とかで
HISTFILE=$ZSH/history

# ... ファイルの末尾あたりに
source $ZSH/env.sh # 個別の環境変数をこっちに書く
&lt;/pre&gt;
としておくなど．当然，この場合 ~/.zsh を mkdir する必要がある．後のリンクを張る項で説明．なお，上記の例だと ~/.zsh/env_common.sh も Dropbox で共有する．&lt;/p&gt;
&lt;h3&gt;個別の環境へシンボリックリンクを張る&lt;/h3&gt;
&lt;p&gt;一回 リンク用のシェルスクリプト書いておくと楽になる．ただ，間違って元の環境を破壊しかねないので，実行する前に以前の環境とかをちゃんと確認してからするように．&lt;/p&gt;
&lt;p&gt;新しいマシンで何も手つかずの環境とかに以下のスクリプトを走らせる．例として vim&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/925581.js?file=gistfile1.vim&quot;&gt;&lt;/script&gt;
&lt;p&gt;&lt;code&gt;ln&lt;/code&gt; コマンドは，リンク元を絶対パスで記述しないと駄目なので &lt;code&gt;$PWD&lt;/code&gt; を使ってる．当たり前だけど，カレントディレクトリでこのパスが変わるので，この場合だと ~/Dropbox/dotfiles/ に &lt;code&gt;cd&lt;/code&gt; とか &lt;code&gt;pushd&lt;/code&gt; してから走らせる．他の例は先にリンクした GitHub に それぞれの場合のスクリプトがあるのでそれを見てみればよろしいかと．&lt;/p&gt;
&lt;h3&gt;After...&lt;/h3&gt;
&lt;p&gt;シンボリックリンクがちゃんと正しく張り終わったら，以下のような状態になる&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;どのマシンでも Vim などのエディタが同じ環境，プラグイン利用で使える!&lt;/li&gt;
&lt;li&gt;SKK の変換履歴や登録が共有されてるので使えば使うほど便利になる!&lt;/li&gt;
&lt;li&gt;コマンド履歴やバックアップは別々なんだけど，コマンドラインの利便性はどこでも一緒!&lt;/li&gt;
&lt;li&gt;Dropbox でそれらが同期しているので，自分から手動で更新する必要が無い!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;とても便利で幸せになれました&lt;/p&gt;
&lt;h3&gt;補足その1 - Dropbox が使えない環境では&lt;/h3&gt;
&lt;p&gt;VPS とか，Dropbox インストールできない環境下ではどうするかというと，自分の場合は ~/Dropbox/dotfiles 自体を git で管理しているので，そこから clone できる．ただ，gitignore してるプラグインの類はそれで入らないので，zip してから sftp で送ってしまえば，リンク張るのもスクリプトでそこまで面倒ではないかと．しかし課題として残るかも (Github をリモートにしないのであれば全部プラグインも &lt;code&gt;git add&lt;/code&gt; してしまえという選択もアリ)&lt;/p&gt;
&lt;h3&gt;補足その2 - アプリケーションは当然入れてないと使えない&lt;/h3&gt;
&lt;p&gt;当然 Vim が入ってないとか，SKK が入ってないとか，そこらへんまで当然世話できないので，環境が手に入ったら，諸々必要なアプリケーションは入れておく手間はある．Vimperator の為に Firefox 入れるとか．&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/9219092837104307486/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/04/dotfile-file-dropbox.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/9219092837104307486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/9219092837104307486'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/04/dotfile-file-dropbox.html' title='dotfile (~/.file) の類を Dropbox で一元管理'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-4606136900181127272</id><published>2011-04-15T10:24:00.000+09:00</published><updated>2011-04-15T10:24:16.188+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="MacOSX"/><category scheme="http://www.blogger.com/atom/ns#" term="NetBeans"/><category scheme="http://www.blogger.com/atom/ns#" term="pear"/><category scheme="http://www.blogger.com/atom/ns#" term="pecl"/><category scheme="http://www.blogger.com/atom/ns#" term="PHP"/><category scheme="http://www.blogger.com/atom/ns#" term="PHPUnit"/><category scheme="http://www.blogger.com/atom/ns#" term="メモ"/><title type='text'>PHPUnit / Phing を MacOSX で実行できるようにして，NetBeans で開発するための設定メモ</title><content type='html'>&lt;div&gt;
&lt;h3&gt;あらすじ&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://voqn.blogspot.com/2011/04/phpunit-phing-macosx-php.html&quot;&gt;前回のエントリ&lt;/a&gt;でMacOS マシンにも最新の PHP と PHPUnit, Phing が入り，開発途中になって pear や pecl のライブラリをインストールできるようになったのだけど，このままだとまだ NetBeans でホイホイ開発できるようにはなってない．このエントリで残りの環境構築をやる&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;/etc/php.ini をコピー&lt;/h3&gt;
&lt;p&gt;前回のビルドで，PHP は再インストールしたわけだけど，実は MacOS にはデフォルトの /etc/php.ini ファイルが無かったりする．(これは今回びっくりした) なので，pecl のライブラリなんかを呼び出せないままになっているのが確認できる．というわけで前回の PHP をビルドしたフォルダから php.ini-production なんかをコピーして編集する．(php.ini-development でも良いけど，別に PHP そのものをハック対象にしてない場合はこれで十分だろう)&lt;/p&gt;
&lt;p&gt;前回で php のソースを &lt;code&gt;~/tmp&lt;/code&gt; に展開しているので，その前提で作業ログを載せる&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
$ pushd ~/tmp/php-5.3.6
$ find . -name &#39;php.ini*&#39; -print
./php.ini-development
./php.ini-production
$ sudo cp php.ini-production /etc/php.ini
$ pushd /etc
$ sudo vim /etc/php.ini
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;/etc/php.ini を編集&lt;/h3&gt;
&lt;p&gt;拡張の指定は&lt;code&gt;extension_dir&lt;/code&gt;と&lt;code&gt;extension&lt;/code&gt;で指定できるけど，Xdebug などは Zend_extension なので絶対パスで記述しないといけない事に注意．また，pecl のデフォルトインストール先は /usr/lib/php/extensions/ じゃなかったりするのでそれもあわせて．&lt;/p&gt;
&lt;pre class=&quot;ini&quot; name=&quot;code&quot;&gt;
;;; /etc/php.ini
;;; だいたい 805 行目くらい
; Directory in which the loadable extensions (modules) reside.                                                                         
; http://php.net/extension-dir
; extension_dir = &quot;./&quot;
; On windows:
; extension_dir = &quot;ext&quot;
;;; 拡張のインストール先を追記
extension_dir = &quot;/usr/lib/php/extensions/no-debug-non-zts-20090626/&quot; 
;;; Zend_extension をそれぞれ絶対パスで追記
zend_extension = &quot;/usr/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so&quot; 
 
;;; XDebug 用の追記
xdebug.remote_enable = On
&lt;/pre&gt;
&lt;p&gt;当然 TimeZone の指定とか，他にも色々この時点でしてもいいけど，本題からそれるので省略．ひとまず これで拡張の件は済んだ．この時点で，IDE 使わない開発が普通にできる．Phing も入れてあるので，プロジェクトのルートディレクトリに build.xml を書いて定期的に phing すれば良い&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3&gt;NetBeans 側の設定&lt;/h3&gt;
&lt;p&gt;前回でそれぞれのバイナリやライブラリはこんな感じにインストールされているはず．まぁそのまんまなので簡単&lt;/p&gt;
&lt;ul&gt;
&lt;dt&gt;php&lt;/dt&gt;&lt;dd&gt;/usr/bin/php&lt;/dd&gt;
&lt;dt&gt;phpunit(コマンドの方)&lt;/dt&gt;&lt;dd&gt;/usr/bin/phpunit&lt;/dd&gt;
&lt;dt&gt;PHPUnit(ライブラリ)&lt;/dt&gt;&lt;dd&gt;/usr/lib/php/PHPUnit&lt;/dd&gt;
&lt;/ul&gt;
&lt;p&gt;これを NetBeans で利用できるように設定する．&lt;/p&gt;
&lt;ol&gt;
&lt;lh&gt;NetBeans の [環境設定] の PHP の項目&lt;/lh&gt;
&lt;li&gt;一般:PHP 5 インタプリタを次に指定 → [/usr/bin/php]&lt;/li&gt;
&lt;li&gt;一般:大域インクルードパスをフォルダ追加 → [usr/lib/php/PHPUnit]&lt;/li&gt;
&lt;li&gt;単体テスト:PHPUnit スクリプトを次に指定 → [/usr/bin/phpunit]&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;大域インクルードパスに &lt;code&gt;/usr/lib/php&lt;/code&gt;を指定しても動くけど，他の余分なライブラリまで見る分だけ重くなるので，それはプロジェクト単位で追加した方がいい&lt;/p&gt;
&lt;p&gt;これでちゃんとひととおり NetBeans で PHP プロジェクトを TDD で開発できるようになった．あとはリモートのリポジトリを Jenkins なんかで監視すると良い&lt;/p&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/4606136900181127272/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/04/phpunit-phing-macosx-netbeans.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4606136900181127272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4606136900181127272'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/04/phpunit-phing-macosx-netbeans.html' title='PHPUnit / Phing を MacOSX で実行できるようにして，NetBeans で開発するための設定メモ'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-7856727791629183796</id><published>2011-04-09T01:42:00.001+09:00</published><updated>2011-04-22T14:38:56.434+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Homebrew"/><category scheme="http://www.blogger.com/atom/ns#" term="MacOSX"/><category scheme="http://www.blogger.com/atom/ns#" term="NetBeans"/><category scheme="http://www.blogger.com/atom/ns#" term="pear"/><category scheme="http://www.blogger.com/atom/ns#" term="pecl"/><category scheme="http://www.blogger.com/atom/ns#" term="Phing"/><category scheme="http://www.blogger.com/atom/ns#" term="PHP"/><category scheme="http://www.blogger.com/atom/ns#" term="PHPUnit"/><category scheme="http://www.blogger.com/atom/ns#" term="メモ"/><title type='text'>PHPUnit / Phing を MacOSX で実行できるようにする為の PHP ビルド・インストールメモ</title><content type='html'>&lt;h3&gt;あらすじ&lt;/h3&gt;
&lt;p&gt;PHP で開発するにあたって，「やっぱり複数人で開発するし，NetBeans とか IDE 使いたいよねー」的な話になったので，NetBeans を入れて，これまでリモートのサーバー上で Phing 使ってテストしてたのだけど，それをローカルマシンでやるにあたって，PHPUnit をインストールすることになった．バンドルされてる PHP も古いバージョンだし．&lt;/p&gt;
&lt;p&gt;しかし，SnowLeopard に標準でインストールされている PHP には &lt;code&gt;--with-pear&lt;/code&gt; 付きでビルドされておらず，しかも，バンドルで付けられていたビルドオプションをコピペして，追加して &lt;code&gt;./configure&lt;/code&gt; しても，&lt;code&gt;--with-jpeg-dir&lt;/code&gt; とかでコケるという愉快なトラブルでえらく時間かかった．&lt;/p&gt;
&lt;p&gt;というわけで，ビルドちゃんと出来るようになるまでの手順ログを書いておく&lt;/p&gt;

&lt;h3&gt;入れたい/使いたいモノ&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://www.php.net/&quot;&gt;PHP 5.3.6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sebastianbergmann/phpunit/&quot;&gt;PHPUnit 3.5.13&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.xdebug.org/&quot;&gt;Xdebug (PHPUnit のカバレッジレポートに要る) 2.1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.phing.info/trac/&quot;&gt;Phing (ビルドツール) 2.4.4&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;こちらがわの環境&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;MacBook 6,1 (2009/10モデル)&lt;/li&gt;
&lt;li&gt;MacOSX 10.6.7 SnowLeopard&lt;/li&gt;
&lt;li&gt;XCode 3.2.5 (XCode Tools インストール済み)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://mxcl.github.com/homebrew/&quot;&gt;Homebrew 0.8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ぶっちゃけ MacPorts だったら何も問題なかった．でも Homebrew の方が使っててステキなんですよ．アホみたいに /opt/local/ にファイルが入ったりしないし，アップデートも早く終わるし&lt;/p&gt;
&lt;h3&gt;ビルドオプション&lt;/h3&gt;
&lt;h4&gt;バンドル時のオプション&lt;/h4&gt;
&lt;pre&gt;
&lt;code&gt;
./configure \
--prefix=/usr \
--mandir=/usr/share/man \
--infodir=/usr/share/info \
--disable-dependency-tracking \
--sysconfdir=/private/etc \
--with-apxs2=/usr/sbin/apxs \
--enable-cli \
--with-config-file-path=/etc \
--with-libxml-dir=/usr \
--with-openssl=/usr \
--with-kerberos=/usr \
--with-zlib=/usr \
--enable-bcmath \
--with-bz2=/usr \
--enable-calendar \
--with-curl=/usr \
--enable-exif \
--enable-ftp \
--with-gd \
--with-jpeg-dir=/usr/local \
--with-png-dir=/usr/local \
--enable-gd-native-ttf \
--with-ldap=/usr \
--with-ldap-sasl=/usr \
--enable-mbstring \
--enable-mbregex \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-mysql-sock=/var/mysql/mysql.sock \
--with-iodbc=/usr \
--enable-shmop \
--with-snmp=/usr \
--enable-soap \
--enable-sockets \
--enable-sysvmsg \
--enable-sysvsem \
--enable-sysvshm \
--enable-wddx \
--with-xmlrpc \
--with-iconv-dir=/usr \
--with-xsl=/usr \
--enable-zend-multibyte \
--enable-zip \
--with-pcre-regex=/usr
&lt;/code&gt;
&lt;/pre&gt;
&lt;h4&gt;追加したオプション&lt;/h4&gt;
&lt;code&gt;-with-pear --with-libedit&lt;/code&gt;
&lt;p&gt;↑のオプション以外はバンドル時に指定されていたはずのモノ．しかし，追加したオプションじゃなくてバンドルについてたオプションのみがコケていた．アホか&lt;/p&gt;

&lt;h3&gt;必要なモノ&lt;/h3&gt;
&lt;p&gt;別に全部公式サイトからバイナリ持ってきてやってもいいけど，たぶん面倒くさいよ．&lt;br&gt;
libpng 以外は &lt;code&gt;$ brew install FORMULA&lt;/code&gt; で余裕でした．これについては後述．&lt;/p&gt;
&lt;ul&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.pcre.org/&quot;&gt;PCRE&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;Perl5 の正規表現を C でも出来るっていうライブラリ&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://xmlsoft.org/&quot;&gt;libxml2&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;まさかこれが入ってなかったとは思わなかった&lt;/dd&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.ijg.org/&quot;&gt;libjpeg&lt;/a&gt;/&lt;a href=&quot;http://www.libpng.org/pub/png/libpng.html&quot;&gt;libpng&lt;/a&gt;&lt;/dt&gt;
&lt;dd&gt;たぶん今回の詰まったキモ&lt;/dd&gt;
&lt;/ul&gt;
&lt;p&gt;もしかすると，あらかじめ自分が別の用事で既にライブラリをインストールしてたおかげでスルーされたものもあるかもしれない．でも大抵 Homebrew あれば大丈夫．MacPorts ならそもそも迷わず sudo port install php5 してもいい．&lt;/p&gt;
&lt;h3&gt;手順&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;PHP-5.3.6.tar.gz を DL して展開&lt;br&gt;
&lt;code&gt;~/tmp&lt;/code&gt;とかでよし&lt;br&gt;
&lt;pre&gt;
&lt;code&gt;$ pushd ~/tmp
$ wget http://jp2.php.net/get/php-5.3.6.tar.gz/from/this/mirror \
&gt; -O php-5.3.6.tar.gz
$ tar -xf php-5.3.6.tar.gz
$ pushd php-5.3.6
&lt;/code&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ ./configure&lt;/code&gt; して必要なライブラリを確認しておく&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ brew install pcre&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ brew install libxml2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;libjpeg と libpng を，この&lt;a href=&quot;http://ethan.tira-thompson.org/Mac_OS_X_Ports.html&quot;&gt;インストーラ&lt;/a&gt;でインストールする&lt;/li&gt;
&lt;li&gt;前述のオプションつけて &lt;code&gt;$ ./configure&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ make &amp;&amp; make test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo make install&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ビルドは時間かかるので何か作業やってる時じゃなくて寝る前とか，よっぽどヒマな時にやった方がいい．とりあえず，これで 購入時 から書いてた PHP と動作互換をとりつつ出来るかと．(わかんない)&lt;/p&gt;
&lt;h3&gt;最後に． PHPUnit, Phing のインストール&lt;/h3&gt;
&lt;p&gt;あとはもうどのディレクトリでもいいから流れ作業で &lt;code&gt;sudo&lt;/code&gt; してけばいいだけ&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;$ sudo pear channel-discover pear.symfony-project.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo pear channel-discover components.ez.no&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo pear channel-discover pear.phpunit.de&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo pear channel-discover pear.phing.info&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo pecl install xdebug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo pear install -a phpunit/PHPUnit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ sudo pear install -a phing/phing&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;補足「なんで libjpeg/libpng は Homebrew 使わなかったの?」&lt;/h3&gt;
&lt;p&gt;2011年3月半ばあたりで&lt;a href=&quot;https://github.com/mxcl/homebrew/issuesearch?state=open&amp;q=libpng#issue/4667&quot;&gt;こんな議論になって&lt;/a&gt;，今は libpng は Formula に一応残ってるものの，素直にコマンドで &lt;code&gt;$ brew install libpng&lt;/code&gt; してもError出てインストールできないようになってる．警告どおり Formula/libpng.rb に
&lt;pre&gt;
 def install
  ENV.x11
  # ENV.libxml2 # あるいはこっちの ENV を指定
  ...
  system &quot;./configure&quot;, ...
  ...
 end
&lt;/pre&gt;
としてもダメ．その内，ちゃんと Formula そのものから除外されるっぽい．libjpeg は普通に Homebrew で入る．&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/7856727791629183796/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2011/04/phpunit-phing-macosx-php.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/7856727791629183796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/7856727791629183796'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2011/04/phpunit-phing-macosx-php.html' title='PHPUnit / Phing を MacOSX で実行できるようにする為の PHP ビルド・インストールメモ'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-3643746843584638209</id><published>2010-09-28T14:50:00.001+09:00</published><updated>2010-09-28T16:48:00.333+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="haskell"/><title type='text'>今、隠蔽しているHaskellライブラリーだけ表示するワンライナー</title><content type='html'>&lt;h4&gt;
Haskellの外部ライブラリ、パッケージ管理ツール、Cabal と ghc-pkg&lt;/h4&gt;
&lt;p&gt;
最近Haskellをまた書きはじめたのだけど、HUnit や HaXml などの外部ライブラリを利用する際に、Cabal というパッケージ管理ツールを利用してCPanとかRubyGemsのような感覚でライブラリーをインストールする事ができるのですね。
&lt;/p&gt;
&lt;p&gt;
で、その際に「自分が何のライブラリをインストールしているか」には、以下の二つの方法で確認する事ができます。
&lt;/p&gt;
&lt;h5&gt;
Cabal コマンド内のサブコマンド list --installed&lt;/h5&gt;
&lt;pre class=&quot;shell&quot; name=&quot;code&quot;&gt;$ cabal list --installed
&lt;/pre&gt;
&lt;p&gt;
こちらは正しく言うと現在の haskage.haskell.org を参照して、利用しているライブラリのバージョンと公開されている最新版を表示します。普通だったら、これを見て Latest version available と Latest version installed を比較してバージョンを上げるのを検討するのに使います。
&lt;/p&gt;

&lt;h5&gt;
GHC のパッケージマネージャーから&lt;/h5&gt;
&lt;pre class=&quot;shell&quot; name=&quot;code&quot;&gt;$ cabal list --installed
&lt;/pre&gt;
&lt;p&gt;
こちらの場合は、現状ローカルでインストール済みの、コンパイルの事情利用不可にしてあるものを含めた全ての(GHC依存の?)ライブラリを列記します。&lt;code&gt;cabal list --installed&lt;/code&gt; コマンドとはそういう意味で毛色が違います。
&lt;/p&gt;
&lt;p&gt;
現状でもその事で躓いたりするのですが、Cabal から欲しいライブラリをインストールしようとした時に、依存している外部の、既にインストール済のライブラリとバージョンの違いにより失敗してしまう、という事がよくあります。(入れようとした hoge ライブラリが bar-1.0.0 に依存しているけど、既に後方互換が約束されていない bar-2.1.0 等がローカルに入ってしまっていた場合など)
&lt;/p&gt;
&lt;p&gt;
こういった状況の時に、まず依存と互換性問題が起きているライブラリを &lt;code&gt;ghc-pkg hide &quot;ライブラリーの名前&quot;&lt;/code&gt;とコマンド実行させて、コンパイラから参照できないようにして目的のライブラリーをインストールして上記の問題を解決させています。
&lt;/p&gt;
&lt;h4&gt;
本題: 読みづらいのをなんとかしたい&lt;/h4&gt;
&lt;p&gt;
ということで、依存性問題を解決する為に、「今ローカルにどんなライブラリがあって、どれを隠蔽しておいたんだっけ」というのを確認する事が多々あるのですが、この ghc-pkg list の出力結果が読みにくい。
&lt;/p&gt;
&lt;img src=&quot;http://gyazo.com/580af6a100a83e0cd4097f140916205f.png&quot; width=&quot;100%&quot;/&gt;
&lt;p&gt;
まぁこんな感じの表示になります。(ライブラリー)でくくられたものが隠蔽されているライブラリーになります。同名でバージョン違いのものは隣に併記してくれたり、矩形にテキスト整形されてはいるもの。まぁ英数字とダッシュと丸括弧でゴチャゴチャして見えますね。
&lt;/p&gt;
&lt;p&gt;
たいていのケースは、「依存ライブラリのバージョン違いを隠蔽したい/隠蔽してたライブラリって何があったっけ?」なので、&lt;code&gt;grep &quot;hoge&quot;&lt;/code&gt;なんかを使って検索しますが、このまんまだと一行に余計なものもついてきます。
&lt;/p&gt;
&lt;h4&gt;
スクリプト&lt;/h4&gt;
&lt;p&gt;
そこでこうしました&lt;/p&gt;
&lt;script src=&quot;http://gist.github.com/600428.js?file=Search-Hidding-Haskell-Library.sh&quot;&gt;&lt;/script&gt;
&lt;p&gt;
結果このような出力結果に&lt;/p&gt;
&lt;img src=&quot;http://gyazo.com/996fd3ea89b173f27e740512f3ef47ab.png&quot; width=&quot;100%&quot; /&gt;
&lt;p&gt;
うむ、見やすい!!!
もし、ライブラリーが重複してないか調べたいなら、上のコードを改変して、&lt;/p&gt;
&lt;pre class=&quot;shell&quot; name=&quot;code&quot;&gt;$ ghc-pkg list | sed &#39;s/,/,   /g&#39; | tr &#39;,&#39; &#39;\n&#39; | grep &quot;$ライブラリーの名前&quot;
&lt;/pre&gt;
&lt;p&gt;
とすると良いでしょう。&lt;/p&gt;

&lt;p&gt;
それにしても hide と cabal install --reinstall 繰り返しってったら、なんかライブラリの再インストールさえ満足にできなくなってきたような気が…&lt;/p&gt;

&lt;h3&gt;追記(2010/09/28 17：00)&lt;/h3&gt;
&lt;p&gt;これ書いた直後に「そういえばGHCのバージョンが古いっていう線もなくね?」と思って確認したら自分のGHCのversionが 6.10.4 で、最新版の 6.12.3 を入れなおしたら上のワンライナーがほぼ不要になってしまった…。まあいいか…&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/3643746843584638209/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2010/09/haskell.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/3643746843584638209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/3643746843584638209'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2010/09/haskell.html' title='今、隠蔽しているHaskellライブラリーだけ表示するワンライナー'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-4223255437214008054</id><published>2010-05-27T01:30:00.029+09:00</published><updated>2010-05-27T12:57:46.115+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CSS3"/><category scheme="http://www.blogger.com/atom/ns#" term="HTML5"/><title type='text'>CSS3だけでWebサイトのUIをMacPro的なメタル調にしてみた</title><content type='html'>&lt;h4&gt;百聞は一見にしかず。デモ&lt;/h4&gt;&lt;strong&gt;このエントリーはIEだと面白くないですよ!&lt;/strong&gt;&lt;br /&gt;
&lt;p&gt;文字サイズを大きくしたり小さくしたり、キャレットで文字列を選択してみたりして、これが画像でないか確めてみましょう。一切画像ファイルは使ってないよ!&lt;/p&gt;&lt;i&gt;&amp;lt;2010/05/28 13:00追記&amp;gt; Chromeだとwindow.onloadイベント処理が動作してないようなのでハンドラをonmouseoverに変更。マウスポインタを重ねるとloading...部分が動作するはず&lt;/i&gt;&lt;br /&gt;
&lt;style type=&quot;text/css&quot;&gt;&lt;!--
    .demoPreview{
      background: #666;
      padding-top: 50px;
      padding-bottom: 50px;
    }

    .demoPage{
    color: #666;
    width: 330px;

    margin: 0 auto !important;
    margin-top: 50px;
    padding: 2px 2px 2px 2px;

    background: #999;
    background: -moz-linear-gradient(top, #eee, #111 60%, #999);
    background: -webkit-gradient(linear, center top, center bottom,
     from(#eee), color-stop(0.6, #111), to(#999));

    border-radius: 22px;
    -moz-border-radius: 22px; /* firfox */
    -webkit-border-radius: 22px; /* Safari */
    
    border: 1px solid #000;
    
    /* drop shadow */
    box-shadow: 0 10px 50px rgba(0, 0, 0, 0.8); /* opera */
    -moz-box-shadow: 0 10px 50px rgba(0, 0, 0, 0.8); /* firefox */
    -webkit-box-shadow: 0 10px 50px rgba(0, 0, 0, 0.8); /* webkit */
}

.demoContents{
    margin: 0 !important;
    background: #aaa;
    background: -moz-linear-gradient(top, #ccc, #333); /* firefox */
    background: -webkit-gradient(linear, center top, center bottom,
     from(#ccc),to(#333)); /* webkit */
    
    border-radius: 20px;
    -moz-border-radius: 20px; /* firfox */
    -webkit-border-radius: 20px /* Safari */
}

.demoTitle{
    margin: 0;
    padding: 15px;
    font-family: sans-serif;
    text-align: center;
    text-shadow: 0px -2px 0px rgba(0, 0, 0, 0.8),
                 0px 2px 0px rgba(255, 255, 255, 0.8);
}

#justnow{
    display: block;
    -webkit-box-sizing: context-box;
    margin: 0 30px 0 30px;
    padding: 5px;

    background: #000;
    color: #0c0;
    text-align: center;

    border-radius: 10px;
    -moz-border-radius: 10px; /* firfox */
    -webkit-border-radius: 10px; /* Safari */

    border: 2px solid #777;
    
    /* represent high-right looks like glass */
    box-shadow: inset 0px 0px 50px rgba(0, 255, 150, 0.5),
                inset 0 1px rgba(255, 255, 255, 0.3),
                inset 0 20px rgba(255, 255, 255, 0.2);
    /* firefox */
    -moz-box-shadow: inset 0px 0px 50px rgba(0, 255, 150, 0.5),
                     inset 0 1px rgba(255, 255, 255, 0.3),
                     inset 0 20px rgba(255, 255, 255, 0.2);
    /* webkit */
    -webkit-box-shadow: inset 0px 0px 50px rgba(0, 255, 150, 0.5),
                        inset 0 1px rgba(255, 255, 255, 0.3),
                        inset 0 20px rgba(255, 255, 255, 0.5);
}

.demoNavi{
    display: -webkit-box;
    -webkit-box-sizing: border-box;
    margin-top: 5px !important;
    margin-bottom: 0px !important;
    padding-top: 5px !important;
    padding-bottom: 5px !important;

    background: #999;
    background: -moz-linear-gradient(top, #ccc, #555);
    background: -webkit-gradient(linear, center top, center bottom,
     from(#ccc), to(#555));

    color: #222;
    border-bottom-left-radius: 20px;
    -moz-border-radius-bottomleft: 20px;
    -webkit-border-bottom-left-radius: 20px;    

    border-bottom-right-radius: 20px;
    -moz-border-radius-bottomright: 20px;
    -webkit-border-bottom-right-radius: 20px;

    border-top: 1px solid #333;
}

/* display as inline */
.demoProf, .demoTwitter, .demoBlog, .demoBookmark {
    display: inline;
    display: -moz-box;
    display: -webkit-box;
    margin: 2px;
}

.demoNavi a{
    padding: 5px;

    font-size: 10pt;
    color: #222;
    text-decoration: none;
    text-shadow: 0px -1px 0px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.5);

    border-radius: 5px;
    -moz-border-radius: 5px;
    -webkit-border-radius: 5px;
    
    border: 1px solid rgba(0, 0, 0, 0.5);
}

.demoNavi a:hover{
    background: #555;
    background: -moz-linear-gradient(top, #777, #333);
    background: -webkit-gradient(linear, center top, center bottom, from(#777),to(#333));

    color: #fff;
    text-shadow: 0px 0px 5px #fff;

    box-shadow: 0 0 3px #fff;
    -moz-box-shadow: 0 0 3px #fff;
    -webkit-box-shadow: 0 0 3px #fff;

    border: 1px solid #fff;
}
    --&gt;
&lt;/style&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;script type=&quot;text/javascript&quot;&gt;&lt;!--
   function disp(){     
       document.getElementById(&#39;justnow&#39;).innerHTML = (new Date()).toString();
       setTimeout(&quot;disp()&quot;, 1000);
   };
   //window.onload = function(){ disp(); };
--&gt;&lt;/script&gt;&lt;br /&gt;
&lt;div class=&quot;demoPreview&quot; onmouseover=&quot;disp()&quot;&gt;&lt;div class=&quot;demoPage&quot;&gt;&lt;div class=&quot;demoContents&quot;&gt;&lt;h1 class=&quot;demoTitle&quot;&gt;I ♥ Internet&lt;/h1&gt;&lt;time id=&quot;justnow&quot;&gt;loading...&lt;/time&gt;&lt;br /&gt;
&lt;ul class=&quot;demoNavi&quot;&gt;&lt;li class=&quot;demoProf&quot;&gt;&lt;a href=&quot;http://www.google.com/profiles/VoQn.tyrantist&quot;&gt;Profile&lt;/a&gt;&lt;/li&gt;
&lt;li class=&quot;demoBlog&quot;&gt;&lt;a href=&quot;http://voqn.blogspot.com/&quot;&gt;Blog&lt;/a&gt;&lt;/li&gt;
&lt;li class=&quot;demoTwitter&quot;&gt;&lt;a href=&quot;http://twitter.com/VoQn&quot;&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li class=&quot;demoBookmark&quot;&gt;&lt;a href=&quot;http://b.hatena.ne.jp/VoQn/&quot;&gt;Bookmark&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h4&gt;趣旨&lt;/h4&gt;&lt;p&gt;見せてもらおうか、W3Cの新しいCSSの性能とやらを&lt;/p&gt;&lt;h4&gt;先に結論&lt;/h4&gt;&lt;p&gt;無理にすべてCSS3だけで表現を完結させようとすればするほど一昔前のブラウザ別ハックみたいな事しなきゃいけなくなりますよ。&lt;/p&gt;&lt;p&gt;ちなみにBloggerにデモ用に直にHTML記述してみたんだけど、やっぱりレンダリングが変になる。Blog内で無理にデモをやってみようとするとやっぱりどこかおかしくなる。あと要素へ与えるidやclass名を記事毎に気をつけて命名しないと意図しない所へCSSが適用されてしまったり…&lt;/p&gt;&lt;p&gt;一応、見掛けはこういう風になるはずなんです。完全なものがレンダリングされるのはChrome, 次にFirefox, そして Safari, Operaの順ですね&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://gyazo.com/bfe9756bb46c66db7e346f88e46249c9.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;180&quot; src=&quot;http://gyazo.com/bfe9756bb46c66db7e346f88e46249c9.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;ソースコード&lt;/h4&gt;&lt;a href=&quot;http://gist.github.com/414498&quot;&gt;gist: 414498 - GitHub&lt;/a&gt;&lt;br /&gt;
&lt;p&gt;ローカルに保存してFirefox, Safari, Chromeなどでひらいて確認してみてください&lt;/p&gt;&lt;script src=&quot;http://gist.github.com/414498.js?file=metallookui.html&quot;&gt;
&lt;/script&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;(予告)メイキング&lt;/h4&gt;&lt;p&gt;メイキング、解説編は後日アップするという事で&lt;/p&gt;&lt;p&gt;あ、ちなみにこれエントリ込みでかなりの頻度で細かいところ修正してます。ご了承ください&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/4223255437214008054/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2010/05/css3webuimacpro.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4223255437214008054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4223255437214008054'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2010/05/css3webuimacpro.html' title='CSS3だけでWebサイトのUIをMacPro的なメタル調にしてみた'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5579535841246168871.post-4480313800930232243</id><published>2010-05-14T01:11:00.012+09:00</published><updated>2010-05-27T13:04:06.544+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="canvas"/><category scheme="http://www.blogger.com/atom/ns#" term="javascript"/><title type='text'>Canvas要素の為の HSV/HLS色空間モデルjavascript</title><content type='html'>&lt;h4&gt;趣旨 「RGBで色指定すんのアホくさい…」&lt;/h4&gt;というわけでマンセル色相環とかそのへんの色計画に馴染みのある人が使いやすい色空間モデルを導入したい。  &lt;h4&gt;先に結論&lt;/h4&gt;たぶん&lt;a href=&quot;http://d.hatena.ne.jp/uupaa/&quot;&gt;uupaa&lt;/a&gt;さんが開発してる&lt;a href=&quot;http://code.google.com/p/uupaa-js-spinoff/&quot;&gt;uupaa.jsのスピンオフ&lt;/a&gt;利用した方が良いですよ  &lt;h4&gt;Canvasで利用できるRGBカラーシンタックス&lt;/h4&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;var red = &#39;rgb(255, 0, 0); // 赤100%
var alphaRed =&#39;rgba(255, 0, 0, 0.5); // 赤100%なんだけど不透明度は50%
&lt;/pre&gt;どんな場でこれが利用できるかというと、こう使う  &lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;var canvas = document.getElementById(&#39;canvas&#39;);
var ctx = canvas.getContext(&#39;2d&#39;);
ctx.beginPath();
ctx.strokeStyle = &#39;rgb(200, 255, 0)&#39;; // ←ココ
ctx.fillStyle = &#39;rgba(0, 255, 0, 0.5)&#39;; // ← ココ
ctx.stokreRect(0, 0, canvas.width, canvas.hight);
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.closePath();
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;#FF0033&lt;/code&gt; みたいな表記よりもわかりやすいんじゃないですかねー、という事はあるし、透過色もサポートされてはいる。これはありがたい…。&lt;/p&gt;&lt;p&gt;だけど、これだと「色相のグラデーションをやりたい」とか「明るさのグラデーションをやりたい」はムリ。っていうか正規でHSVとかサポートされてないの? って調べてたんだけど、すぐに見つからなかったので、もう自前でモデルつくって見る事にした。結果的に凄い基本的な色空間の変換アルゴリズムを知る良い勉強になったので仮にムダでも良かった事にする。&lt;/p&gt;ソースコードは例のごとくGistに&lt;br /&gt;
&lt;a href=&quot;http://gist.github.com/399832&quot;&gt;ColorSpaceModel.js&lt;/a&gt;  &lt;h4&gt;使い方&lt;/h4&gt;&lt;pre class=&quot;javascript&quot; name=&quot;code&quot;&gt;var hlsa = new HLSA(0, 0.5, 1, 1); // ← HLSA色空間における 赤の純色
var hsva = new HSVA(0, 1, 1, 0.9);   // ← HSVA色空間における 赤の純色(不透明度90%)

var color1 = convertHSLAtoRGBA(hlsa).toString() // &#39;rgb(255, 0, 0)&#39;
var color2 = convertHSVAtoRGBA(hsva).toString() // &#39;rgba(255, 0, 0, 0.9)&#39;
&lt;/pre&gt;&lt;p&gt;&lt;code&gt;convert{$Model1}to{$Model2}({$InstanceOfModel1})&lt;/code&gt; っていう書式で命名している関数があるので、それにモデル化したオブジェクトを渡し、&lt;code&gt;toString()&lt;/code&gt; 関数を呼ぶと(RGBAモデルなら)canvasで使える文字列が返る、という仕様にしました。&lt;/p&gt;&lt;p&gt;でもこれは非常にJava脳っぽいところがあるので、多分これJavaScriptユーザーにとっては不自然なんだろうな、という処もあり、今後改良したいところ。 &lt;/p&gt;&lt;p&gt;なんというか convert〜 の戻りが直に文字列返ってくるのがちょっと気に入らなかったというか、Objectとして返す方が 一旦 あるモデルに変換してから、メンバを少し弄って、更に別のモデルに変換する、みたいな便利さがあるんじゃないかとか思ったり。実際どうなんだろ &lt;/p&gt;&lt;h4&gt;デモ&lt;/h4&gt;&lt;script type=&quot;text/javascript&quot;&gt;
var innerVal = function(min, value, max){
    return Math.max(min, Math.min(value, max)); 
};

var format = function(fmt){
    const loop = function(fmt, i, args){
     return i &lt; args.length
     ? loop(fmt.replace(new RegExp(&#39;%&#39; + i + &#39;\\$s&#39;, &quot;g&quot;),
          args[i]),
     i + 1,
     args)
     : fmt;
    };
    return loop(fmt, 1, arguments);
};

var defSet = function(arg, defValue, proc){
    return !arg ? defValue : !proc ? arg : proc(arg); 
};

var HSVA = function(hue, sat, val, alpha){
    var rate = 1.0;
    this.rate = rate;
    const innerRange = function(value){
 return innerVal(0.0, value, rate);
    };
    
    const innerHueRange  = function(hue){  
 return (function(value, mods){
      return value &lt; 0 ? mods - (-value % mods) % mods : value % mods;
  })(hue, 360);
    };
    
    this.setHue   = function(hue)  { this.hue   = defSet(hue,   0, innerHueRange);};
    this.setSat   = function(sat)  { this.sat   = defSet(sat,   0, innerRange);};
    this.setVal   = function(val)  { this.val   = defSet(val,   0, innerRange);};
    this.setAlpha = function(alpha){ this.alpha = defSet(alpha, 1, innerRange);};

    this.setHue(hue);
    this.setSat(sat);
    this.setVal(val);
    this.setAlpha(alpha);

    this.string = function(){
 return (function(hsva){
      if(hsva.alpha == 1){
   return format(&#39;hsv(%1$s, %2$s, %3$s)&#39;,
          hsva.hue, hsva.sat, hsva.val);
      }
      return format(&#39;hsva(%1$s, %2$s, %3$s, %4$s)&#39;,
      hsva.hue, hsva.sat, hsva.val, hsva.alpha);
  })(this);};
};

HSVA.prototype = {
    &#39;hue&#39;   :0.0,
    &#39;sat&#39;   :0.0,
    &#39;val&#39;   :0.0,
    &#39;alpha&#39; :1.0,
    &#39;rate&#39;  :1.0
};

var HLSA = function(hue, lum, sat, alpha){
    var rate = 1.0;
    this.rate = rate;
    const innerRange = function(value){
 return innerVal(0.0, value, rate);
    };
    
    const innerHueRange  = function(hue){  
 return (function(value, mods){
      return value &lt; 0 ? mods - (-value % mods) % mods : value % mods;
  })(hue, 360);
    };
    
    this.setHue   = function(hue)  { this.hue   = defSet(hue,   0, innerHueRange); };
    this.setLum   = function(lum)  { this.lum   = defSet(lum,   0, innerRange); };
    this.setSat   = function(sat)  { this.sat   = defSet(sat,   0, innerRange); };
    this.setAlpha = function(alpha){ this.alpha = defSet(alpha, 1, innerRange); };

    this.setHue(hue);
    this.setLum(lum);
    this.setSat(sat);
    this.setAlpha(alpha);
    
    this.string = function(){
 return (function(hlsa){
      if(hlsa.alpha == 1){
   return format(&#39;hls(%1$s, %2$s, %3$s)&#39;,
          hlsa.hue, hlsa.lum, hlsa.sat);
      }
      return format(&#39;hlsa(%1$s, %2$s, %3$s, %4$s)&#39;,
      hlsa.hue, hlsa.lum, hlsa.sat, hlsa.alpha);
  })(this);
    };
};

HLSA.prototype = {
    &#39;hue&#39;  :0,
    &#39;lum&#39;  :0,
    &#39;sat&#39;  :0,
    &#39;alpha&#39;:1
};

var RGBA = function(red, blue, green, alpha){
    var rate = 255.0;
    this.rate = rate;
    const innerRangeRGB = function(value){
 return innerVal(0.0, value, rate);
    };
    const innerRangeAlpha = function(value){
 return innerVal(0.0, value, 1.0);
    };
    
    this.setRed   = function(red)   { this.red   = defSet(red,   0, innerRangeRGB); };
    this.setGreen = function(green) { this.green = defSet(green, 0, innerRangeRGB); };
    this.setBlue  = function(blue)  { this.blue  = defSet(blue,  0, innerRangeRGB); };
    this.setAlpha = function(alpha) { this.alpha = defSet(alpha, 1, innerRangeAlpha); };
    
    this.setRed(red);
    this.setGreen(green);
    this.setBlue(blue);
    this.setAlpha(alpha);
    
    this.string = function(){
 return (function(rgba) {
      if(rgba.alpha == 1){
   return format(&#39;rgb(%1$s, %2$s, %3$s)&#39;,
          rgba.red, rgba.green, rgba.blue);
      }
      return format(&#39;rgba(%1$s, %2$s, %3$s, %4$s)&#39;,
        rgba.red, rgba.green, rgba.blue, rgba.alpha);
  })(this);};
};

RGBA.prototype = {
    &#39;red&#39;     :0,
    &#39;blue&#39;    :0,
    &#39;green&#39;   :0,
    &#39;alpha&#39;   :1
};

var RGB = function(H, C, X){
    var red, green, blue;
    const make = function(_red, _green, _blue){
 red   = _red;
 green = _green;
 blue  = _blue;
    };

    // Initialize
    if(0.0 &lt;= H &amp;&amp; H &lt; 1.0)
 make(C, X, 0);
    else if(1.0 &lt;= H &amp;&amp; H &lt; 2.0)
 make(X, C, 0);
    else if(2.0 &lt;= H &amp;&amp; H &lt; 3.0)
 make(0, C, X);
    else if(3.0 &lt;= H &amp;&amp; H &lt; 4.0)
 make(0, X, C);
    else if(4.0 &lt;= H &amp;&amp; H &lt; 5.0)
 make(X, 0, C);
    else if(5.0 &lt;= H &amp;&amp; H &lt; 6.0)
 make(C, 0, X);
    else
 make(0, 0, 0);
    
    this.red   = red;
    this.green = green;
    this.blue  = blue;
};

const convertHSVAtoRGBA = function(hsva){
    const H = hsva.hue   / 60.0;
    const S = hsva.sat   / hsva.rate;
    const V = hsva.val   / hsva.rate;
    const A = hsva.alpha / hsva.rate;

    const C = V * S;
    const X = C * (1 - Math.abs(H % 2 - 1));

    const makeRGBA = function(rgb, alpha){
 const rate = (new RGBA).rate;
 const m = V - C;
 const calc = function(v){ return Math.round((v + m) * rate); };
 return new RGBA(calc(rgb.red),
   calc(rgb.green),
   calc(rgb.blue),
   alpha);
    };
    
    return makeRGBA(new RGB(H,C,X), A);
};

const convertHLSAtoRGBA = function(hlsa){
    const H = hlsa.hue   / 60.0;
    const L = hlsa.lum   / hlsa.rate;
    const S = hlsa.sat   / hlsa.rate;
    const A = hlsa.alpha / hlsa.rate;

    const C = (L &lt;= 1 / 2) ? 2 * L * S : 2 * (1 - L) * S; // chroma
    const X = C * (1 - Math.abs(H % 2 - 1));

    const makeRGBA = function(rgb, alpha){
 const rate = (new RGBA).rate;
 const m = L - C / 2;
 const calc = function(v){ return Math.round((v + m) * rate); };
 return new RGBA(calc(rgb.red),
   calc(rgb.green),
   calc(rgb.blue),
   alpha);
    };

    return makeRGBA(new RGB(H, C, X), A);    
};

const convertRGBAtoHSVA = function(rgba){
    const red   = rgba.red   / rgba.rate;
    const green = rgba.green / rgba.rate;
    const blue  = rgba.blue  / rgba.rate;
    const alpha = rgba.alpha;
    
    const max = Math.max(red, green, blue);
    const min = Math.min(red, green, blue);
    
    const val = max;
    
    if(val == 0){ return new HSVA(val, val, val, alpha); }
    
    const sat = (max - min) / max;
 
    const calc = function(value){
 return (max - value) / (max - min);
    };
    
    const cr = calc(red);
    const cg = calc(green);
    const cb = calc(blue);
    
    const hue = ((red == max ? cb - cg
    : green == max ? 2 + cr - cb
    : 4 + cg - cr)
   * 60 + 360);
    return new HSVA(hue, sat, val, alpha);
};

const convertRGBAtoHLSA = function(rgba){
    const red   = rgba.red   / rgba.rate;
    const green = rgba.green / rgba.rate;
    const blue  = rgba.blue  / rgba.rate;
    const alpha = rgba.alpha / rgba.rate;
    
    const max = Math.max(red, green, blue);
    const min = Math.min(red, green, blue);
    
    const lum = (max + min) / 2;
    
    if(max == min){ return new HLSA(0, 0, lum, alpha); }
    
    const sat = lum &lt;= 0.5
 ? (max - min) / (max + min)
 : (max - min) / (2 - max - min);
    
    const calc = function(value){ return (max - value) / (max - min); };
    
    const cr = calc(red);
    const cg = calc(green);
    const cb = calc(blue);
    
    const hue = ((red == max ? cb - cg : green == max ? 2 + cr - cb : 4 + cg - cr) * 60 + 360);
    return new HLSA(hue, sat, lum, alpha);  
};
// =====================================
  var WIDTH, HEIGHT;
  var ctx;
  window.onload = function(){
      if(!ctx){
          var canvas = document.getElementById(&#39;csdemo&#39;);
          if(!canvas || !canvas.getContext){
              return false;
          }

          ctx = canvas.getContext(&#39;2d&#39;);
          WIDTH  = canvas.width;
          HEIGHT = canvas.height;
  
          displayConverted();
          //fill(&#39;rgb(0, 0, 0)&#39;);
      }
      return true;
  }
  
  function fill(colorString){
      ctx.beginPath();
      ctx.fillStyle = colorString;
      ctx.fillRect(0, 0, WIDTH, HEIGHT);
      ctx.closePath();
  }
  
  function displayConverted(){
      var hlsa = createHLSA();
      var color =  convertHLSAtoRGBA(hlsa).string();
      var status = format(&quot;%1$s =&amp;gt %2$s&quot;, hlsa.string(), color);

      fill(color);
      document.getElementById(&#39;rgb&#39;).innerHTML = status;
  }
  
  function createHLSA(){
      const get = function(id){ return eval(document.getElementById(id).value); };
             
      var hue = get(&#39;hue&#39;) % 360;
      var lum = get(&#39;lum&#39;) / 100;
      var sat = get(&#39;sat&#39;) / 100;
      
      return new HLSA(hue, lum, sat);
  }

  var currentFormIndex = 0;

  window.onkeyup = function(){
      if(event.keyCode == 13){
          currentFormIndex++;
          currentFormIndex %= document.input.elements.length;
          document.input[currentFormIndex].focus();
      }
      displayConverted();
  }
    &lt;/script&gt;
&lt;div id=&#39;demo&#39;&gt;

    &lt;div&gt;

      &lt;canvas id=&quot;csdemo&quot; width=&quot;200&quot; height=&quot;200&quot;&gt;&lt;/canvas&gt;
    &lt;/div&gt;&lt;div&gt;
Color: &lt;span id=&quot;rgb&quot;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div&gt;

      &lt;form name=&quot;input&quot;&gt;

&lt;ul&gt;
&lt;li&gt;H: &lt;input type=&quot;text&quot; id=&quot;hue&quot; value=&quot;0&quot;&gt;(0〜360)&lt;/li&gt;
&lt;li&gt;L: &lt;input type=&quot;text&quot; id=&quot;lum&quot; value=&quot;0&quot;&gt;(0〜100)&lt;/li&gt;
&lt;li&gt;S: &lt;input type=&quot;text&quot; id=&quot;sat&quot; value=&quot;0&quot;&gt;(0〜100)&lt;/li&gt;
&lt;/ul&gt;&lt;input type=&quot;button&quot;
        onClick=&quot;displayConverted()&quot;
        value=&quot;convert to RGB&quot;&gt;
&lt;/form&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4&gt;
参考リンク&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB&quot;&gt;HSL and HSV - Wikipedia, the free encyclopedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://image-d.isp.jp/commentary/color_cformula/HLS.html&quot;&gt;ISP imaging-developers - 色変換式集 - HLS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://image-d.isp.jp/commentary/color_cformula/HLS.html&quot;&gt;ISP imaging-developers - 色変換式集 - HLS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h4&gt;ソースコード&lt;/h4&gt;&lt;script src=&quot;http://gist.github.com/399832.js?file=ColorSpaceModel.js&quot;&gt;&lt;/script&gt;</content><link rel='replies' type='application/atom+xml' href='http://voqn.blogspot.com/feeds/4480313800930232243/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://voqn.blogspot.com/2010/05/canvas-hsvhlsjavascript.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4480313800930232243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5579535841246168871/posts/default/4480313800930232243'/><link rel='alternate' type='text/html' href='http://voqn.blogspot.com/2010/05/canvas-hsvhlsjavascript.html' title='Canvas要素の為の HSV/HLS色空間モデルjavascript'/><author><name>Anonymous</name><uri>http://www.blogger.com/profile/16253960931645850144</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>