web-dev-qa-db-ja.com

Swiftコンパイラエラー:文字列連結で「複雑すぎる式」

これは何よりも面白いと思います。私はそれを修正しましたが、原因について疑問に思っています。エラーは次のとおりです:DataManager.Swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions。なぜ文句を言うのですか?可能な限り最も単純な表現の1つのようです。

コンパイラは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つの+操作をチェーンして、コンパイラーにすべての引数を推測するように依頼すると、一見しただけでなく、それ以上のことが求められます。

この推論は複雑になる可能性があります-たとえば、UInt8Int+を使用して追加すると、出力はIntになりますが、演算子と型を混合するためのルールを評価する作業がいくつかあります。

また、例のStringリテラルのようなリテラルを使用している場合、コンパイラはStringリテラルをStringに変換する作業を行い、その後+演算子の引数と戻り値の型を推測する作業を行います。

式が十分に複雑な場合、つまり、コンパイラが引数と演算子について多くの推論を行う必要がある場合、式は終了し、終了したことを通知します。

式が特定のレベルの複雑さに達すると、コンパイラを終了させるのは意図的です。別の方法は、コンパイラーにそれを試行させ、実行できるかどうかを確認することですが、それは危険です-コンパイラーは永遠に試行を続け、行き詰まり、または単にクラッシュする可能性があります。したがって、コンパイラの範囲を超えない式の複雑さには静的なしきい値があるというのが私の理解です。

私の理解では、Swiftチームは、これらのエラーをより一般的にしないコンパイラの最適化に取り組んでいます。 このリンクをクリックすると、Apple開発者フォーラムでそれについて少し学ぶことができます

開発者フォーラムでは、クリスラトナーは、これらのエラーをレーダーレポートとして提出するように人々に依頼しました。

こことDevフォーラムでいくつかの投稿を読んだ後、それは私が理解する方法ですが、コンパイラの私の理解は素朴であり、これらのタスクをどのように処理するかについてより深い知識を持つ誰かが私が拡張することを望んでいますここに書いています。

177
Aaron Rasmussen

これは受け入れられた回答とほぼ同じですが、いくつかの対話(Rob Napier、彼の他の回答、Cocoaheadミートアップからの別の友人との会話)が追加されました。

this ディスカッションのコメントを参照してください。その要点は次のとおりです。

+ operator は27個の異なる関数を持っているので、非常にオーバーロードされているため、4つの文字列を連結する場合、つまり3つの+演算子がある場合、コンパイラはcheck毎回27人のオペレーター間で、27 ^ 3回です。しかし、それは違います。

また、+関数のlhsrhsが両方とも有効であるかどうかを確認するために check があります。 appendを呼び出します。そこには、いくつかのやや集中的な checks が発生することがあります。文字列が非連続的に格納されている場合、扱っている文字列が実際にNSStringにブリッジされている場合に該当するようです。 Swiftはすべてのバイト配列バッファーを単一の連続したバッファーに再構成する必要があり、途中で新しいバッファーを作成する必要があります。そして、最終的に、連結しようとしている文字列を含む1つのバッファを取得します。

一言で言えば、3つのクラスターのコンパイラーチェックがあり、これにより速度が低下します。つまり、各サブ式は、すべての観点から再検討する必要がありますmightreturn。その結果、文字列を補間と連結する、つまり" My fullName is \(firstName) \(LastName)"を使用することは、補間にはオーバーロードがないため、"My firstName is" + firstName + LastNameよりもはるかに優れています

Swift 3someの改善を行いました。詳細については、 コンパイラの速度を落とさずに複数の配列をマージする方法 を参照してください。


SOに関するRob Napierによる他の同様の回答:

文字列の追加に時間がかかるのはなぜですか?

コンパイラの速度を落とさずに複数の配列をマージする方法?

Swift配列には、ビルド時間が長くなる関数が含まれています

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