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

127

Я нахожу это забавным больше всего. Я исправил это, но мне интересно о причине. Вот ошибка: 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))"

  • 10
    Вы видели, работает ли это: var statement = "create table if not exists \(self.tableName()) (\(columns))" ?
  • 5
    Строковая интерполяция, рекомендованная @efischency, как правило, лучше, чем ручная конкатенация с + .
Показать ещё 8 комментариев
Теги:
xcode
functional-programming
compiler-errors

4 ответа

183
Лучший ответ

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

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

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

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

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

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

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

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

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

  • 0
    Я понял кое-что на этот счет, но это был полезный ответ, тем не менее. Спасибо за ответы. Вы подсчитали количество операторов + вручную или есть какой-то хитрый способ, о котором я не знаю?
  • 0
    Я просто посмотрел на SwiftDoc.org и посчитал их вручную. Это страница, о которой я говорю: swiftdoc.org/operator/pls
Показать ещё 10 комментариев
29

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

Смотрите комментарии в этой дискуссии. Суть этого заключается в:

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

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

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

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


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

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

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

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

15

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

Изображение 1884

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

return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"
2

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

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, который пытается вывести тип данных из кода.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню