2015-01-07 12 views
0

Недавно я экспериментировал с операторами присваивания в Swift и столкнулся с проблемой, от которой я не мог найти решение.Можно ли инициализировать переменную с помощью специального оператора присваивания?

Рассмотрит следующую архитектуру:

struct Box<T> { 

    let value: T 

} 

struct MyStruct { 

    let myProperty: String 

    init(boxedProperty: Box<String>) { 
     self.myProperty = boxedProperty.value 
    } 

} 

Кажется довольно простым, но представьте себе, что MyStruct принимает 20 коробочного свойства - каждый из них требует «распаковки» путем доступа к value свойству Box экземпляра. Теперь представьте себе 20 версий MyStruct.

Это привело бы к сотням .value линий, которых довольно много. Вместо этого, чтобы уменьшить беспорядок кода, я хотел бы использовать пользовательский оператор присваивания, который неявно «распаковывает» код Box и присваивает его значение переменной.

Рассмотрим простой пример (без поддержки диспозитивности и т.д.):

infix operator <|= { 
    associativity right 
    precedence 90 
    assignment 
} 

func <|= <T>(inout lhs: T, rhs: Box<T>) { 
    lhs = rhs.value 
} 

В идеале я хотел бы использовать <|= оператора сразу же, как это:

init(boxedProperty: Box<String>) { 
    self.myProperty <|= boxedProperty 
} 

Но, к сожалению (и вполне предсказуемо?), это не работает, так как переменная myProperty не инициализируется перед использованием в функции:

error: variable 'self.myProperty' passed by reference before being initialized 
    self.myProperty <|= boxedProperty 
        ^

Могу ли я обеспечить компилятор, чтобы моя операторская функция assignment всегда инициализировала переменную (a.k.a. его операнд lhs)? Кроме того, если у вас есть идея другого подхода, не стесняйтесь комментировать.


Примечание: Фактический случай является более сложным и требует более «распаковка», чем просто доступ к value propterty в виде Box структуры.

+0

Ваша проблема не в использовании массивов ящиков и свойств? init (boxedProperties: [Box ]) {self.myProperties = boxedProperties.map {% 0.value}} При этом 20 или 200 свойств, неважно. – GoZoner

+0

@GoZoner No. Это на самом деле 'init (anEnumValue: MyEnum)' в реальном случае. Значение enum содержит разные связанные значения, которые должны быть двойными - «unboxed». – akashivskyy

+0

В отличие от других операторов присваивания Swift (кроме фактического назначения с '=') вам не нужен параметр 'inout', потому что, как вы знаете, вы никогда не ссылаетесь на значение. Но на поверхности вы торгуете: 'self.myProperty <| = boxedProperty' для' self.myProperty = boxedProperty.value'. Я не понимаю преимущества для оператора в этом случае, но я согласен признать, что не вижу вашей реальной проблемы. – GoZoner

ответ

3

Вы не можете сделать это так, как вы описываете, поскольку операторы присваивания используют оба операнда в выражении, которые вы не можете сделать с неинициализированной переменной. Как насчет использования префиксного оператора, который возвращает значение в штучной упаковке?

prefix operator <| { } 

prefix func <|<T>(rhs: Box<T>) -> T { 
    return rhs.value 
} 

// ... 

init(boxedProperty: Box<String>) { 
    self.myProperty = <|boxedProperty 
} 
+0

Я тоже думал об этом, используя инфиксный оператор просто кажется более естественным (поскольку я на самом деле разрабатываю фрейм с открытым исходным кодом). – akashivskyy

+0

В этом случае ваш единственный способ - сначала указать значения по умолчанию для всех свойств, а затем использовать оператор infix для распаковки/процесса. –

+0

Или сделайте их необязательными. Неприятный материал в обоих случаях. – akashivskyy

1

У вас нет причин использовать синтаксис для достижения своей цели. Просто определите функцию, которая инкапсулирует ваш оператор «switch» несколькими парами ». Убедитесь в том, что эта функция связана с let так, что она доступна в init() Как таковой:

struct Box<T> { 
    let value: T 
    init (t:T) { 
    self.value = t 
    } 
}  

struct MyStruct { 
    let myProp: String 
    let myUnboxer : Box<String> -> String = { (b:Box<String>) in 
    /* switch, 5-10 lines */ 
    return b.value 
    } 

