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


143

これは何よりも面白いと思います。私はそれを修正しましたが、原因について疑問に思っています。エラーは次のとおり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))"かどうか確認しましたか?
効率性2015

5
@efischencyで推奨されている文字列補間は、通常、を使用した手動連結よりも優れたオプションです+
mattt

5
確かに、それはポイントではありません。それが「提案された」方法であるかどうかは気にしません。コンパイラーがそれを制限する理由を知りたいだけです。私はうまくいく解決策を持っています、それはエラーを修正することではなく、エラーを理解することです。
ケンドリックテイラー

2
私が聞いたところによると、Swiftコンパイラーはまだ非常に進行中の作業です。チームはこれに関するバグレポートを高く評価するかもしれません。
molbdnilo 2015

これを6.3.1でコンパイルしても問題はありませんでした。過去にも同じようなばかげたメッセージがありました。Swiftがアルファ状態を終了するまで待つ必要があります。
qwerty_so 2015

回答:


183

私はコンパイラの専門家ではありません-この答えが「意味のある方法であなたの考え方を変える」かどうかはわかりませんが、問題に対する私の理解は次のとおりです。

型推論と関係があります。+演算子を使用するたびに、Swiftはすべての可能なオーバーロードを検索し、使用して+いるバージョンを推測する+必要があります。+オペレーターのオーバーロードは30弱と数えました。それは多くの可能性であり、4つまたは5つの+演算をつなぎ合わせて、コンパイラーにすべての引数を推測するように依頼すると、一見した以上に多くのことが求められます。

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

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

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

式が特定のレベルの複雑さに達したときにコンパイラーを終了させることは、意図的なものです。代わりの方法は、コンパイラーに試行錯誤させ、それが可能かどうかを確認することですが、それは危険です。だから私の理解は、コンパイラが超えない式の複雑さには静的なしきい値があるということです。

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

開発者フォーラムでは、Chris Lattnerがこれらのエラーをレーダーレポートとして提出するように依頼しています。

これは、こことDevフォーラムでいくつかの投稿を読んだ後の理解ですが、コンパイラについての私の理解は初心者であり、これらのタスクを処理する方法についてより深い知識を持っている人が私に拡張してくれることを期待していますここに書いています。


私はその影響について何かを考えましたが、それでもなお有用な答えでした。回答ありがとうございます。+演算子の数を手で数えましたか、それとも私が知らない洗練された方法がありますか?
ケンドリックテイラー

私はSwiftDoc.orgでそれをのぞいて、手で数えただけです。これは私が話しているページです:swiftdoc.org/operator/pls
アーロンラスムッセン

28
彼らがそれをそれと呼ぶかどうかに関係なく、これはバグです。他の言語のコンパイラは、投稿されたものと同様のコードで問題はありません。エンドユーザーに修正を提案するのはばかげています。
ジョン

7
型推論?このばかげた状況で、Swiftのような強く型付けされた言語(String + Intを連結することさえできないので、Intをキャストする必要がない)の意味は何ですか?もう一度、Swiftはそもそも誰も持っていなかった問題を解決しようとします。
Azurlake 2017年

10
@Johnバグではありません。私に尋ねれば、言語の設計が悪いだけです。Swiftは、違うものにしようとするだけです。
T.レックス

31

これは、受け入れられた回答とほとんど同じですが、追加の対話(ロブネイピアと彼の他の回答、およびSlackからのマット、オリバー、デビッド)とリンクが追加されています。

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

+ 非常に過負荷になっている(Appleが一部のケースでこれを修正したようです)

+オペレータは、あなたが4つの文字列を連結しているので、もしそれが27の異なる機能を持っている今のところ大きく、過負荷になっているあなたは3つの持っている。すなわち+、コンパイラはしている事業者チェックそれでは27 ^ 3倍という、27事業者の間で、それぞれの時間を。しかし、それだけではありません。

あり、チェックかどうかを確認するためにlhsrhs+、彼らはそれがコアに通じ呼び出しをされている場合の機能は両方とも有効であるappendと呼ばれるが。そこでは、いくらか集中的なチェックが行われる可能性があることがわかります。文字列が不連続に格納されている場合、これは、処理している文字列が実際にNSStringにブリッジされている場合に発生する可能性があります。その後、Swiftはすべてのバイト配列バッファーを単一の連続したバッファーに再アセンブルする必要があり、途中で新しいバッファーを作成する必要があります。そして、最終的に、連結しようとしている文字列を含む1つのバッファーを取得します。

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

Swift 3 では、いくつかの改善が行われています。詳細については、コンパイラを遅くせずに複数の配列をマージする方法をお読みください。それにもかかわらず、+演算子は依然として過負荷であり、長い文字列には文字列補間を使用することをお勧めします


オプションの使用(進行中の問題-解決策あり)

この非常に単純なプロジェクトでは:

import UIKit

class ViewController: UIViewController {

    let p = Person()
    let p2 = Person2()

    func concatenatedOptionals() -> String {
        return (p2.firstName ?? "") + "" + (p2.lastName ?? "") + (p2.status ?? "")
    }

    func interpolationOptionals() -> String {
        return "\(p2.firstName ?? "") \(p2.lastName ?? "")\(p2.status ?? "")"
    }

    func concatenatedNonOptionals() -> String {
        return (p.firstName) + "" + (p.lastName) + (p.status)
    }

    func interpolatedNonOptionals() -> String {
        return "\(p.firstName) \(p.lastName)\(p.status)"
    }
}


struct Person {
    var firstName = "Swift"
    var lastName = "Honey"
    var status = "Married"
}

struct Person2 {
    var firstName: String? = "Swift"
    var lastName: String? = "Honey"
    var status: String? = "Married"
}

関数のコンパイル時間は次のとおりです。

21664.28ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:16:10 instance method concatenatedOptionals()
2.31ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:20:10 instance method interpolationOptionals()
0.96ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:24:10 instance method concatenatedNonOptionals()
0.82ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:28:10 instance method interpolatedNonOptionals()

のコンパイル期間が非常に高いことに注目してくださいconcatenatedOptionals

これは次のようにして解決できます:

let emptyString: String = ""
func concatenatedOptionals() -> String {
    return (p2.firstName ?? emptyString) + emptyString + (p2.lastName ?? emptyString) + (p2.status ?? emptyString)
}

コンパイルする 88ms

この問題の根本的な原因は、コンパイラがを""として識別しないことStringです。それは実際にExpressibleByStringLiteral

コンパイラが表示されます??とする必要があります。このプロトコルに準拠しているすべてのタイプをループそれはへのデフォルトの可能なタイプを見つけるまで、StringemptyStringwhichがにハードコードされているを使用することによりString、コンパイラーはすべての適合タイプをループする必要がなくなります。ExpressibleByStringLiteral

コンパイル時間を記録する方法については、ここまたはここを参照しください


SOでのロブネイピアの他の同様の回答:

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

コンパイラを遅くせずに複数の配列をマージするにはどうすればよいですか?

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


19

これはあなたが何を言ってもかなりばかげています!:)

ここに画像の説明を入力してください

しかし、これは簡単に渡されます

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コンパイラがコードからデータ型を推測しようとすることと関係があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.