より実践的なSequence
Sequence
が真の威力を発揮するのは、単一の値ではなく複数の値をまとめて扱うときにあるのは、.map()
、.filter()
、.reduce()
の例のとおりです。Swiftにはすでに標準でArray
を持っていますが、たとえば5,000兆個のデータをArray
に読み込んでというのはメモリが足りなさ過ぎるでしょう。そういう場合にもSequence
に準拠した型としてそれを実装すれば、少しずつストレージから読んで処理するのも
ここではLispやHaskellでよく用いられている片方向リストを実装してみましょう。こんな感じですか。
enum List<T> {
case Nil
indirect case Pair(head:T, tail:List<T>)
}
最低限のアクセサーと……、
extension List {
var car:T? {
get {
switch self {
case .Nil: return nil
case let .Pair(v, _): return v
}
}
set {
switch self {
case .Nil: return
case let .Pair(_, l):
self = List.Pair(head:newValue!, tail:l)
}
}
}
var cdr:List<T>? {
get {
switch self {
case .Nil: return nil
case let .Pair(_, l): return l
}
}
set {
switch self {
case .Nil: return
case let .Pair(v, _):
self = List.Pair(head:v, tail:newValue!)
}
}
}
}
………イニシャライザーを用意しておきます。
extension List {
init(fromArray: [T]) {
self = fromArray
.reversed()
.reduce(.Nil) {
List.Pair(head: $1, tail: $0)
}
}
init(_ values:T...) {
self.init(fromArray:values)
}
}
この状態で、
var l = List(0,1,2,3)
とすれば確かにl
は0->1->2->3->Nil
という具合に初期化されて、print(l)
してみると、Pair(head: 0,tail: __
なんて感じになっているのですが、これをSequence
にするにはどうしたらよいのでしょう? 先ほどの例のようにListIterator
を追加しても良いのですが、実はSwiftには次のような簡単な方法も用意されています。
extension List : Sequence {
public func makeIterator() -> AnyIterator<T> {
var list = self
return AnyIterator {
let v = list.car
if let l = list.cdr {
list = l
}
return v
}
}
}
こうしてからfor v in l { print(v) }
してみると、確かに値を頭から取れていることがわかります。
あとはこれを利用して、Array化したり……、
extension List {
var asArray:[T] {
return self.map{$0}
}
}
もっと見やすいよう文字列化したりするのは楽勝です。
extension List : CustomStringConvertible {
var description:String {
return "List("
+ self.map{"\($0)"}
.joined(separator:", ")
+ ")"
}
}
print(List(0,1,2,3)) // "List(0, 1, 2, 3)"
次回予告
今回はSwiftがSequence
プロトコルを通してどのように
次回はCollection
プロトコルを通してそれを見ていきます。
本誌最新号をチェック!
Software Design 2022年6月号
2022年5月18日発売
B5判/
定価1,342円
- 第1特集
シェルの基本大全
どんな環境でも迷わない・困らない知識 - 第2特集
後悔しないAWSデータベースの選び方
RDSとDynamoDB,使い分けのポイントを徹底解説 - 第3特集
不思議の国のSE用語
一人前のエンジニアになる方法 - 短期連載
新生「Ansible」 徹底解説
[1] Ansibleとは - 短期連載
HashiCorp Vaultではじめるシークレット管理
[最終回] KubernetesからVaultを利用しよう