htsign's blog

ノンジャンルで書くつもりだけど技術系が多いんじゃないかと思います

F#のべき乗計算

現在F#の勉強を始めたところで、基礎的なところをぼちぼち習得している段階です。

演算子の話なんですが、F#にはOCamlの系譜だからなのか**演算子(べき乗)があります。
RubyPythonなど、関数型の血を引いたいくつかの言語にも備わっている珍しくもない演算子なんですが、
なんとこの演算子、F#においては浮動小数点型でしか使えないのです…。
コンパイルするとMath.Pow : float * float -> floatになるようなので、当然と言えば当然ですね…)
追記: コンパイル後に Math.Pow になるのはインライン関数だからで、実際は Pow 静的メソッドを持つ任意の型に対して使える模様。
つまり BigInteger とかでも使えます。

なので整数計算で**演算子を使用したい場合は、

let i = 2 ** 3 // ここでコンパイルエラー
printfn "%i" i

ではダメで、

let i = 2.0 ** 3.0 |> int
printfn "%i" i // 8

などとしなくてはならず、少々面倒くさいです。

どうしてもという場合にはpown関数がありますが、あくまで関数なので演算子のように中置はできない模様。

let i = pown 2 3
printfn "%i" i // 8

// パイプ演算子を使うと中置っぽくなるが、アホっぽい
let i2 = 2 |> pown <| 3
printfn "%i" i2 // 8

ぐぬぬ

全てのフレームを対象にするには

某大手工業系企業の新規開発プロジェクトに参画しているのですが、非常に悲しいことに社内ポータルが未だにフレームを使用したサイトなのです。
フレームを使うと容易にペインで分けたサイトを構築できるため、実用重視のエンタープライズ界隈で重宝されるのはよく分かります。

ただ、フレーム*1を使うと、ブックマークレットを作ろうと思ったときにそのままではトップレベルのウィンドウにしか効力を発揮しません。
埋め込まれているフレームのソースURLがトップレベルのウィンドウと同じドメインに属しているのであれば*2、以下のようなコードを使うことで全フレームに対して操作することができます。

// for IE9+
(function loop(win) {
  return Array.prototype.reduce.call(win, function(p, c) {
    return p.concat(loop(c.window));
  }, [win]);
})(window)
.forEach(function(window) {
  var document;
  try { document = window.document; } catch(e) { return; }
  /* code here */
});
// compressed
(function l(w){return [].reduce.call(w,function(p,c){return p.concat(l(c.window))},[w])})(window).forEach(function(w){try{var d=w.document}catch(e){return}/* code here */})

*1:<iframe>も同様です。

*2:Same-Origin PolicyはWebにおける重要な概念です。

最近 Nemerle がマイブームです

私自身は最近知ったのですが、実は以前からある言語っぽいですね。
C#によく似た構文を数多く持ち、関数型言語によく見られる機能(パターンマッチングとかタプルとか)を言語レベルで実装しているのが特徴です。
型を静的にチェックできる強力なマクロもあって、かなり自由度の高いコードが書けるようです。
foreach文なんかもマクロで書かれているそうです。

あと#pragma indentディレクティブを宣言することでPythonやF#, CoffeeScript, Haml, SASSなどで馴染みのある「インデントが意味を持つ」書き方ができます。

自分はまだそこまで使いこなせてないですが、書いていてすごく楽しいです。
知名度もそれほどなくコミュニティもあまり活発ではないようですが、C#もF#も好きって人には向いているんではないでしょうか。

例えば、以下は拙い知識で書いたフィボナッチ数列を求めるコードです。 gist.github.com

これはC#で書くと大体こんなような感じです。

using static System.Console;
using System.Numerics;

static class Program
{
    static void Main()
    {
        Action<int> fib = null;
        fib = n =>
        {
            WriteLine("{0,8} : {1}", n, Fibonacci(n));
            if (n % 10 == 0) ReadLine();
            fib(n + 1);
        };

        fib(1);
    }

    private static BigInteger Fibonacci(int n)
    {
        var a = BigInteger.Zero;
        var b = BigInteger.One;

        foreach (int i in Enumerable.Range(0, 31).Reverse())
        {
            BigInteger c = a;
            a = a * (b * 2 - a);
            b = c * c + b * b;

            if ((((uint)n >> i) & 1) != 0)
            {
                c = a;
                a = b;
                b = c + b;
            }
        }
        return a;
    }
}

これだけだと正直何がいいのか伝わらない自覚はあります。
Nemerleらしい箇所はタプルでの多重代入とdefを使った型推論くらい…。

match (expr)defの直下やforeachの直下で省略できるため、

// A(文字コード: 65)からz(同: 122)までの大文字で、かつ偶数番目に出現する文字のみを出力
foreach (c in $['A' .. 'z'] with index)
{
  | x when char.IsUpper(x) && index % 2 == 1 => Write(c)
  | _ => ()
}
WriteLine();

// 冗長に書くとこう
foreach (c in $['A' .. 'z'] with index)
{
  match (c)
  {
    | x when char.IsUpper(x) && index % 2 == 1 => Write(c)
    | _ => ()
  }
}

こんな感じでforeachからのパターンマッチとかやるとちょっとはメリットが見えてくるかもしれません。
分かりやすさのためにあえてcxと変数名を変えましたが、Nemerleはスコープが異なる場合は変数名のオーバーライドが可能なので、

// 上のコードで、4行目をこう書いても意味は同じ
  | c when char.IsUpper(c) && index % 2 == 1 => Write(c)

です。

こんなコードも書けます。

Main() : void
{
  def n = 10;
  
  def somefunc(n)
  {
    $[n ** 3, n in [1 .. n]]
  }
  
  def listToString(list)
  {
    "[" + string.Join(", ", list) + "]"
  }
  
  def list = somefunc(n);
  WriteLine(listToString(list));
}

nが何を指しているのか、コンテキストが分かりづらいのでほどほどにするのがよさげですね。

個人的には自由度が上がったC#の延長、みたいな感じに捉えています。
地味にC# 7.0から対応の2進数リテラル0b1011みたいなの)もとっくのとうに実装済みだったり、便利です。

そんなわけで、はてなさんにはNemerleシンタックスハイライトに対応していただきたく。

リンク化スクリプト

かねてからHatena::Let で公開 *1 していて、個人的にもかなり使っているスクリプトGreaseMonkey用に書き直したのでそれも公開してみます。
https://gist.github.com/htsign/5eed5473a9e75c7c45f3a5571d7d0803/raw/convertTextToLink.user.js

GreaseMonkey用っていうか単にES2015で書き直しただけで、特別GreaseMonkeyには依存していませんが。

せっかくなのでGreaseMonkey版はMutationObserverによる追加ノードの監視機能も付けました。
Hatena::Letの方(以下、ブックマークレット版)にはこの機能は持たせていません。
対象となるブラウザの範囲にIEも入るからです。
新しいノードに適用する為には都度実行してやれば済む話で、別にそんなに手間なわけでもありませんし。

一応Mutation Eventsdocument.addEventListener("DOMNodeInserted", ...みたいなやつ)はIE9から対応していますが、これは既に非推奨となった技術なので積極的に採用するつもりはないです。
場合分けめんどいですしね(本音)

NodeIteratorで探索しているのでそれなりに速いと思いますが、そもそもの計算量の時点でたかが知れてるのでアレです。

注意点として、テキストに対して要素を追加する(DOMに破壊的変更を加える)為、DOMの構造に依存した外部コードがある場合には不具合の原因となる可能性があります。

*1:ブックマークレット版もES2015版での大幅な編集に合わせてだいぶ構造変わってます