Как обозначить изменяемые параметры в замыканиях с помощью Swift › 2.2?

Возможно, это проблема бета-версии Xcode 8, однако до версии 2.2 ключевому слову var разрешено добавлять параметры в сигнатуры функций:

func (var stringName: String) { ... }

С тех пор это устарело, поскольку от него мало пользы по сравнению с inout.

func (stringName: inout String) { ... }

Я попытался сделать следующее в закрытии map, и хотя я не получил предупреждения об устаревании, как ожидалось, ошибка была скорее segmentation fault: 11

let demoString = ["hi", "there", "world"].map { (var word) -> String in 
    let firstChar = word.remove(at: word.startIndex)
}

Ошибка возникает, как только я пытаюсь изменить (предположительно изменяемую) переменную word.

Я пробовал другой вариант, например. используя inout

let demoString = ["hi", "there", "world"].map { (word: inout String) -> String in 
    let firstChar = word.remove(at: word.startIndex)
}

Но компилятор жалуется, что это ошибочно изменяет сигнатуру замыкания и не компилируется.

Очевидно, обходной путь — просто скопировать переменную в локальную внутри замыкания:

let demoString = ["hi", "there", "world"].map { (word) -> String in 
    let tempWord = word
    let firstChar = tempWord.remove(at: tempWord.startIndex)
}

Тем не менее, мне интересно узнать, является ли это ожидаемой функциональностью и есть ли способ изменить параметр, переданный в замыкание напрямую?


person Kyle G    schedule 11.08.2016    source источник
comment
На ваш вопрос ответили?   -  person Alexander    schedule 14.08.2016


Ответы (2)


Замыкания могут прекрасно видоизменять inout аргументы:

var str1 = "Pine"
var str2 = "Juniper"

let closure = { (s1: inout String, s2: inout String) -> Void in
    s1 = "Oak"
    s2 = "Chestnut"
}

closure(&str1, &str2)

print(str1, str2)

Проблема, с которой вы столкнулись, заключается в том, что Array.map не имеет сигнатуры метода, которая включает параметр inout.

Единственный способ обойти это, который я могу придумать, - это расширить Array и добавить метод map самостоятельно:

extension Array {
    func map<T>(_ closure: (inout T) -> Void) -> Array<T> {
        var arr = [T]()
        for i in 0..<self.count {
            var temp : T = self[i] as! T
            closure(&temp)
            arr.append(temp)
        }
        return arr
    }
}

var hi = "hi", there = "there", world = "world"
var demoStrings = [hi, there, world]
var result = demoStrings.map { (word: inout String) in 
    word.remove(at: word.startIndex)
}

print(result) // ["i", "here", "orld"]
person Lightbeard    schedule 12.08.2016

Согласно SE-0003 var параметры больше не существуют в Swift 3.

По сути, вы все равно не должны изменять параметры, заданные в map. Вместо этого: используйте неизменяющие функции или сделайте локальную изменяемую копию.

В этом случае нет причин использовать remove(_:) только для получения первого символа. Это потребует копирования памяти строки (исключая первый символ) исключительно для получения удаленного символа. Это довольно неуклюже.

В этом случае вы можете просто получить свойство first (из протокол Collection) на странице characters свойство String.

Попробуй это:

let demoStrings = ["hi", "there", "world"]

let firstLetters = demoStrings.map {(word: String) -> String in
    return word.characters.first
}

или для краткости:

let firstLetters = demoStrings.map{ $0.characters.first }
person Alexander    schedule 11.08.2016
comment
Спасибо за комментарий. На самом деле я не ищу способ переписать выражение. Это просто для демонстрационных целей. Я пытаюсь выяснить, могут ли быть изменены параметры, переданные в замыкания. FYI word.characters.first, если все, что вам нужно, это первая буква. - person Kyle G; 12.08.2016
comment
О, конечно, я понятия не имею, почему .first не пришло в голову, лол - person Alexander; 12.08.2016
comment
У вас могут быть замыкания с параметрами inout, но вы не можете использовать такое замыкание для map из-за объявления замыкания карты (которое не использует inout) - person Alexander; 12.08.2016