From AP to Z
本題に入る前に臨時ニュースを。本連載第18回でも紹介したAPFSですが、
それよりずっと地味ながらも見落とせないのは、


すでにインストーラまでZFSに対応しているFreeBSD以外のOSのZFSブート環境構築はまだまだ面倒なのですが、
オープンソースといえば、
型の集まりもまた型
ニュースはこれくらいにして本題に入りましょう。前回に引き続き今回も型の話です。今回は配列
Swiftの型は静的。当然配列や辞書も静的な型を持つのですが、
まずは、
% irb irb(main):001:0> [nil,false,0,"",[],{}].each{¦e¦ p e.class} NilClass FalseClass Fixnum String Array Hash => [nil, false, 0, "", [], {}]
% node > [null,false,0,"",[],{}].forEach(function(e){console.log(typeof e)}) object boolean number string object object undefined >
見てのとおり、
Swiftはどうでしょう?
図3のように文句を言われてしまいました。もう少し見ていきましょう。
% swift Welcome to Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1). Type :help for assistance. 1> [nil,false,0,"",[],[:]] error: repl.swift:1:1: error: type of expression is ambiguous without more context [nil,false,0,"",[],[:]]
1> [false,true] $R0: [Bool] = 2 values { [0] = false [1] = true } 2> [0,1] $R1: [Int] = 2 values { [0] = 0 [1] = 1 } 3> ["","one"] $R2: [String] = 2 values { [0] = "" [1] = "one" }
そう。Swiftでは配列の要素の型はすべて同じで、[Element]
という型になるのです。
なんて型苦しい? ところでこのRubyコードを見てくれ。図4をどう思う?
% irb irb(main):001:0> ["answer",42,42.195,[true],false].each{¦e¦ p e + e} "answeranswer" 84 84.39 [true, true] NoMethodError: undefined method `+' for false:FalseClass from (irb):2:in `block in irb_binding' from (irb):2:in `each' from (irb):2 from /usr/bin/irb:12:in `<main>'
すごく……大きなバグを誘発しそうな気がしませんか?
こういう場合、
% irb irb(main):001:0> ["answer",42,42.195,[true],false].each do ¦e¦ irb(main):002:1* p e + e if e.respond_to?(:+) irb(main):003:1> end "answeranswer" 84 84.39 [true, true] => ["answer", 42, 42.195, [true], false]
しかしそれはその分コードの量が増えるということでもあり、
1> [41,42].map{$0+$0} $R0: [Int] = 2 values { [0] = 82 [1] = 84 } 2> [41.0,42.195].map{$0+$0} $R1: [Double] = 2 values { [0] = 82 [1] = 84.39 } 3> ["answer","universe"].map{$0+$0} $R2: [String] = 2 values { [0] = "answeranswer" [1] = "universeuniverse" } 4> [false,true].map{$0+$0} error: repl.swift:4:20: error: binary operator'+' cannot be applied to two 'Bool' operands [false,true].map {$0+$0}
型がないなら作ればいいのに
しかしそれではJSONのような入れ子があるデータはどうしたらよいのでしょう? 答えは単純。そのような型を作ってしまえばよいのです。たとえば[Int]
を入れ子にしたいとしたら、
enum NestedArray<Element> {
case V(Element)
case A([NestedArray])
}
という型を宣言しておいてから、
var na = NestedArray.A([
NestedArray.V(0),
NestedArray.V(1),
NestedArray.A([
NestedArray.V(2),
NestedArray.V(3)
])
])
と書けばおk……って何これ長い!
もちろん、
var na:NestedArray = .A([
.V(0), .V(1), .A([.V(2), .V(3)])
])
あらかじめ型を宣言しておけば、
ここから本格的な型作りが始まります。
まずは手始めに中の値にアクセスできるようにしてみましょう。
extension NestedArray {
var value:Element? {
switch self {
case let .V(v):
return v
default:
return nil
}
}
var array:[NestedArray]? {
switch self {
case let .A(a):
return a
default:
return nil
}
}
}
これを実行すると次のようになります。
na.array?[0].value // 0 na.array?[1].value // 1 na.array?[2].value // nil na.array?[2].array?[0].value // 2
どうやらうまくいったようです。
でも、.array
で中身を取り出さなければならないなんておかしいですよね?
添字で直接中身を読み書きできない?
extension NestedArray {
subscript(i:Int)->NestedArray? {
get {
switch self {
case let .A(a):
return a[i]
default:
return nil
}
}
set {
switch self {
case var .A(a):
a[i] = newValue!
self = .A(a)
default:
return
}
}
}
}
これも実行すると、
na[0]?.value // 0 na[1]?.value // 1 na[2]?.value // nil na[2]?[0]?.value // 2 na[0] = .V(42) na[0]?.value // 42 na[1]?.value // 1
直接.map()
できない? そのままfor
ループにかけられない? いっそきちんとしたSequence
になるまで発展させられない?
―あとは読者の宿題ということで。
このような感じで必要な機能をextension
に追加していくのが、
次号予告
ところで今回作ったNestedArray
は、Int
だけではなく、Double
はString
でもいけます。
var nad:NestedArray = .A([
.V(0.0), .V(1.1), .A([.V(2.2), .V(3.3)])
])
var nas:NestedArray = .A([
.V("zero"), .V("one"), .A([.V("two"), .V("three")])
])
とくにそう宣言していないのになぜそれができたのでしょうか?
ところでSwift
にはAny
というなんでも入る型がすでに存在しています。
var a:Any = [0,1,[2,3]]
結論から先に言うと、Any
は避けるべきです。少なくとも、
次回はこれらの疑問に答えつつ、
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/
定価1,342円
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」 「使いたい」 「発信したい」 をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画] Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編] リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回] 文字コードのハマりどころTips集 - 短期連載
新生「Ansible」 徹底解説
[4] Playbookの実行環境 (基礎編)