書いて覚えるSwift入門

第13回 Protocol-Oriented Programming

この記事を読むのに必要な時間:およそ 4 分

前回に引き続きPOP

今回はProtocol-Oriented Programmingが,実際どれほど使い物になるかを実例とともに紹介していきます。

PONS=Protocol-Oriented Number Systemの紹介

前回取り上げた実例は,複素数(complexnumbers)の実装,swift-complexでした。記事にはこうあります。

  • 誰かが任意精度の数値ライブラリを用意すれば,それを使うことも可能

でもそれって本当?

それが,今回紹介するPONS = Protocol-Oriented Number Systemを書くことになったきっかけです図1)。

図1 swift-pons

図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とか,新たな数値型を実装したときにexplogsincosを書き直す必要はありません。実際BigRatBigloatはそれぞれまったく別の型なのに,これらの関数のソースは共通です。

しかし,PONSの本当のウリはそこじゃないんです。

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と,標準で用意されているだけで3種類もあります。四倍精度(float128)とかが標準装備になったらcosq()でも加えるんですか? 昨今GPUで採用されはじめている半精度(float16)cosh()ですか? でも待って,coshはもう双曲線コサイン(hyperbolic cosine)に取られちゃってますよ?

ぶっちゃけ付き合ってられませんよね?

Swiftは,はじめからこの問題にある程度対処されています。

#if os(Linux)
import Glibc
#else
import Darwin
#endif

された状態でXcodeにてcosと打つと……,図2のように,DoubleFloatCGFloatも,どれも同じcosで呼び出せることがわかります。

図2 COSの呼び出し

図2 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) }

だが断る!

だがしかし,Swiftには総称型があります。こうは書けないのでしょうか?

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は,まさにそのためにあるのです。実際に試してみましょう。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

になりましたか?

でも,プロトコルなら運命を変えられる。避けようのない重複コードも,嘆きも,すべて君が覆せばいい。だからPONSと契約して,数学ガールになってよ!

……失礼しました。SEGVです。XcodeでProtocolを多様したプログラムを書いていると本当によくお目にかかれます:-(。

しかし数値は整数だけではありません。整数だけで満足できるのは小学生とクロネッカー先生注1だけです。有理数も浮動小数点数もあるんだよ。

とはいえこれらを漠然と並べただけでは,体系(system)とはいえません。同じ/だって,整数型と実数型で違いますし。しかも,ただ符号なし整数→符号付き整数→実数→複素数とトップダウンにするわけにもいかないのです。確かに複素数は,四則演算と冪乗根(べきじょうこん)に対して閉じていますが,大小比較ができないというほかの数値型にはない特徴があります。

向き合った結果が,冒頭のグラフになります。ご覧いただければわかるとおり,複素数は実数からできているけど,大小比較はできないという関係が確かに成立しています。

だから,きちんとこのようになります。

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)
ドイツの数学者

著者プロフィール

小飼弾(こがいだん)

1969年生まれ,東京都出身。元ライブドア取締役の肩書きよりも,最近はPokemon GOのガチトレーナーのほうが有名になりつつある……かもしれない永遠のエンジニアオヤジ。

活躍の場はIT業界だけでなく,サブカルからアカデミックまで多方面にわたり,ネットからの情報発信は気の向くまま毎日毎秒! https://twitter.com/dankogai,ニコニコチャンネルは,http://ch.nicovideo.jp/dankogai,blogはhttp://blog.livedoor.jp/dankogai/

当社刊行書籍は『小飼弾のアルファギークに逢ってきた』『小飼弾のコードなエッセイ』など。他にも著書多数。

コメント

コメントの記入