<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
        <title>Typeof.net</title>
        <description>The Feed of typeof.net</description>
        <link>http://typeof.net</link>
<lastBuildDate>2012-05-17 10:22:15</lastBuildDate>
<pubDate>2012-05-17 10:22:15</pubDate> <ttl>1800</ttl>
<item><title>Moescript 的模式匹配</title><link>http://typeof.net/mechanix/pattern-match-in-moe.html</link><guid>http://typeof.net/mechanix/pattern-match-in-moe.html</guid><pubDate>2012-05-16 23:57</pubDate><description>&lt;p&gt;自从 &lt;a href="http://www.cppblog.com/vczh"&gt;vczh&lt;/a&gt; 说 “没有模式匹配的语言不是好语言” 后，我就用 Moe 的单子做了个模式匹配，没有去修语法。当然不是「完全没有修」，原来单子范本（Monad Schemata）里的 &lt;code&gt;yield&lt;/code&gt; 动作就给改成 &lt;code&gt;bindYield&lt;/code&gt; 了。这下子 Moe 的单子就更适合做 DSL 了。&lt;/p&gt;
&lt;p&gt;大多数有模式匹配的语言，其匹配过程其实有两部分：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;判断某个表达式是否满足某个条件（匹配）&lt;/li&gt;
&lt;li&gt;如果满足，把它分解（提取）&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;在 Scala 里，这个过程可以用&lt;strong&gt;提取器&lt;/strong&gt;（&lt;em&gt;Extractor&lt;/em&gt;）来描述：一个提取器有个 &lt;code&gt;unapply&lt;/code&gt; 方法来“拆解”送进来的对象，如果满足某个条件，则返回 &lt;code&gt;Some(part1, part2, ...)&lt;/code&gt;，否则返回 &lt;code&gt;None&lt;/code&gt;。一个提取器的案例是：&lt;/p&gt;
&lt;pre class="mghl source scala"&gt;&lt;span class="class"&gt;&lt;span class="keyword"&gt;object&lt;/span&gt; &lt;span class="title"&gt;Email&lt;/span&gt; {&lt;/span&gt;
    &lt;span class="keyword"&gt;def&lt;/span&gt; unapply(str: String): Option[(String, String)] = {
        &lt;span class="keyword"&gt;val&lt;/span&gt; parts = str split &lt;span class="string"&gt;"@"&lt;/span&gt;
        &lt;span class="keyword"&gt;if&lt;/span&gt; (parts.length == &lt;span class="number"&gt;2&lt;/span&gt;) Some(parts(&lt;span class="number"&gt;0&lt;/span&gt;), parts(&lt;span class="number"&gt;1&lt;/span&gt;))
        &lt;span class="keyword"&gt;else&lt;/span&gt; None
    }
}&lt;/pre&gt;&lt;p&gt;在 Moe 里，出于性能考虑，我不打算用数组存贮被拆开的各个部分。考虑到模式匹配里，提取器后面往往跟着一个动作，所以我打算把提取器做成这样：&lt;/p&gt;
&lt;pre class="mghl source moe"&gt;&lt;span class="name keyword tt_Def"&gt;def&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;Email&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;[&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;formMatch&lt;/span&gt;&lt;span class="punctor tt_Colon"&gt;:&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;f&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;]&lt;/span&gt;
&lt;span class="name keyword tt_Where"&gt;where&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;f&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;got&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;miss&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="name keyword tt_if"&gt;if&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name literal constant tt_Constant"&gt;not&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt; &lt;span class="name operator tt_Operator"&gt;is&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;String&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; 
        &lt;span class="name identifier tt_Identifier"&gt;miss&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor tt_Semicolon"&gt;;&lt;/span&gt;
    &lt;span class="name keyword tt_Else"&gt;else&lt;/span&gt;
        &lt;span class="name keyword tt_Def"&gt;def&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;parts&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;split&lt;/span&gt; &lt;span class="string literal"&gt;"@"&lt;/span&gt;
        &lt;span class="name keyword tt_if"&gt;if&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;parts&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;length&lt;/span&gt; &lt;span class="operator tt_Operator"&gt;==&lt;/span&gt; &lt;span class="number literal"&gt;2&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; 
            &lt;span class="name identifier tt_Identifier"&gt;got&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;parts&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;[&lt;/span&gt;&lt;span class="number literal"&gt;0&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;]&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;parts&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;[&lt;/span&gt;&lt;span class="number literal"&gt;1&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;]&lt;/span&gt;
        &lt;span class="name keyword tt_Else"&gt;else&lt;/span&gt; 
            &lt;span class="name identifier tt_Identifier"&gt;miss&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;这样一来，&lt;code&gt;Email(&amp;lt;匹配动作&amp;gt;, &amp;lt;未匹配动作&amp;gt;)&lt;/code&gt; 就可以得到一个函数，对于丢给它的对象，如果满足一些条件，就提取之并交付 &lt;code&gt;&amp;lt;匹配动作&amp;gt;&lt;/code&gt;，否则就交付 &lt;code&gt;&amp;lt;未匹配动作&amp;gt;&lt;/code&gt;。这样，就可以串联两个提取器实现模式匹配里的“先到先得”原则：&lt;/p&gt;
