htsign's blog

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

Partial Active Patterns

引数で受けた正規表現を評価して、ヒットしたら返します。
習作として作りました。

gist.github.com

使い方

アクティブパターンなので、パターンマッチの内部でリストから取り出すイメージですね。
マッチしなければ次のパターン(この例では| _ -> ()の行)に評価が移ります。
今回はデモなのでリストをそのまま出力しています。

open RegExp

let testString  = "abc123def456ghi789"
let testPattern = @"(?<name>[a-z])+(?<number>[0-9])+"

// 単純なマッチ
match testString with
| Match testPattern result -> printfn "%A" result
| _ -> ()

// 単純なマッチの集合
match testString with
| Matches testPattern result -> printfn "%A" result
| _ -> ()

// 名前付きマッチ
match testString with
| NamedMatch testPattern result -> printfn "%A" result
| _ -> ()

// 名前付きマッチの集合
match testString with
| NamedMatches testPattern result -> printfn "%A" result
| _ -> ()

(* 上記 result の部分もパターンマッチなので、
   うまいことリストのリテラルを書いて受けると、そのままリストの中身を取り出すこともできます。
   参考にしたMicrosoft Docsのドキュメントにはこの方法が書かれています。 *)
match testString with
| Match testPattern [e1; e2] -> printfn "e1: '%s'\ne2: '%s'" e1 e2
| _ -> ()

出力

// 単純なマッチ
["c"; "3"]

// 単純なマッチの集合
[["c"; "3"]; ["f"; "6"]; ["i"; "9"]]

// 名前付きマッチ
map [("name", "c"); ("number", "3")]

// 名前付きマッチの集合
[map [("name", "c"); ("number", "3")]
 map [("name", "f"); ("number", "6")]
 map [("name", "i"); ("number", "9")]]

// 下のリストで受けたやつ
e1: 'c'
e2: '3'

これだけあれば、とりあえずはだいたいのニーズを満たせるのではないかと思います。

参考: Active Patterns (F#) | Microsoft Docs

追記

名前付きマッチの返り値をMapにしました。
添え字でアクセスできるようになります。

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における重要な概念です。