前回に引き続きPOP
今回はProtocol-Oriented Programmingが,
PONS=Protocol-Oriented Number Systemの紹介
前回取り上げた実例は,
- 誰かが任意精度の数値ライブラリを用意すれば,
それを使うことも可能
でもそれって本当?
それが,
図1 swift-pons
使い方は簡単。
- ①
git clone
してPONS.
を開いて,xcworkspace Framework-OSX
をビルドしたらOSX Playground
の実例が実際に動くようになります。試しに(1...
と打ってみてください。100).reduce(BigInt(1),combine:*) - ②もちろんREPLでも動きます。
make repl
するとREPLが立ち上がるので,import PONS
してから(1...
とか打ってみてください。100).reduce(BigInt(1),combine:*).description - ③読者ご自身のプロジェクトで使いたい場合も,
Frameworkをコピーしてもよし, ソースファイルをコピーしてもよしです。
これで,
RubyやPythonやHaskellではおなじみの任意精度整数
( BigInt
)が, GMPなどの外部ライブラリなしで使えるようになります。
→素数判定メソッドも付いてきます。いや,同じく任意精度整数が組込みのPerl6にも組込みだったので。 有理数型
(Rational) もついてきます。分子と分母の型を引数とする総称型ですので, もちろん任意精度有理数 =Rational<BigInt>
も使えます。よく使うのでBigRat
ととしてtypealias
してあります。任意精度浮動小数点数
( BigFloat
)もついています。 BigRat
だけでも好きなだけ小さな数も実現されているのですが,より高速かつ省スペースです。
→総称的に定義された初等関数(elementary functions) の実装も付いてきます。たとえば Float128
とか,新たな数値型を実装したときに exp
やlog
やsin
やcos
を書き直す必要はありません。実際BigRat
とBigloat
はそれぞれまったく別の型なのに,これらの関数のソースは共通です。
しかし,
1つで十分ですよ, わかってくださいよ!
古のCの時代,man cos
してみると……,
NAME
cos -- cosine function
SYNOPSIS
#include <math.h>
double
cos(double x);
long double
cosl(long double x);
float
cosf(float x);
倍精度double
用にcos()
,float
用にcosf()
,long double
用にcosl
と,cosq()
でも加えるんですか? 昨今GPUで採用されはじめている半精度cosh()
ですか? でも待って,cosh
はもう双曲線コサイン
ぶっちゃけ付き合ってられませんよね?
Swiftは,
#if os(Linux)
import Glibc
#else
import Darwin
#endif
された状態でXcodeにてcos
と打つと……,Double
もFloat
もCGFloat
も,cos
で呼び出せることがわかります。
しかし,
こうですか?
func fib(n:Int8)->Int8 { return n < 2 ? i: fib(n-2)+fib(n-1) }
func fib(n:Int16)->Int16 { return n < 2 ? i : fib(n-2)+fib(n-1) }
func fib(n:Int32)->Int32 { return n < 2 ? i : fib(n-2)+fib(n-1) }
func fib(n:Int64)->Int64 { return n < 2 ? i : fib(n-2)+fib(n-1) }
だが断る!
だがしかし,
func fib<T>(i:T)->T { return i < 2 ? i :fib(n-2)+fib(n-1) }
でもT
を足したりTどうしを比較する方法をSwiftは知りませんから,Hoge
があれば,
func fib<T:Hoge>(i:T)->T { return i < 2 ?i : fib(n-2)+fib(n-1) }
で行けるはずだ。でもそのHoge
ってどこにあるの? PONSは,Hoge
はPOIntegerが相当します。
import PONS
func fib<T:POInteger>(n:T)->T {
if n < T(2) { return n }
var (a, b) = (T(0), T(1))
for _ in 2...n {
(a, b) = (b, a+b)
}
return b
}
で,
let F11 = fib(11 as Int8)
let F13 = fib(13 as UInt8)
let F23 = fib(23 as Int16)
let F24 = fib(24 as UInt16)
let F46 = fib(46 as Int32)
let F47 = fib(47 as UInt32)
let F92 = fib(92 as Int64)
let F93 = fib(93 as UInt64)
ぜひご自身でご確認を。
しかし,
BigInt
でやってみましょう。
let F666 = fib(666 as BigInt)
6859356963880484413875401302176431788073214234535725264860437720157972142108894511264898366145528622543082646626140527097739556699078708088
になりましたか?
でも,
……失礼しました。SEGVです。XcodeでProtocolを多様したプログラムを書いていると本当によくお目にかかれます:-(。
しかし数値は整数だけではありません。整数だけで満足できるのは小学生とクロネッカー先生
とはいえこれらを漠然と並べただけでは,
向き合った結果が,
だから,
Double.sqrt(-1) // NaN
Complex.sqrt(-1) // (0.0+1.0.i)
// そもそも比較できない
1.0+0.0.i < 2.0+0.0.i
// 絶対値を見ればおk
(1.0+0.0.i).abs < (2.0+0.0.i).abs
これが,
- 注1)
- ドイツの数学者