Swift Compiler Error:“ การแสดงออกที่ซับซ้อนเกินไป” ในการต่อสตริง


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))"?
efischency

5
การแก้ไขสตริงโดยที่ @efischency แนะนำโดยทั่วไปแล้วจะเป็นตัวเลือกที่ดีกว่าการต่อข้อมูลด้วย+ตนเอง
mattt

5
แน่นอน แต่นั่นไม่ใช่ประเด็น ฉันไม่สนใจว่ามันเป็น "แนะนำ" หรือไม่ฉันแค่อยากรู้ว่าทำไมคอมไพเลอร์ทำให้เกิดการติดขัด ฉันมีโซลูชันที่ใช้งานได้ไม่ใช่เกี่ยวกับการแก้ไขข้อผิดพลาด แต่เกี่ยวกับการเข้าใจข้อผิดพลาด
Kendrick Taylor

2
จากสิ่งที่ฉันเคยได้ยินคอมไพเลอร์ Swift ยังคงเป็นงานที่อยู่ระหว่างดำเนินการ ทีมอาจซาบซึ้งรายงานข้อผิดพลาดนี้
molbdnilo

ฉันไม่มีปัญหาในการรวบรวมสิ่งนี้กับ 6.3.1 ฉันมีข้อความที่ไร้สาระที่คล้ายกันในอดีต เราต้องรอจนกว่า Swift จะออกจากสถานะอัลฟ่า
qwerty_so

คำตอบ:


183

ฉันไม่ใช่ผู้เชี่ยวชาญในการคอมไพล์เลอร์ - ฉันไม่รู้ว่าคำตอบนี้จะ "เปลี่ยนวิธีที่คุณคิดอย่างมีความหมาย" แต่ความเข้าใจของฉันเกี่ยวกับปัญหาคือ:

มันจะทำอย่างไรกับการอนุมานประเภท ทุกครั้งที่คุณใช้+โอเปอเรเตอร์ Swift จะต้องค้นหาโอเวอร์โหลดที่เป็นไปได้ทั้งหมดเพื่อหา+และอนุมานว่า+คุณใช้เวอร์ชั่นใด ฉันนับเกิน 30 เกินพิกัดสำหรับ+ผู้ควบคุมเครื่อง นั่นเป็นความเป็นไปได้มากมายและเมื่อคุณ+โยงการปฏิบัติการ4 หรือ 5 เข้าด้วยกันและขอให้คอมไพเลอร์สรุปข้อโต้แย้งทั้งหมดคุณจะถามมากกว่าที่มันจะปรากฏขึ้นในแวบแรก

การอนุมานนั้นอาจซับซ้อน - ตัวอย่างเช่นถ้าคุณเพิ่มUInt8และการIntใช้+เอาต์พุตจะเป็นIntแต่มีงานที่จะประเมินกฎสำหรับการผสมประเภทกับตัวดำเนินการ

และเมื่อคุณใช้ตัวอักษรเช่นStringตัวอักษรในตัวอย่างของคุณคอมไพเลอร์ทำงานเพื่อแปลงStringตัวอักษรให้เป็น a Stringจากนั้นทำการทำงานของการอนุมานอาร์กิวเมนต์และชนิดส่งคืนสำหรับ+โอเปอเรเตอร์ ฯลฯ

หากการแสดงออกมีความซับซ้อนเพียงพอ - เช่นนั้นมันต้องมีคอมไพเลอร์ที่จะทำการอนุมานเกี่ยวกับข้อโต้แย้งและผู้ประกอบการมากเกินไป - มันออกจากและบอกคุณว่ามันเลิก

การให้คอมไพเลอร์หยุดทำงานเมื่อนิพจน์มาถึงระดับความซับซ้อนที่กำหนดไว้ ทางเลือกอื่นคือให้คอมไพเลอร์ลองและทำมันและดูว่าทำได้หรือไม่ แต่มันมีความเสี่ยง - คอมไพเลอร์สามารถดำเนินการต่อไปได้ตลอดไปหรือล้มเหลว ดังนั้นความเข้าใจของฉันคือว่ามีเกณฑ์คงที่สำหรับความซับซ้อนของการแสดงออกที่รวบรวมจะไม่ไปเกิน

ความเข้าใจของฉันคือทีม Swift กำลังทำงานเกี่ยวกับการเพิ่มประสิทธิภาพของคอมไพเลอร์ที่จะทำให้ข้อผิดพลาดเหล่านี้น้อยลง คุณสามารถเรียนรู้เล็กน้อยเกี่ยวกับเรื่องนี้ได้ในฟอรัม Apple Developer โดยคลิกที่ลิงค์นี้

ในฟอรัม Dev, Chris Lattner ได้ขอให้ผู้คนยื่นข้อผิดพลาดเหล่านี้เป็นรายงานเรดาร์เพราะพวกเขากำลังทำงานอย่างแข็งขันในการแก้ไขปัญหา

นั่นคือวิธีที่ฉันเข้าใจหลังจากอ่านบทความจำนวนมากที่นี่และในฟอรัม Dev เกี่ยวกับเรื่องนี้ แต่ความเข้าใจในคอมไพเลอร์ของฉันนั้นไร้เดียงสาและฉันหวังว่าคนที่มีความรู้อย่างลึกซึ้งเกี่ยวกับวิธีจัดการงานเหล่านี้ ได้เขียนที่นี่


ฉันคิดอะไรบางอย่างกับเอฟเฟกต์นั้น แต่มันก็เป็นคำตอบที่เป็นประโยชน์ไม่น้อยเลย ขอบคุณสำหรับคำตอบ. คุณนับจำนวนตัวดำเนินการ + ด้วยมือหรือมีวิธีเรียบเนียนที่ฉันไม่ทราบหรือไม่
Kendrick Taylor