    init (bp:Box<String>) { 
    self.myProp = myUnboxer(bp) 
    } 
} 

В действии:

15> var ms = MyStruct(bp:Box(t:"abc")) 
ms: MyStruct = { 
    myProp = "abc" 
    myUnboxer = 
} 
16> ms.myProp 
$R0: String = "abc" 

«затраты» это является «дополнительной» переменной экземпляра для myUnboxer, но это может быстро стать преимуществом, когда детали unboxing становятся специфичными для конкретной структуры - в этот момент вы также инициализируете MyStruct с помощью unboxer.

Для меня, на языке с закрытием и первоклассными функциями, обычно бывает ошибкой придумывать синтаксис - если только он не активно определяет подъязык.Наличие сложения с особыми правилами оценки (иначе говоря, «синтаксис») смущает.

+0

Он также имеет операторы как первоклассные функции. ;) Я думаю, что оператор 'assign' должен иметь возможность« инициализировать »переменную. – akashivskyy

0

Я использовал перечисления для хранения свойств объектов, например:

enum TextFieldProps { 
    case StringValue(String) 
    case Editable(Bool) 
} 

Я тогда иметь расширение к NSTextField, который включает в себя функцию, называемой assignProps, которая принимает массив TextFieldProps:

func assignProps(tfp: [TextFieldProps]) { 
    tfp.reduce(self) { prop in 
     switch prop { 
      case .StringValue(let value): 
       self.stringValue = value 
      case .Editable(let value): 
       self.editable = value 
     } 
     return self 
    } 
} 

Это позволяет мне, когда мне нужно установить или изменить свойства NSTextField, просто передать массив из TextFieldProps в assign, который обновляет все за один проход, используя reduce ,

Я понимаю, что вы пытаетесь использовать что-то подобное, только с общим Box, который содержит значение внутри него. Возможно, есть способ изменить технику выше, чтобы удовлетворить ваши цели. Трудно дать вам четкий ответ, не зная больше о структуре перечислений и значениях, с которыми вы работаете.

Я могу сказать, что если вы пытаетесь установить значение 20 свойств, где-то будет 20 вызовов назначения, независимо от того, используете ли вы пользовательский оператор или нет.

Когда я использую объект типа Box (который я иногда делаю), у меня есть несколько различных пользовательских функций для них. Один из них так и называется o, для «открытого»:

func o(b: Box<T>) -> T { 
    return b.value 
} 

// Now you can use it like this: 

init(b: Box<String>) { 
    self.property = o(b) 
} 

Я знаю, что это не прямо ответить на вопрос, который вы просили об операторах пользовательских назначений, но если сократить до метки инициализации для b и использовать функция называется o, вы получили все до 4 символов, o(b). Это примерно так же коротко и эффективно, как вы можете это сделать, и o(b), для меня, интуитивно выглядит как стенография для opened box, поэтому код не слишком запутан.

Возможно, вы захотите ограничить область действия o, чтобы более поздние пользователи вашей инфраструктуры могли использовать o в качестве имени переменной, если захотят. Вы действительно можете создать o внутри MyStruct «s init функции, как это:

struct MyStruct { 
    let myProperty: String 

    init(b: Box<String>) { 
     let o: Box<String> -> String = { $0.value } 
     self.myProperty = o(b) 
    } 
} 

o теперь замыкание, определенное внутри функций init и ограничен только для этой функции. Вы все равно можете использовать o как имя переменной в любом другом месте кода, который вы хотите, без каких-либо конфликтов.

0

Я думаю, что это разрешимо с использованием значений свойств по умолчанию. Например. что-то вроде:

struct Box<T> { 

    private let value: T? = nil 

    init(value: T) { 
     self.value = value 
    } 

} 

struct MyStruct { 

    let myProperty: String = "" 

    init(boxedProperty: Box<String>) { 
     self.myProperty <|= boxedProperty 
    } 

} 

infix operator <|= { associativity right precedence 90 assignment } 

func <|= <T>(inout lhs: T, rhs: Box<T>) { 
    lhs = rhs.value! 
} 

var box = Box<String>(value: "foo") 

var myStruct = MyStruct(boxedProperty: box) 
+0

Да, но свойства не должны быть дополнительными. – akashivskyy

Смежные вопросы