🌀
[Swift] structでも再帰する
Swiftでは以下のような型をかけません。サイズが決まらないからです。
struct Node<T> {
let value: T
let previous: Node<T>?
}
しかしだからといってclass
にするのは気乗りしない場合があります。参照型は色々と扱いが厄介です。そこでstruct
でもどうにか再帰させてみましょう。
まずはproperty wrapperを用意します。これがミソで、再帰的enum
となっています。
@propertyWrapper
enum Recursive<T> {
indirect case value(T)
init(_ wrappedValue: T){
self = .value(wrappedValue)
}
var wrappedValue: T {
get {
switch self{
case let .value(value): return value
}
}
set {
self = .value(newValue)
}
}
}
これを使うとこんな風に書けます。
struct Node<T> {
let value: T
@Recursive private(set) var previous: Node<T>?
func getPreviousValue() -> T? {
previous?.value
}
}
property wrapperがvar
にしか使えない都合上、今回はアクセスをprivate(set)
にしておきました。少なくとも外からは変更できなくなっています。
property wrapperを使ったおかげで、表面上他のプロパティと同様にprevious
でNode<T>?
の値にアクセスすることができています。また、enum
を利用したおかげで値を変更しようとするとmutating
の利用を強制されます。class
でproperty wrapperを実装した場合はこうはいきません(let
にできるのが一番良いのですが)。
@propertyWrapper
class Recursive<T> {
private var value: T
init(_ wrappedValue: T){
self.value = wrappedValue
}
var wrappedValue: T {
get {
return self.value
}
set {
self.value = newValue
}
}
}
struct Node<T> {
let value: T
@Recursive private(set) var previous: Node<T>?
func getPreviousValue() -> T? {
previous = self //mutatingでなくても代入できてしまう。
return previous?.value
}
}
もちろんproperty wrapperとしては使わず、直接包んで用いるという手もあります。これならlet
で宣言できます。若干型名部分が長くなり、利用が面倒になるのがイマイチですが、こちらでも十分スッキリしているとは思います。
struct Node<T> {
let value: T
let previous: Recursive<Node<T>?>
func getPreviousValue() -> T? {
previous.wrappedValue?.value
}
}
Discussion