ฉันแค่มองดูมันใน SwiftDoc.org และนับด้วยมือ นี่คือหน้าเว็บที่ฉันกำลังพูดถึง: swiftdoc.org/operator/pls
Aaron Rasmussen

28
นี่เป็นข้อผิดพลาดไม่ว่าพวกเขาจะเรียกมันว่า คอมไพเลอร์ของภาษาอื่นไม่มีปัญหากับโค้ดคล้ายกับที่โพสต์ การแนะนำผู้ใช้ควรแก้ไขด้วยความงี่เง่า
จอห์น

7
การอนุมานประเภท? อะไรคือจุดของการมีภาษาที่พิมพ์ได้ดีอย่าง Swift (ซึ่งคุณไม่สามารถเชื่อมต่อ String + Int ได้โดยไม่ต้องใช้ Int) โดยไม่ต้องใช้ Int) อีกครั้ง Swift พยายามแก้ไขปัญหาที่ไม่มีใครมีตั้งแต่แรก
Azurlake

10
@ จอห์นไม่ใช่ข้อผิดพลาดเพียงแค่การออกแบบภาษาที่ไม่ดีถ้าคุณถามฉัน! สวิฟท์ไปไกลเกินกว่าจะพยายามทำสิ่งที่แตกต่างออกไป
ต. เร็กซ์

31

นี่เกือบจะเหมือนกับคำตอบที่ยอมรับ แต่มีบางบทสนทนาเพิ่มเติม (ฉันมีกับ Rob Napier คำตอบอื่น ๆ ของเขาและ Matt, Oliver, David จาก Slack) และลิงก์

ดูความคิดเห็นในเรื่องนี้การอภิปราย ส่วนสำคัญของมันคือ:

+ มีการใช้งานมากเกินไป (Apple ดูเหมือนจะแก้ไขปัญหานี้ในบางกรณี)

+ผู้ประกอบการมากเกินไปอย่างหนัก ณ ขณะนี้มันมี 27 ฟังก์ชั่นที่แตกต่างกันดังนั้นหากคุณกำลังเชื่อมโยง 4 สายคือคุณมี 3 +ผู้ประกอบการคอมไพเลอร์ที่มีการตรวจสอบระหว่างวันที่ 27 ผู้ประกอบการในแต่ละครั้งเพื่อให้เป็น 27 ^ 3 ครั้ง แต่นั่นไม่ใช่มัน

นอกจากนี้ยังมีการตรวจสอบเพื่อดูว่าlhsและrhsของ+ฟังก์ชั่นมีทั้งที่ถูกต้องหากพวกเขาเป็นมันเรียกร้องผ่านไปยังแกนappendเรียกว่า ที่นั่นคุณจะเห็นว่ามีการตรวจสอบอย่างเข้มข้นจำนวนหนึ่งที่สามารถเกิดขึ้นได้ หากสตริงถูกจัดเก็บแบบไม่ต่อเนื่องซึ่งดูเหมือนจะเป็นกรณีถ้าสตริงที่คุณจัดการนั้นถูกเชื่อมต่อกับ NSString จากนั้น Swift จะต้องรวบรวมบัฟเฟอร์อาร์เรย์ไบต์ทั้งหมดลงในบัฟเฟอร์ที่อยู่ติดกันอีกครั้งและต้องการสร้างบัฟเฟอร์ใหม่ไปพร้อมกัน และในที่สุดคุณก็จะได้รับหนึ่งบัฟเฟอร์ที่มีสตริงที่คุณพยายามเชื่อมต่อเข้าด้วยกัน

สรุปมี 3 กลุ่มของการตรวจสอบคอมไพเลอร์ที่จะชะลอตัวลงเช่นย่อยแต่ละแสดงออกจะต้องมีการพิจารณาในแง่ของทุกอย่างที่มันอาจจะกลับมา ผลลัพธ์ที่ได้คือการต่อสตริงที่มีการแก้ไขเช่นการใช้" My fullName is \(firstName) \(LastName)"นั้นดีกว่า"My firstName is" + firstName + LastNameเนื่องจากการแก้ไขไม่มีการโหลดมากเกินไป

Swift 3 ได้ทำการปรับปรุงบางอย่าง สำหรับข้อมูลเพิ่มเติมอ่านวิธีการรวมหลายอาร์เรย์โดยไม่ทำให้คอมไพเลอร์ช้าลง? . อย่างไรก็ตาม+โอเปอเรเตอร์ยังคงโอเวอร์โหลดและดีกว่าที่จะใช้การแก้ไขสตริงสำหรับสตริงที่ยาวกว่า


การใช้ optionals (ปัญหาต่อเนื่อง - มีวิธีแก้ไข)

ในโครงการง่าย ๆ นี้:

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

คอมไพเลอร์จะเห็น??และจะต้องวนซ้ำทุกประเภทที่สอดคล้องกับโปรโตคอลนี้จนกระทั่งพบประเภทที่สามารถเป็นค่าเริ่มต้นStringได้ โดยการใช้emptyStringซึ่ง hardcoded เพื่อStringคอมไพเลอร์ไม่จำเป็นต้องวนซ้ำผ่านทุกประเภทที่สอดคล้องกันExpressibleByStringLiteral

หากต้องการเรียนรู้วิธีบันทึกเวลารวบรวมดูที่นี่หรือที่นี่


คำตอบที่คล้ายกันอื่น ๆ โดย Rob Napier บน SO:

เหตุใดการเพิ่มสตริงจึงใช้เวลานานในการสร้าง

วิธีการรวมหลายอาร์เรย์โดยไม่ทำให้คอมไพเลอร์ช้าลง?

Swift Array มีฟังก์ชั่นทำให้เวลาในการสร้างนาน


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.