&lt;pre class="mghl source moe"&gt;&lt;span class="name identifier tt_Identifier"&gt;extractor1&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;formMatch&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;match1&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;extractor2&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;formMatch&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;match2&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;allMiss&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;明确思路以后就可以利用 Moe 的单子来构造复杂提取器了。单子（Monad）有个外号叫“可编程的分号”，是对流程本身进行的抽象。处理单子就可以用各种方法处理流程本身，从而得到传统语言难以完成的神奇效果，比如让函数可以从指定的地方断成两截，并且可以在你需要的时候执行后半截，成为各种被异步 API 困扰人士的救星。相信各位看过 &lt;a href="http://be5invis.github.com/moescript"&gt;Moe 文档&lt;/a&gt;里的异步单子（&lt;code&gt;async&lt;/code&gt;）和列表综合单子（&lt;code&gt;table&lt;/code&gt;）的看官都已经知道单子有多么逆天了。&lt;/p&gt;
&lt;p&gt;利用单子做 DSL 的原理是利用单子范本（&lt;code&gt;schemata&lt;/code&gt;）在 Moe 提供的两种&lt;strong&gt;绑定点&lt;/strong&gt;（&lt;code&gt;!&lt;/code&gt; 和 &lt;code&gt;&amp;lt;-&lt;/code&gt;）对应的方法（&lt;code&gt;bindYield&lt;/code&gt; 和 &lt;code&gt;bind&lt;/code&gt;）上对流程进行处理。单子原语里&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;code&gt;f!(&amp;lt;args&amp;gt;)&lt;/code&gt; 的定义是 &lt;code&gt;schemata.bindYield(f, null, &amp;lt;args&amp;gt;, &amp;lt;接下来的流程&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;o.m!(&amp;lt;args&amp;gt;)&lt;/code&gt; 的定义是 &lt;code&gt;schemata.bindYield(o.m, o, &amp;lt;args&amp;gt;, &amp;lt;接下来的流程&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x &amp;lt;- e&lt;/code&gt; 的定义是 &lt;code&gt;schemata.bind(e, (x) =&amp;gt; &amp;lt;接下来的流程&amp;gt;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;注意到 &lt;code&gt;bindYield&lt;/code&gt; 的形式和 &lt;code&gt;extractor.formMatch&lt;/code&gt; 相似，尤其是那个 &lt;code&gt;&amp;lt;接下来的流程&amp;gt;&lt;/code&gt;，和上面例子里串联提取器时候的 &lt;code&gt;&amp;lt;未匹配动作&amp;gt;&lt;/code&gt; 链几乎一模一样。这就提供了利用 &lt;code&gt;bindYield&lt;/code&gt; 做复杂模式匹配的可能性。下面就是模式匹配单子（&lt;code&gt;matcher&lt;/code&gt;）的实现：&lt;/p&gt;
&lt;div class="show"&gt;&lt;pre class="mghl source moe"&gt;&lt;span class="comment"&gt;// From: Moescript src/prelude/prelude.moe&lt;/span&gt;
&lt;span class="comment"&gt;// Pattern match functions&lt;/span&gt;
&lt;span class="name keyword tt_Def"&gt;def&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;Matcher&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;G&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; 
    &lt;span class="name keyword tt_Var"&gt;var&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;fMatch&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="name literal constant tt_Constant"&gt;undefined&lt;/span&gt;&lt;span class="punctor tt_Semicolon"&gt;;&lt;/span&gt;
    &lt;span class="name keyword tt_Def"&gt;def&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;schemata&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;object&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;MONAD_SCHEMATA_M&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="name keyword tt_Def"&gt;def&lt;/span&gt; &lt;span class="operator tt_My sign"&gt;@&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;bindYield&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;extractor&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;thisp&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;got&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;cont&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor tt_Colon"&gt;:&lt;/span&gt;
            &lt;span class="name identifier tt_Identifier"&gt;cont&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;
            &lt;span class="name identifier tt_Identifier"&gt;fMatch&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;extractor&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;formMatch&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;call&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;thisp&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;got&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;fMatch&lt;/span&gt;
    
    &lt;span class="name identifier tt_Identifier"&gt;G&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;build&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;schemata&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;
    &lt;span class="name keyword tt_Return"&gt;return&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;fMatch&lt;/span&gt;

&lt;span class="name keyword tt_Def"&gt;def&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;export&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="string literal"&gt;'match'&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;match&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;G&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt;
    &lt;span class="name keyword tt_if"&gt;if&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name literal constant tt_Constant"&gt;not&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;G&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;Matcher&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;
    &lt;span class="name keyword tt_Else"&gt;else&lt;/span&gt;       &lt;span class="name identifier tt_Identifier"&gt;Matcher&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;G&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是 &lt;code&gt;match&lt;/code&gt; 的一个用例：&lt;/p&gt;
&lt;div class="show"&gt;&lt;pre class="mghl source moe"&gt;&lt;span class="comment"&gt;// Generate matcher functions&lt;/span&gt;
&lt;span class="name keyword tt_Var"&gt;var&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;matcher&lt;/span&gt; &lt;span class="operator tt_Assign symbol"&gt;=&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;match&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="name identifier tt_Identifier"&gt;Array&lt;/span&gt; &lt;span class="punctor tt_Exclamation symbol"&gt;!&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;first&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;second&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;tail&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="name identifier tt_Identifier"&gt;trace&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;[&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;first&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;second&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;]&lt;/span&gt;
    &lt;span class="name identifier tt_Identifier"&gt;String&lt;/span&gt; &lt;span class="punctor tt_Exclamation symbol"&gt;!&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;s&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="name identifier tt_Identifier"&gt;trace&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;s&lt;/span&gt;
&lt;span class="name identifier tt_Identifier"&gt;matcher&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;

&lt;span class="comment"&gt;// Direct match&lt;/span&gt;
&lt;span class="name identifier tt_Identifier"&gt;match&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;x&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="name identifier tt_Identifier"&gt;Array&lt;/span&gt; &lt;span class="punctor tt_Exclamation symbol"&gt;!&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;first&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;second&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;tail&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="name identifier tt_Identifier"&gt;trace&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;[&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;first&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;second&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;]&lt;/span&gt;
    &lt;span class="name identifier tt_Identifier"&gt;String&lt;/span&gt; &lt;span class="punctor tt_Exclamation symbol"&gt;!&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;s&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="name identifier tt_Identifier"&gt;trace&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;s&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到，&lt;code&gt;match&lt;/code&gt; 的最末参数，一个 DSL里，对提取器（Extractor）进行 &lt;code&gt;!&lt;/code&gt; 调用（&lt;code&gt;bindYield&lt;/code&gt; 调用）来构造模式匹配器。如果展开这个 &lt;code&gt;！&lt;/code&gt;，那么结果将是：&lt;/p&gt;
&lt;pre class="mghl source moe"&gt;&lt;span class="name identifier tt_Identifier"&gt;schemata&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;bindYield&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;Array&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name literal constant tt_Constant"&gt;null&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;first&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;second&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;tail&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="operator tt_Operator"&gt;...&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="name identifier tt_Identifier"&gt;schemata&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;bindYield&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;String&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name literal constant tt_Constant"&gt;null&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;s&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="operator tt_Operator"&gt;...&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="name literal constant tt_Constant"&gt;undefined&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Matcher&lt;/code&gt; 里的 &lt;code&gt;schemata.bindYield&lt;/code&gt; 从后向前搭建匹配函数，慢慢把 &lt;code&gt;fMatch&lt;/code&gt; 成型。按照 &lt;code&gt;schemata.bindYield&lt;/code&gt; 的定义，上面一大团东西的实际上就是：&lt;/p&gt;
&lt;pre class="mghl source moe"&gt;&lt;span class="name identifier tt_Identifier"&gt;Array&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;formMatch&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;first&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;second&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="name identifier tt_Identifier"&gt;tail&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="operator tt_Operator"&gt;...&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;
    &lt;span class="name identifier tt_Identifier"&gt;String&lt;/span&gt;&lt;span class="punctor tt_Dot"&gt;.&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;formMatch&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="name identifier tt_Identifier"&gt;s&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="operator tt_Operator"&gt;...&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor tt_Comma"&gt;,&lt;/span&gt; &lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket open tt_Open"&gt;(&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt; &lt;span class="operator lambda tt_Lambda"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="name literal constant tt_Constant"&gt;undefined&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;span class="punctor bracket close tt_Close"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;这是一个函数，和 &lt;code&gt;Email.formMatch(... , ...)&lt;/code&gt; 的结果一样，处理传入的对象，并处理相应的动作。这个 DSL 写起来很方便，只要在提取器和匹配后动作之间加入一个 &lt;code&gt;!&lt;/code&gt; 就行了。&lt;/p&gt;</description></item>
<item><title>Moescript 中的 Layout 过程</title><link>http://typeof.net/mechanix/the-layouting-in-moe.html</link><guid>http://typeof.net/mechanix/the-layouting-in-moe.html</guid><pubDate>2012-05-14 15:29</pubDate><description>&lt;p&gt;Moescript 里在 lex 和 parse 之间有个过程，称为 Layout，来处理缩进。许多缩进语言的语法都不是上下文无关的，因此都必须有一个类似的过程来处理缩进。比如 Haskell 里就用一个函数来“修理”语素流，把缩进的代码块处理成 &lt;code&gt;{...; ...; ...;}&lt;/code&gt; 的形式。具体过程可以看 &lt;a href="http://www.haskell.org/onlinereport/haskell2010/"&gt;Haskell 2010 Report&lt;/a&gt; &lt;a href="http://www.haskell.org/onlinereport/haskell2010/haskellch10.html#x17-17800010.3"&gt;10.3 节&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;在早期的实现里，Moe 的解析器会在语素化过程里同时处理缩进（生成 &lt;code&gt;INDENT&lt;/code&gt; 和 &lt;code&gt;OUTDENT&lt;/code&gt; 语素），然而近期的一个改进就是分离了 Layout 过程，使得解析器更简单效率更高，同时可以方便的把 Lexer “改装”成高亮器（现在我 Moescript 文档的高亮器就是直接用它的 Lexer 驱动的）。&lt;/p&gt;
&lt;p&gt;Layout 过程分为两部分：第一步是去掉某些环境里的换行（比如括弧里的换行就会被忽略掉；运算符两边的亦然），另一步就是把换行（&lt;code&gt;NEWLINE&lt;/code&gt; 语素）进行 Layout。&lt;/p&gt;
&lt;p&gt;定义运算符 &lt;span class="equation"&gt;&lt;i&gt;a&lt;/i&gt; ⊏ &lt;i&gt;b&lt;/i&gt;&lt;/span&gt; 表示 &lt;span class="equation"&gt;&lt;i&gt;a&lt;/i&gt;&lt;/span&gt; 是 &lt;span class="equation"&gt;&lt;i&gt;b&lt;/i&gt;&lt;/span&gt; 的真前缀，即存在非空字符串 &lt;span class="equation"&gt;&lt;i&gt;c&lt;/i&gt;&lt;/span&gt;，有 &lt;span class="equation"&gt;&lt;i&gt;a&lt;/i&gt; + &lt;i&gt;c&lt;/i&gt; = &lt;i&gt;b&lt;/i&gt;&lt;/span&gt;。语素化过程中，每个换行语素（&lt;span class="equation"&gt;&lt;i&gt;t&lt;/i&gt;&lt;/span&gt;）都记录了缩进数 &lt;span class="equation"&gt;&lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt;&lt;/span&gt;（一个字符串，包括空格和 Tab），那么 Layout 可以用函数 &lt;span class="equation"&gt;&lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T&lt;/i&gt;, &lt;i&gt;S&lt;/i&gt;)&lt;/span&gt; 表示，其中 &lt;span class="equation"&gt;&lt;i&gt;S&lt;/i&gt;&lt;/span&gt; 为一个堆栈，维护已有的缩进序列。&lt;/p&gt;
&lt;p&gt;一个缩进更深的行，表示你写了一个缩进代码块，故会产生一个 &lt;code&gt;INDENT&lt;/code&gt; 语素，并且缩进堆栈会压入这一行的行首空白：&lt;/p&gt;
&lt;div class="display-eq"&gt;&lt;div class="equation"&gt;&lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], [&lt;i&gt;p&lt;/i&gt; : &lt;i&gt;S'&lt;/i&gt;]) = [INDENT : &lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T'&lt;/i&gt;, [&lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt; : &lt;i&gt;p&lt;/i&gt; : &lt;i&gt;S'&lt;/i&gt;])]  &lt;br /&gt;


&lt;i&gt;t&lt;/i&gt; 是 NEWLINE；&lt;i&gt;p&lt;/i&gt; ⊏ &lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;然而进行比较必须堆栈非空。在堆栈空时，应该补充一个边界状况：&lt;/p&gt;
&lt;div class="display-eq"&gt;&lt;div class="equation"&gt;&lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], [ ]) = [INDENT : &lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T'&lt;/i&gt;, [&lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt;])]  &lt;br /&gt;


&lt;i&gt;t&lt;/i&gt; 是 NEWLINE&lt;/div&gt;&lt;/div&gt;&lt;p&gt;缩进相同的两行，会把换行视作分号：&lt;/p&gt;
&lt;div class="display-eq"&gt;&lt;div class="equation"&gt;&lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], [&lt;i&gt;p&lt;/i&gt; : &lt;i&gt;S'&lt;/i&gt;]) = [SEMICOLON : &lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T'&lt;/i&gt;, [&lt;i&gt;p&lt;/i&gt; : &lt;i&gt;S'&lt;/i&gt;])]  &lt;br /&gt;


&lt;i&gt;t&lt;/i&gt; 是 NEWLINE；&lt;i&gt;p&lt;/i&gt; = &lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;而对于缩出（&lt;span class="equation"&gt;&lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt; ⊏ &lt;i&gt;p&lt;/i&gt;&lt;/span&gt;），情况就比较复杂了。对这种状况，栈顶元素 &lt;span class="equation"&gt;&lt;i&gt;p&lt;/i&gt;&lt;/span&gt; 会弹栈，并产生一个 &lt;code&gt;SEMICOLON&lt;/code&gt; 和 &lt;code&gt;OUTDENT&lt;/code&gt;，之后继续处理这个换行语素，直到能归并到前面的状况为止。这时 &lt;span class="equation"&gt;&lt;b&gt;L&lt;/b&gt;&lt;/span&gt; 的定义是：&lt;/p&gt;
&lt;div class="display-eq"&gt;&lt;div class="equation"&gt;&lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], [&lt;i&gt;p&lt;/i&gt; : &lt;i&gt;S'&lt;/i&gt;]) = [SEMICOLON : OUTDENT : &lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], &lt;i&gt;S'&lt;/i&gt;)]  &lt;br /&gt;


&lt;i&gt;t&lt;/i&gt; 是 NEWLINE；&lt;i&gt;j&lt;sub&gt;t&lt;/sub&gt;&lt;/i&gt; ⊏ &lt;i&gt;p&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;如果 &lt;span class="equation"&gt;&lt;i&gt;t&lt;/i&gt;&lt;/span&gt; 和栈顶元素 &lt;span class="equation"&gt;&lt;i&gt;p&lt;/i&gt;&lt;/span&gt; 之间无法比较，就会报异常：&lt;/p&gt;
&lt;div class="display-eq"&gt;&lt;div class="equation"&gt;&lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], [&lt;i&gt;S&lt;/i&gt;]) = SYNTAXERROR  &lt;br /&gt;


