すでに始まったPOP
(Protocol Oriented Programming)の時代
今回はいよいよ懸案のProtocol Oriented Programmingについて解説します。
Swift Standard Library
Classが少ないのです。たったの5つ。しかも1つは継承でつながっているので実質3つ

それに対してStructとEnumとProtocolはどっさりあります。これは何を意味するのか?
SwiftにおいてClassというのはあくまでもObjective-Cの遺産を活用するためのものであって、
ClassとStructやEnumの違い
それを理解するためには、
Classは継承できるが、
class ClassV1 {
var x = 0.0
init (x:Double) {
self.x = x
}
}
class ClassV2 : ClassV1 {
var y = 0.0
init (x:Double, y:Double) {
super.init(x:x)
self.y = y
}
}
class ClassV3 : ClassV2 {
var z = 0.0
init (x:Double, y:Double, z:Double) {
super.init(x:x, y:y)
self.z = z
}
}
var cv3 = ClassV3(x:1, y:2, z:3)
なぜ継承できないのか? 参照型であるClassと異なり、x
にアクセスするには親クラスの親クラスまで参照をたぐらなければなりません。sizeofValue(cv3)
はポインターのサイズである8
。Classのインスタンスであれば、
これに対し、
struct StructV3 {
var x = 0.0, y = 0.0, z = 0.0
init (x:Double, y:Double, z:Double) {
self.x = x
self.y = y
self.z = z
}
}
let sv3 = StructV3(x:1, y:2, z:3)
ここでsizeofValue(sv3)
は、Double
のきっかり3倍である24。確かに実体を持っています。StructやEnumは、
「えー、
でも、 それを可能にするのが、 論より証拠。実例を見てみましょう。[swiftcomplex]というgithub projectがあります。Swiftの演習用に筆者がずっと書いてきたものですが、 要は複素数を使うためのライブラリです。使い心地はRubyであれば、 Pythonであれば
したときにとてもよく似ています。余談ではありますが、 そのまま遊べるように、 で、 では実際に見てみましょう。500行ちょっとしかないので全部掲載したいところですが、 まず、 というのをSwift語で書き下しただけです。 で、 とすでに ここまではSwift 1の時代からあったのですが、 ご覧のとおり、 Protocolといういうのは、 実際Swift 2では、 ではいよいよ リスト4は見てのとおり、 ところで賢明な読者は、 うまいこと、 まず、 そしてこれをProtocol Extensionで拡張します 要するに、 そうしたうえで、 つまり複素数の要素が あとは、 共役 次に"cmath” 「博士の愛した数式」 というわけで弾言します。総称関数とプロトコルを制するものが、 とはいえ、 あと、 それにしても、 2022年8月18日発売実践例
require 'cmath'
include cmath
from cmath import *
→Playground
→OS XやiOSだけでなく、
public protocol ArithmeticType:
AbsoluteValuable, Equatable, カ
Comparable, Hashable {
// Initializers (predefined)
init(_: Int)
//// [中略]
init(_: Double)
init(_: Float)
init(_: Self)
// CGFloat if !os(Linux)
#if !os(Linux)
init(_: CGFloat)
#endif
// Operators (predefined)
prefix func + (_: Self)->Self
prefix func - (_: Self)->Self
func + (_: Self, _: Self)->Self
func - (_: Self, _: Self)->Self
func * (_: Self, _: Self)->Self
func / (_: Self, _: Self)->Self
func += (inout _: Self, _: Self)
func -= (inout _: Self, _: Self)
func *= (inout _: Self, _: Self)
func /= (inout _: Self, _: Self)
}
extension Int : ArithmeticType {}
ArithmeticType
に準拠public extension ArithmeticType {
/// self * 1.0i
public var i:Complex<Self> カ
{ return Complex(Self(0), self) }
/// abs(z)
public static func abs(x:Self)->Self { カ
return Swift.abs(x) }
/// failable initializer to conver the type
/// - parameter x: `U:ArithmeticType` カ
where U might not be T
/// - returns: Self(x)
public init?<U:ArithmeticType>(_ x:U) {
switch x {
case let s as Self: self.init(s)
case let d as Double: self.init(d)
case let f as Float: self.init(f)
case let i as Int: self.init(i)
default:
return nil
}
}
}
42.
とかと書くと(0+42.
になるのは、Array
だけではなくSequence
に準拠する型であればすべて.map
や.reduce
が使えるようになったのですが、Complex
を見てみましょうpublic struct Complex<T:ArithmeticType> : Equatable, CustomStringConvertible, Hashable {
public typealias Element = T
public var (re, im): (T, T)
//// [中略]
}
ArithmeticType
に準拠したTによる総称型です。1+1.
はComplex<Int>
、1.
はComplex<Double>
になるわけです。.abs
や偏角.arg
がないことに気づかれるかもしれません。これらは複素数自体がComplex<Int>
、ArithmeticType
の要件をすべて満たす上位互換Protocolを1つ追加しますpublic protocol RealType : ArithmeticType, FloatingPointType {
static var EPSILON:Self { get } // for =̃
}
extension RealType {
/// Default type to store RealType
public typealias Real = Double
//typealias PKG = Foundation
// math functions - needs extension for each struct
#if os(Linux)
public static func cos(x:Self)-> Self { return Self(Glibc.cos(Real(x)!))! }
//// [中略]
#else
public static func cos(x:Self)-> Self { return Self(Foundation.cos(Real(x)!))! }
//// [中略]
#endif
}
Glibc
、Foundation
からごっそり持ってくるわけです。ちなみにProtocol Extensionと型のExtensionで同名の識別子がある場合、Float
に関してはいったんDouble
に変換してFloat
に戻すのではなくFloat
のままで計算するためにcosf
など末尾にf
がついた関数を使いたかったので、extension Float
で上書きしています。extension Complex where T:RealType {
public init(abs:T, arg:T) {
self.re = abs * T.cos(arg)
self.im = abs * T.sin(arg)
}
/// absolute value of self in T:RealType
public var abs:T {
get { return T.hypot(re, im) }
set(r){ let f = r / abs; re *= f; im *= f }
}
/// argument of self in T:RealType
public var arg:T {
get { return T.atan2(im, re) }
set(t){ let m = abs; re = m * T.cos(t); im = m * T.sin(t) }
}
/// projection of self in Complex
public var proj:Complex {
if re.isFinite && im.isFinite {
return self
} else {
return Complex(
T(1)/T(0), im.isSignMinus ? -T(0) : T(0)
)
}
}
}
RealType
に準拠してある場合にのみ、.abs
や.arg
を追加するということがSwift 2で可能になったのです。/
はこんな感じ。public func / <T>(lhs:Complex<T>,
rhs:Complex<T>) -> Complex<T> {
return (lhs * rhs.conj) / rhs.norm
}
.conj
とノルム.norm
は、Complex<T>
であれば必ず持っているので、public func exp<T:RealType>(z:Complex<T>)
-> Complex<T> {
let r = T.exp(z.re)
let a = z.im
return Complex(r * T.cos(a), r *
T.sin(a))
}
e ** (x+y.
そのままですね。ただしcos
でなくてT.
と書いています。RealType
のProtocolでpublic static func cos(x:Self)->Self
となっているものを指定しています。なぜメソッドではなく型関数(static method)かというと、.cos
まで補完されてしまってちょっと驚きなのです。Rubyistsなどからするとちょっと残念かもしれませんが。Todo
というエラーメッセージでXcodeが真っ赤になったりするのですが、importFoundation
できるのに、#if
を書かずに済ませたいのに……。本誌最新号をチェック!
Software Design 2022年9月号
B5判/
定価1,342円
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
OSSソースコードリーディングのススメ
企業のシステムを支えるOSとエコシステムの全貌
今さら聞けないSSH
MySQLで学ぶ文字コード
新生