スキップしてメイン コンテンツに移動

Punning いろいろ

Perl 6 に colon pairs という構文がある。名前の通りコロンから始まる Pair のコンストラクタで、例えば次のように使う:

:foo(42)   # foo => 42
:bar<yay>  # bar => 'yay'; 値が括弧類を使うリテラルの場合は小括弧は不要
:baz       # baz => True; 値を省略すると True になる
:!quux     # quux => False;  実に Perl らしい否定形
:$var      # var => $var

言う間でもないがこれは Hash を生成する場合のみならず、名前つき引数を渡す際にも便利である。 :$var という形式は変数の (sigil を除いた) 名前がそのままキーになるので、Perl 5 でよくある f(foo => $foo, bar => $bar, ...) といった繰り言をひとまとめにできる。 こういう構文を Punning (語呂合わせ、地口) と言ったりする。

対称的に関数のシグネチャも同様に書ける:

sub f(:$x, :$y) { $x - $y }

my $x = 42;
say f(:$x, :y(1));   # 41
say f(y => 1, :$x);  # ditto; 名前つき引数なので順番は可変

JavaScript の場合

JavaScript は冗長な用語を好むので Punning より Destructuring Assignment (の省略構文) と言った方が通りが良いだろう。 Destructuring Assignment の正確な構文は複雑だが、「右辺のオブジェクトを構築するときに値を置くべき場所に識別子を置くと対応する値が束縛される」程度の認識で概ね正しい:

const { x: value } = { x: 42 };  // value === 42

そして代入しようとする識別子名がオブジェクトのキーと同じ場合は識別子を省略できる:

const { x: x } = { x: 42 }  // x === 42
const { x } = { x: 42 };    // ditto; 上記の省略形

このようにオブジェクトを分解して代入できるのだから、オブジェクトの構築も対称にできるのが自然である:

const foo = 42;
const bar = 'yay';
const baz = true;
{ foo, bar, baz };  // { foo: foo, bar: bar, baz: baz } 即ち { foo: 42, bar: 'yay', baz: true }

OCaml の場合

Punning という用語を公式に使っているのは OCaml が有名だろう。対象はレコード型のフィールドと名前付き引数である。前二者の言語と違い静的型検査があるので安全である。

type record =
  { foo: int
  ; bar: string
  ; baz: bool
  }

let f { foo; bar; baz } =
  Printf.printf "foo: %d, bar: %s, baz: %b\n" foo bar baz

let () =
  let foo = 42 and bar = "yay" and baz = true in
  let r = { foo; bar; baz }  (* { foo = foo; bar = bar; baz = baz } *) in
  f r  (* foo: 42, bar: yay, baz: true *)
let f ~x ~y = x - y

let () =
  let x = 42 and y = 1 in
  print_int (f ~x ~y); print_newline ();  (* f ~x:x ~y:y = 41 *)
  print_int (f ~y ~x); print_newline ()   (* ditto *)

フィールド名はモジュールに所属しているので、モジュール外からレコードを生成しようとすると微妙な構文になる:

module M = struct
  type record =
    { foo: int
    ; bar: string
    ; baz: bool
    }
end

(*
  M.record 型の M.foo フィールドの値を変数 foo に束縛する。
  bar, baz も同様だが、既に M に所属していることが明らかなのでモジュール名を省略可能。
*)
let f { M.foo; bar; baz } =
  Printf.printf "foo: %d, bar: %s, baz: %b\n" foo bar baz

let () =
  let foo = 42 and bar = "yay" and baz = true in
  let r = { M.foo; bar; baz } in  (* { M.foo = foo; M.bar = bar; M.baz = baz } *)
  f r

コメント

このブログの人気の投稿

OCaml で Web フロントエンドを書く