&lt;i&gt;t&lt;/i&gt; 是 NEWLINE；其他状况&lt;/div&gt;&lt;/div&gt;&lt;p&gt;接下来就是完善 &lt;span class="equation"&gt;&lt;b&gt;L&lt;/b&gt;&lt;/span&gt; 的其他情况&lt;/p&gt;
&lt;div class="display-eq"&gt;&lt;div class="equation"&gt;&lt;b&gt;L&lt;/b&gt;([&lt;i&gt;t&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;], &lt;i&gt;S&lt;/i&gt;) = [&lt;i&gt;t&lt;/i&gt; : &lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T'&lt;/i&gt;, &lt;i&gt;S&lt;/i&gt;)] &lt;br /&gt;


&lt;i&gt;t&lt;/i&gt; 不是 NEWLINE &lt;br /&gt;


&lt;b&gt;L&lt;/b&gt;([ ], [&lt;i&gt;p&lt;/i&gt; : &lt;i&gt;T'&lt;/i&gt;]) = [SEMICOLON : OUTDENT : &lt;b&gt;L&lt;/b&gt;([ ], &lt;i&gt;S&lt;/i&gt;)] &lt;br /&gt;


&lt;b&gt;L&lt;/b&gt;([ ], [ ]) = [ ]&lt;/div&gt;&lt;/div&gt;&lt;p&gt;那么，在 Lex 之后，就可以用 &lt;span class="equation"&gt;&lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T&lt;/i&gt;, [ ])&lt;/span&gt; 来 Layout 语素，生成用于 Parse 的语素流。上面的 &lt;span class="equation"&gt;&lt;b&gt;L&lt;/b&gt;(&lt;i&gt;T&lt;/i&gt;, &lt;i&gt;S&lt;/i&gt;)&lt;/span&gt; 模仿了 Haskell Report 里的一些形式。&lt;/p&gt;</description></item>
<item><title>JavaScript 常见误解，其一</title><link>http://typeof.net/mechanix/on-some-js-misunderstandings-1.html</link><guid>http://typeof.net/mechanix/on-some-js-misunderstandings-1.html</guid><pubDate>2012-05-13 23:27</pubDate><description>&lt;p&gt;这里来澄清一些常见的 JavaScript 流言和误解，不论来自大神还是草根……&lt;/p&gt;
&lt;h3&gt;最长行匹配&lt;/h3&gt;

&lt;p&gt;这个骗到了无数学 JS 的人啊……JS 的规范里描述了在某些情况下为源码增加分号的行为，然而在某些人的书里，这个行为被描述成：&lt;/p&gt;
&lt;blockquote&gt;如果单行语素构成一个完整的句子，那么不管末尾是否有分号，都作为一个独立句子来执行。否则的话匹配多行，直到这些行共同构成完整句子或者出现分号为止。&lt;/blockquote&gt;&lt;p&gt;很不幸，这是错的，而且正好和 ECMA Spec 相反。根据&lt;a href="http://es5.github.com"&gt;规范&lt;/a&gt; 7.9.1 节，自动分号插补的规则是：&lt;/p&gt;
&lt;blockquote&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;When, as the program is parsed from left to right, a token (called the &lt;em&gt;offending token&lt;/em&gt;) is encountered that is not allowed by any production of the grammar, then a semicolon is automatically inserted before the offending token if one or more of the following conditions is true:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;The offending token is separated from the previous token by at least one &lt;i&gt;LineTerminator&lt;/i&gt;.&lt;/li&gt;
&lt;li&gt;The offending token is &lt;code&gt;}&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;When, as the program is parsed from left to right, the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete ECMAScript &lt;i&gt;Program&lt;/i&gt;, then a semicolon is automatically inserted at the end of the input stream.&lt;/li&gt;
&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;When, as the program is parsed from left to right, a token is encountered that is allowed by some production of the grammar, but the production is a &lt;em&gt;restricted production&lt;/em&gt; and the token would be the first token for a terminal or nonterminal immediately following the annotation “&lt;i&gt;&lt;code&gt;[no LineTerminator here]&lt;/code&gt;&lt;/i&gt;” within the restricted production (and therefore such a token is called a restricted token), and the restricted token is separated from the previous token by at least one &lt;i&gt;LineTerminator&lt;/i&gt;, then a semicolon is automatically inserted before the restricted token.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;However, there is an additional overriding condition on the preceding rules: a semicolon is never inserted automatically if the semicolon would then be parsed as an empty statement or if that semicolon would become one of the two semicolons in the header of a for statement&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;这一段话相当冗长而且难以理解（因为牵涉到了编译原理里关于从左到右解析的相关知识），不过简单来说就是，如果解析器从左向右解析脚本时，如果看到前面的东西（语素），一个换行或者 &lt;code&gt;}&lt;/code&gt;，比较“扎眼”的时候，就会尝试先在后面那个“扎眼”的语素前面插入一个分号，尝试分析。因而，在某些特殊情形（比如 &lt;code&gt;return&lt;/code&gt; 后面）之外，其他的时候解析器都会优先把换行忽略掉，尝试继续解析到下一行。和所谓“最长行匹配”完全相反：最长行匹配会优先把换行当成分号而不是忽略之。或许最典型的例子就是&lt;/p&gt;
&lt;pre class="mghl source javascript"&gt;&lt;span class="identifier id"&gt;f&lt;/span&gt;
&lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;g&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;会被解释成 &lt;code&gt;f(g)&lt;/code&gt; 而不是 &lt;code&gt;f;(g)&lt;/code&gt;。&lt;/p&gt;

