it-swarm.com.ru

Swift Ошибка компилятора: «Слишком сложное выражение» при конкатенации строк

Я нахожу это забавным больше всего на свете. Я исправил это, но я задаюсь вопросом о причине. Вот ошибка: DataManager.Swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions. Почему это жалуется? Кажется, это одно из самых простых возможных выражений.

Компилятор указывает на раздел columns + ");";

func tableName() -> String { return("users"); } 

func createTableStatement(schema: [String]) -> String {

    var schema = schema;

    schema.append("id string");
    schema.append("created integer");
    schema.append("updated integer");
    schema.append("model blob");

    var columns: String = ",".join(schema);

    var statement = "create table if not exists " + self.tableName() + "(" + columns + ");";

    return(statement);
}

исправление:

var statement = "create table if not exists " + self.tableName();
statement += "(" + columns + ");";

это также работает (через @efischency), но мне это не очень нравится, потому что я думаю, что ( теряется:

var statement = "create table if not exists \(self.tableName()) (\(columns))"

138
Kendrick Taylor

Я не эксперт по компиляторам - я не знаю, изменит ли этот ответ "значимое изменение вашего мышления", но мое понимание проблемы таково:

Это связано с выводом типа. Каждый раз, когда вы используете оператор +, Swift должен выполнять поиск всех возможных перегрузок для + и определять, какую версию + вы используете. Я насчитал чуть менее 30 перегрузок для оператора +. Это много возможностей, и когда вы объединяете 4 или 5 операций + вместе и просите компилятор вывести все аргументы, вы запрашиваете гораздо больше, чем может показаться на первый взгляд.

Этот вывод может усложниться - например, если вы добавите UInt8 и Int, используя +, на выходе будет Int, но есть некоторая работа по оценке правил смешивания типов с операторами.

И когда вы используете литералы, такие как литералы String в вашем примере, компилятор выполняет преобразование литерала String в String, а затем выводит аргументы и возвращаемые типы для оператора + и т.д.

Если выражение достаточно сложное, т. Е. Требуется, чтобы компилятор сделал слишком много выводов об аргументах и ​​операторах, - оно завершает работу и сообщает, что оно завершается.

Завершение работы компилятора, когда выражение достигает определенного уровня сложности, является намеренным. Альтернатива состоит в том, чтобы позволить компилятору попробовать и сделать это, и посмотреть, может ли это, но это рискованно - компилятор может продолжать пытаться вечно, зависать или просто падать. Поэтому я понимаю, что для сложности выражения существует статический порог, который компилятор не выйдет за пределы.

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

На форумах Dev Крис Латтнер попросил людей регистрировать эти ошибки как отчеты радаров, потому что они активно работают над их исправлением.

Вот как я понимаю это после прочтения ряда постов здесь и на форуме разработчиков об этом, но мое понимание компиляторов наивно, и я надеюсь, что кто-то с более глубоким знанием того, как они справляются с этими задачами, расширит то, что я написал здесь.

177
Aaron Rasmussen

Это почти то же самое, что принятый ответ, но с некоторым дополнительным диалогом (я имел с Робом Нейпером, его другими ответами и другим другом от встречи Cocoahead) и связями.

Смотрите комментарии в этом обсуждении. Суть этого:

+ operator сильно перегружен, на данный момент он имеет 27 различных функций, поэтому если вы объединяете 4 строки, т.е. у вас есть 3 + оператора, то компилятор должен проверить между 27 операторами каждый раз, так что это 27 ^ 3 раза. Но это не так.

Также есть проверка , чтобы проверить, действительны ли lhs и rhs функций +, если они вызваны через ядро append называется. Там вы можете увидеть несколько интенсивных проверок , которые могут произойти. Если строка хранится несмежно, что, как представляется, имеет место, если строка, с которой вы работаете, фактически соединена с NSString. Затем Swift необходимо заново собрать все буферы байтового массива в единый непрерывный буфер, который требует создания новых буферов. и затем вы в конечном итоге получаете один буфер, содержащий строку, которую вы пытаетесь объединить вместе.

В двух словах, есть 3 кластера проверок компилятора, которые замедляют вас, т. Е. каждое подвыражение должно быть пересмотрено в свете всего, что оно может возврат . В результате конкатенация строк с интерполяцией, т.е. с использованием " My fullName is \(firstName) \(LastName)", намного лучше, чем "My firstName is" + firstName + LastName, поскольку интерполяция не имеет перегрузки.

Swift 3 внес некоторые улучшения. Для получения дополнительной информации читайте Как объединить несколько массивов без замедления компилятора?


Другие подобные ответы Роба Нейпира на SO:

Почему сложение строк занимает так много времени?

Как объединить несколько массивов без замедления компилятора?

Swift Array содержит функцию, увеличивающую время сборки

30
Honey

Это довольно нелепо, независимо от того, что вы говорите! :)

enter image description here

Но это легко проходит

return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"
16
karim

У меня была похожая проблема:

expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions

В Xcode 9.3 строка выглядит так:

let media = entities.filter { (entity) -> Bool in

После изменения в что-то вроде этого:

let media = entities.filter { (entity: Entity) -> Bool in

все получилось.

Возможно, это как-то связано с компилятором Swift, который пытается вывести тип данных из кода.

2
vedrano