要旨フロントエンド開発に Elm は堅くて速くてとても良いと思う。昨今の Flux 系アーキテクチャは代数的データ型と相性が良い。ところで工数を減らすためにはバックエンドも同じ言語で書いてあわよくば isomorphic にしてしまいたいところだが、Elm はバックエンドを書くには現状適していない。OCaml なら js_of_ocaml でエコシステムを丸ごとブラウザに持って来れるのでフロントエンドもバックエンドも無理なく書けるはずである。まず The Elm Architecture を OCaml で実践できるようにするため Caelm というライブラリを書いている。俺の野望はまだまだこれからだ (未完)Elm と TEA についてElm というプログラミング言語がある。いわゆる AltJS の一つである。 ミニマリスティクな ML 系の関数言語で、型推論を持ち、型クラスを持たず、例外機構を持たず、変数の再代入を許さず、正格評価され、代数的データ型を持つ。 言語も小綺麗で良いのだが、何より付属のコアライブラリが体現する The Elm Architecture (TEA) が重要である。TEA は端的に言えば Flux フロントエンド・アーキテクチャの変種である。同じく Flux の派生である Redux の README に TEA の影響を受けたと書いてあるので知っている人もいるだろう。 ビューなどから非同期に送信される Message (Redux だと Action) を受けて状態 (Model; Redux だと State) を更新すると、それに対応して Virtual DOM が再構築されビューがよしなに再描画され人生を書き換える者もいた——という一方向の流れはいずれにせよ同じである。 差異はオブジェクトではなく関数で構成されていることと、アプリケーション外部との入出力は非同期メッセージである Cmd / Sub を返す規約になっていることくらいだろうか。後者は面白い特徴で、副作用のある処理はアプリケーションの外で起きて結果だけが Message として非同期に飛んでくるので、内部は純粋に保たれる。つまり Elm アプリケーションが相手にしないといけない入力は今現在のアプリケーションの完全な状態である Model と、時系列イベントである Me…

京大テキストコーパスのパーサを書いた

要旨CaboCha やなんかの出力形式であるところの京大テキストコーパス形式のパーサモジュールを Perl で書いたので紹介します。GithubTarball on Github Ppagesこれを使うと例えば CaboCha の出力した係り受け関係を Perl のオブジェクトグラフとして取得できます。使用例単なる文節区切りの例。#!/usr/bin/env perl use v5.18; use utf8; use IPC::Open3; use Parse::KyotoUniversityTextCorpus; use Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab; use Symbol qw//; my ($in, $out, $err); my $pid; BEGIN { ($in, $out, $err) = (Symbol::gensym, Symbol::gensym, Symbol::gensym); $pid = open3($in, $out, $err, cabocha => '-f1'); } END { close $out; close $err; waitpid $pid => 0 if defined $pid; } binmode STDOUT, ':encoding(utf8)'; binmode $in, ':encoding(utf8)'; binmode $out, ':encoding(utf8)'; my $parser = Parse::KyotoUniversityTextCorpus->new( morpheme_parser => Parse::KyotoUniversityTextCorpus::MorphemeParser::MeCab->new, ); say $in '星から出るのに、その子は渡り鳥を使ったんだと思う。'; say $in '出る日の朝、自分の星の片付けをした。'; close $in; my $sentence_trees = $parser->…

Algorithm::LibLinear の紹介

Notice: This article is outdated. Please refer an updated English tutorial. 要旨かなり前になりますが、Algorithm::LibLinear という Perl モジュールを書きました。CPANGithubこれを使うと線形分類器などが高速に学習できます。テキストや画像の分類が応用として期待されます。LIBLINEAR についてLIBLINEARLIBSVM と同じ台湾国立大学の Chih-Jen Lin 教授のチームが公開しているオープンソースの機械学習パッケージです。 関数のロジスティック回帰、サポートベクター回帰及び線形 SVM による多クラス分類を行うことができます。LIBSVM と違ってカーネル関数を使うことはできませんが、はるかに高速に動作します。Algorithm::LibLinear についてLIBLINEAR には C++ で書かれたライブラリと、その機能を使って機械学習と分類・関数回帰を行うコマンドラインユーティリティが含まれています。 Algorithm::LibLinear はライブラリの機能を Perl からオブジェクト指向的に利用できるようにした上で、コマンドラインユーティリティの一部機能をライブラリ化して Perl で再実装したものです。使い方分類問題を解くときは、訓練データセットの読み込み・スケーリング学習器パラメータの設定分類器の訓練実データの分類という手順で行います。訓練データセットの読み込み正解ラベルのついたデータを大量に用意して学習させます。LIBSVM 形式のデータを読み込むか:my $data_set = Algorithm::LibLinear::DataSet->load(string => <<'EOD'); 1 1:0.1 2:0.1 4:0.1 -1 1:0.1 2:-0.1 3:0.1 ... EOD HashRef として表現されたデータを使います:my $data_set = Algorithm::LibLinear::DataSet->new(data_set => [ +{ feature => +{ 1 => 0.1, 2 => 0.1, 4 =…