&lt;h3&gt;JavaScript 里所有数值都是浮点，因此位运算很慢&lt;/h3&gt;

&lt;p&gt;很不幸这又是一个“熟读规范”的人作出的错误预言。设想下，假如真的 JavaScript 里所有的数值都用浮点，那么第一个遭殃的肯定不是位运算，而是最简单的 &lt;code&gt;for&lt;/code&gt; 循环：&lt;/p&gt;
&lt;pre class="mghl source javascript"&gt;&lt;span class="keyword reserved"&gt;for&lt;/span&gt;&lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;i&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt; &lt;span class="identifier id"&gt;i&lt;/span&gt; &lt;span class="operator"&gt;&amp;lt;&lt;/span&gt; &lt;span class="identifier id"&gt;a&lt;/span&gt;&lt;span class="punctor"&gt;.&lt;/span&gt;&lt;span class="identifier id"&gt;length&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt; &lt;span class="identifier id"&gt;i&lt;/span&gt;&lt;span class="operator"&gt;++&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt;
    &lt;span class="identifier id"&gt;f&lt;/span&gt;&lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;a&lt;/span&gt;&lt;span class="punctor open"&gt;[&lt;/span&gt;&lt;span class="identifier id"&gt;i&lt;/span&gt;&lt;span class="punctor close"&gt;]&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;在大多数的芯片上浮点运算比整数要慢的多（更何况 JavaScript 的数值是 double），没人会笨到用浮点数去做 &lt;code&gt;for&lt;/code&gt; 循环的。事实上，绝大多数引擎都会“尽可能的”使用整数存储 JavaScript 数值，这个行为早在 IE6 出现的时候就有了。&lt;/p&gt;
