web-dev-qa-db-fra.com

Swift Erreur du compilateur: "Expression trop complexe" dans une concaténation de chaîne.

Je trouve cela amusant plus que tout. Je l'ai corrigé, mais je m'interroge sur la cause. Voici l'erreur: DataManager.Swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions. Pourquoi se plaint-il? Cela semble être l'une des expressions les plus simples possibles.

Le compilateur pointe vers la section 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);
}

le correctif est:

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

cela fonctionne aussi (via @efischency) mais je ne l'aime pas autant parce que je pense que le ( est perdu:

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

138
Kendrick Taylor

Je ne suis pas un expert en compilateurs - je ne sais pas si cette réponse va "changer votre façon de penser de manière significative", mais ma compréhension du problème est la suivante:

Cela a à voir avec l'inférence de type. Chaque fois que vous utilisez l'opérateur +, Swift doit rechercher toutes les surcharges possibles pour + et en déduire quelle version de + vous utilisez. J'ai compté un peu moins de 30 surcharges pour l'opérateur +. C’est beaucoup de possibilités, et lorsque vous enchaînez 4 ou 5 + opérations et que vous demandez au compilateur de déduire tous les arguments, vous en demandez bien plus qu’il n’apparaît à première vue.

Cette inférence peut devenir compliquée - par exemple, si vous ajoutez un UInt8 et un Int à l'aide de +, le résultat sera un Int, mais certains travaux seront exécutés dans évaluer les règles de mélange de types avec des opérateurs.

Et lorsque vous utilisez des littéraux, comme les littéraux String dans votre exemple, le compilateur effectue le travail de conversion du littéral String en un String, puis effectue le travail de déduction de l'argument et renvoyer des types pour l'opérateur +, etc.

Si une expression est suffisamment complexe - c’est-à-dire qu’elle nécessite que le compilateur fasse trop d’inférences sur les arguments et les opérateurs - elle se ferme et vous dit qu’elle s’est arrêtée.

Le fait de quitter le compilateur une fois que l'expression atteint un certain niveau de complexité est intentionnel. L'alternative est de laisser le compilateur essayer de le faire et voir si c'est possible, mais c'est risqué - le compilateur pourrait continuer à essayer pour toujours, à s'enliser ou simplement à planter. Donc, ma compréhension est qu'il existe un seuil statique pour la complexité d'une expression que le compilateur n'ira pas au-delà.

D'après ce que j'ai compris, l'équipe Swift travaille actuellement sur des optimisations du compilateur qui rendront ces erreurs moins courantes. Vous pouvez en apprendre un peu plus sur les forums de développeurs Apple en cliquant sur ce lien .

Sur les forums de développement, Chris Lattner a demandé aux utilisateurs de classer ces erreurs sous forme de rapports radar, car ils travaillent activement à les corriger.

C’est ce que je comprends après avoir lu un certain nombre d’articles ici et sur le forum Dev, mais ma compréhension des compilateurs est naïve et j’espère que quelqu'un mieux informé sur la façon dont ils gèrent ces tâches approfondira ce que je sais. ont écrit ici.

178
Aaron Rasmussen

C'est presque la même chose que la réponse acceptée mais avec un dialogue supplémentaire (j'ai eu avec Rob Napier, ses autres réponses et un autre ami d'une réunion Cocoahead) et des liens.

Voir les commentaires dans this discussion. L'essentiel est:

L'opérateur + est fortement surchargé. Il dispose désormais de 27 fonctions différentes. Par conséquent, si vous concaténez 4 chaînes, c’est-à-dire que vous avez 3 opérateurs _+_, le compilateur doit vérifiez entre 27 opérateurs à chaque fois, donc 27 ^ 3 fois. Mais ce n'est pas ça.

Il y a aussi un vérification pour voir si la lhs et rhs du _+_ Les fonctions __ sont valides si elles sont appelées jusqu’au noyau appelé append. Vous pouvez y voir qu'il existe un certain nombre de contrôles assez intensifs pouvant se produire. Si la chaîne est stockée de manière non contiguë, cela semble être le cas si la chaîne que vous traitez est réellement pontée vers NSString. Swift doit alors ré-assembler tous les tampons du tableau d'octets en un seul tampon contigu, ce qui nécessite la création de nouveaux tampons en cours de route. et puis vous obtenez finalement un tampon qui contient la chaîne que vous essayez de concaténer ensemble.

En résumé, il existe 3 groupes de vérifications du compilateur qui vous ralentiront, c'est-à-dire que chaque sous-expression doit être réexaminée à la lumière de tout ce qu'elle pourrait retourne . Par conséquent, concaténer des chaînes avec interpolation, c’est-à-dire que " My fullName is \(firstName) \(LastName)" est bien meilleur que _"My firstName is" + firstName + LastName_, car l’interpolation n’a pas de surcharge

Swift 3 a apporté quelques améliorations. Pour plus d'informations, lisez Comment fusionner plusieurs tableaux sans ralentir le compilateur?


Autres réponses similaires de Rob Napier sur SO:

Pourquoi l'ajout de chaîne prend si longtemps à construire?

Comment fusionner plusieurs tableaux sans ralentir le compilateur?

Swift Array contient la fonction rend les temps de construction longs

30
Honey

C'est assez ridicule, peu importe ce que vous dites! :)

enter image description here

Mais cela se passe facilement

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

J'ai eu le même problème:

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

Dans Xcode 9.3, la ligne est la suivante:

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

Après l'avoir changé en quelque chose comme ça:

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

tout a fonctionné.

Cela a probablement quelque chose à voir avec le compilateur Swift qui essaie d'inférer le type de données à partir du code.

2
vedrano