ベクトルの内積を求める
Perl 6に関する情報が少なめなのでメモ書きです。
n次元ベクトルa,bの内積
を求める関数(演算子)は次のように書けます:
# n次元ベクトルの内積を求める中置演算子 sub infix:<・> (@a, @b) { [+] map {$^a * $^b} zip(@a, @b) };
演算子として定義しているのには特に意味はありません(ただしUTF-8で書かないと多分動きません)。説明は後回しにして,これはこんな風に使えます:
#! /usr/bin/pugs use v6; sub infix:<・> (@a, @b) { [+] map {$^a * $^b} zip(@a, @b) }; my Num @x = <2 3 5>; my Num @y = <1 2 3>; my Num @z = <1 1 -1>; (@x・@y).say; # 23 (@x・@z).say; # 0 (@y・@z).say; # 0 (@z・@z).say; # 3
それでは説明です。ます,この関数「・」は中置演算子(infix operator)として定義されています。infix以外にもprefixやらcircumfixやら色々あります。で,(@a, @b) がこの関数(演算子)の取る引数です。これは通常通り。
関数の中は,大きく2つの手順で内積を求めています。最初に map 関数でそれぞれの項の積を求めて(この時点ではリスト),次に [+] という reduction operator*1 を使って和を求めています(この時点でスカラー)。
通常map関数っていえば1つのリストを取って1つのリストを返すもんですが,ここでは2つのリストを取って1つのリストを返しています。ここで便利なのがzip関数。これは一番多いのは for zip(%hash.keys, %hash.values) -> $key, $value { ... } というfor文での使い方でしょう。でも,mapに食わせることもできます。zipによって,それぞれのリストの先頭から,1回のループでそれぞれ1つずつ抜き出してmapに渡してくれます。
それから $^a とか $^b とかいう書き方ですが,これはいわゆる以前の map での $_ や,sort での $a と $b のことです。多分Rubyでイテレータに渡すブロックで現れる {|x| ...} と同じ表現,といえば一番分かりやすいのかな,知ってる人には。Perl 6ではハット君 (^) を付けます。
これで map によって返されるリストは ($a[0] * $b[0], $a[1] * $b[1], ...) というものになりました。後はこれを足し合わせて一つの値にして返してやるだけです。「リスト→1つの値」にする便利な高階関数(いわゆるapply関数)というのはPerl 5にはなかったのですが,Perl 6ではreduction operatorというものが使えるようになりました。ここではΣと同じ役割をする [+] 演算子を使っています。これらのreduction operatorはリスト操作の関数なのですが,ちょっと動作が変わっていて,例えば [+] (3 2 1) と書くと,まず始めに「3+2」が評価され,次に「その結果+1」が評価されます。(3+2)+1 の結果なので,スカラー値が返されます。なお,左に結合されるとは限りません。詳しく知りたい方は,雰囲気は 404 Blog Not Found:mapとはおれのことかとcollectいい あたりで,詳細は http://dev.perl.org/perl6/doc/design/syn/S03.html あたりを読んでみてください。
そんなこんなで,内積を求める演算子が定義できます。おさらいすると,infix で中置演算子として宣言し,zip関数によって2つのリストからまとめて値を引き出し,map関数でそれらを掛け算した値を1つのリストにまとめ,それを [+] 演算子で足し算してスカラー値にして返しています。
以上です。