&lt;p&gt;位运算的性能事实上并不慢，这里有一个&lt;a href="http://rocha.la/JavaScript-bitwise-operators-in-practice"&gt;评测&lt;/a&gt;表明运算 &lt;code&gt;~~x&lt;/code&gt; 并不比 &lt;code&gt;Math.floor(x)&lt;/code&gt; 慢。如果你要优化一些算数运算，使用位运算是一个有效的选择。&lt;/p&gt;
&lt;p&gt;下面这个函数是我以前写的，用来混合两个 ARGB8888 颜色。&lt;/p&gt;
&lt;pre class="mghl source javascript"&gt;&lt;span class="keyword reserved"&gt;function&lt;/span&gt; &lt;span class="identifier id"&gt;blend&lt;/span&gt;&lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;c1&lt;/span&gt;&lt;span class="punctor"&gt;,&lt;/span&gt; &lt;span class="identifier id"&gt;c2&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt;&lt;span class="punctor open"&gt;{&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;a1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="identifier id"&gt;c1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;24&lt;/span&gt;&lt;span class="punctor"&gt;,&lt;/span&gt; &lt;span class="identifier id"&gt;a2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="identifier id"&gt;c2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;24&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;a&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="identifier id"&gt;a1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;+&lt;/span&gt; &lt;span class="identifier id"&gt;a2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;-&lt;/span&gt; &lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;a1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;a2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;inv_a&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0x10000&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;/&lt;/span&gt; &lt;span class="identifier id"&gt;a&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;|&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;ma&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="identifier id"&gt;a1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="number"&gt;0x100&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;-&lt;/span&gt; &lt;span class="identifier id"&gt;a2&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;

    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;rb1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="identifier id"&gt;c1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;amp;&lt;/span&gt; &lt;span class="number"&gt;0xff00ff&lt;/span&gt;&lt;span class="punctor"&gt;,&lt;/span&gt; &lt;span class="identifier id"&gt;rb2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="identifier id"&gt;c2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;amp;&lt;/span&gt; &lt;span class="number"&gt;0xff00ff&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;rb&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;rb1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;ma&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;+&lt;/span&gt; &lt;span class="identifier id"&gt;rb2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;a2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;inv_a&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;amp;&lt;/span&gt; &lt;span class="number"&gt;0xff00ff&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;

    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;g1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;c1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;amp;&lt;/span&gt; &lt;span class="number"&gt;0xff00&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punctor"&gt;,&lt;/span&gt; &lt;span class="identifier id"&gt;g2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;c2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;amp;&lt;/span&gt; &lt;span class="number"&gt;0xff00&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;var&lt;/span&gt; &lt;span class="identifier id"&gt;g&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;=&lt;/span&gt; &lt;span class="punctor open"&gt;(&lt;/span&gt;&lt;span class="identifier id"&gt;g1&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;ma&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;+&lt;/span&gt; &lt;span class="identifier id"&gt;g2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;a2&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&gt;&gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt;&lt;span class="punctor close"&gt;)&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;*&lt;/span&gt; &lt;span class="identifier id"&gt;inv_a&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;amp;&lt;/span&gt; &lt;span class="number"&gt;0xff00&lt;/span&gt;&lt;span class="punctor SEMICOLON"&gt;;&lt;/span&gt;
    &lt;span class="keyword reserved"&gt;return&lt;/span&gt; &lt;span class="identifier id"&gt;a&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="number"&gt;24&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;|&lt;/span&gt; &lt;span class="identifier id"&gt;rb&lt;/span&gt; &lt;span class="operator ASSIGN"&gt;|&lt;/span&gt; &lt;span class="identifier id"&gt;g&lt;/span&gt;
&lt;span class="punctor close"&gt;}&lt;/span&gt;&lt;/pre&gt;</description></item>
<item><title>Moescript 近况 + 编辑器计划</title><link>http://typeof.net/mechanix/moe-recent-1.html</link><guid>http://typeof.net/mechanix/moe-recent-1.html</guid><pubDate>2012-05-13 20:10</pubDate><description>&lt;p&gt;最近主要还是在坑文档。Moe 的标准库，包括 &lt;code&gt;std&lt;/code&gt;，&lt;code&gt;stdenum&lt;/code&gt; 和 &lt;code&gt;async&lt;/code&gt; 都合并到了一起，称为 &lt;code&gt;moe/prelude&lt;/code&gt;（别吐槽名字），并且把「能做到的部分」全部 Moe 化。我把「二进制」&lt;code&gt;moec&lt;/code&gt; 加上了一个选项，可以自定义 Runtime 以及其他全局变量的绑定，让写在 JavaScript 里的那些函数和用 Moe 写的函数能够协同工作。&lt;/p&gt;</description></item>
<item><title>这个网站是怎么做出来的？</title><link>http://typeof.net/chaotix/the-site-in-detail.html</link><guid>http://typeof.net/chaotix/the-site-in-detail.html</guid><pubDate>2012-05-13 17:16</pubDate><description>&lt;p&gt;现在的 typeof.net 里面&lt;strong&gt;一个动态文件也没有&lt;/strong&gt;，所有的东西都是用 &lt;a href="http://github.com/be5invis/eido"&gt;&lt;code&gt;eidoc&lt;/code&gt;&lt;/a&gt; 从 .ed 文件生成的 HTML。你也许会问我为什么不用 markdown 这种流行物，我要说，markdown 没有宏，没法作出 eido 这种逆天的效果。整个 typeof.net 我没有写&lt;strong&gt;一行&lt;/strong&gt; HTML，所有的东西都是 &lt;code&gt;eidoc&lt;/code&gt; 生成的。&lt;/p&gt;</description></item>
<item><title>一个新开始</title><link>http://typeof.net/chaotix/a-new-start.html</link><guid>http://typeof.net/chaotix/a-new-start.html</guid><pubDate>2012-05-13 17:16</pubDate><description>&lt;p&gt;把网站挪到 git 上面感觉还可以，我自己写了个 makefile 来生成全站的 html，而原始格式是 &lt;a href="http://github.com/be5invis/eido"&gt;Eido&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;总的来说效果还是可以的。虽然折腾 git 的钩子花了一段时间。&lt;/p&gt;</description></item>
<item><title>Some test</title><link>http://typeof.net/chaotix/sometest.html</link><guid>http://typeof.net/chaotix/sometest.html</guid><pubDate>2012-05-11 16:14</pubDate><description>Some test.</description></item>
<item><title>Some test</title><link>http://typeof.net/mechanix/sometest.html</link><guid>http://typeof.net/mechanix/sometest.html</guid><pubDate>2012-05-11 16:14</pubDate><description>Some test.</description></item></channel>
</rss>

