[Swift]Unicode Scalarと文字列count時間複雑度関係


アップルSWIFT公式文書で自慢のハングル(ザラザラ~)

上図は、アップルの公式ドキュメントUnicode Scalarを説明しています.

Unicode Scaleとは?


可変サイズのString文字列
  • にアクセスする方法.
  • Unicodeベース21ビットコード
  • UTF-32とほぼ同じです.
  • は、1つまたは複数のUnicode ScaleからなるCharacterである.
  • ここでは、サイズ可変のStringの例が一気に理解できる.
    let wordA = "A"
    print(wordA.utf8.count) // 1
    print(wordA.utf16.count)    // 1
    print(wordA.unicodeScalars.count)   // 1
    
    let wordKorean = "권"
    print(wordKorean.utf8.count)    // 3
    print(wordKorean.utf16.count)   // 1
    print(wordKorean.unicodeScalars.count)  // 1
    
    let wordXxck = "凸"
    print(wordXxck.utf8.count)  // 3
    print(wordXxck.utf16.count) // 1
    print(wordXxck.unicodeScalars.count)    // 1
    
    let emoji = "👩"
    print(emoji.utf8.count) // 4
    print(emoji.utf16.count)    // 2
    print(emoji.unicodeScalars.count)   // 1
    UTF−8,UTF−16,Unicode Scalarの長さを単一文字について計算した.
    Unicode Scaleの長さが1の場合のみ、UTF-8とUTF-16は常に可変です.
    Q)単一文字の場合、Unicode Scaleのサイズは常に1ですか?
    A)いいえ
    以下の例を見てください.
    let emoji = "👩"
    print(emoji.utf8.count) // 4
    print(emoji.utf16.count)    // 2
    print(emoji.unicodeScalars.count)   // 1
    
    let emojiWithBlack = "👩🏿"
    print(emojiWithBlack.utf8.count)    // 8
    print(emojiWithBlack.utf16.count)   // 4
    print(emojiWithBlack.unicodeScalars.count)  // 2
    
    let emojiWomanFamily = "👩‍👩‍👧‍👧"  // 두명의 엄마와 두명의 딸로 구성된 가족
    print(emojiWomanFamily.utf8.count)    // 25
    print(emojiWomanFamily.utf16.count)   // 11
    print(emojiWomanFamily.unicodeScalars.count)  // 7
    Macではなくオペレーティングシステムなのか、ブラウザの違いで表情が少し違うように見えるので写真を添付します.

    1文字の場合、Unicode Scaleのサイズは7になることがあります.
    では、ここでemojiwoman家族のメールを数えてみてはいかがでしょうか.
    let emojiWomanFamily = "👩‍👩‍👧‍👧"  // 두명의 엄마와 두명의 딸로 구성된 가족
    print(emojiWomanFamily.count)    // 1
    奥応?私たちが望むように出てきましたか??
    文字列に直接カウントした場合、カウントはCharacterになります.
    Characterは1つまたは複数のUnicode Scaleの集合である.
    Characterは私たちが考えているメールです
    だから1が現れる
    簡単に言えば

    1つ以上のUnicode ScalarがCharacterを構成し、CharacterがStringを構成します。


    以上、Unicode Scaleの説明です.
    お辞儀~


    まだ終わってない!


    ここまで読んでいたらこんな思いをしていたはず
    Unicode Scaleとは何か知っていますが、何に使われているか知っていますか.
    業務に少しも役に立たない!
    これはお客様のビジネスに役立ちます.
    UnicodeScaleを知ってこそ次の疑問を知ることができるからです
    主に2つの原因があります.
    1.Stringはなぜ[Int]の購読をサポートしないのですか?
    2.String長を取得する場合、なぜ時間の複雑さはO(1)ではなくO(N)なのか.
    まず、最初の理由を理解してみましょう.
    let string = "A권凸👩👩🏿👩‍👩‍👧‍👧"
    print(string[1])	// error: 'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead.
    ご存知のように、他の言語でも使用できますが、SWIFTでは下付きアクセス文字列の使用はサポートされていません.
    前述したように、文字サイズが可変であるためです.
    ここでもう一つ疑問があります
    UTF-8 UTF-16 Unicode Scaleはすべて可変です
    しかし、Characterの長さは1ではありませんか?
    Character単位で持って来ればいいんじゃないですか?
    はい、そうです.Character単位で「Aボリューム」をチェック👩👩🏿👩‍👩‍👧‍👧「countの長さは6でもよい.
    ここからsubscript[5]を使用してアクセスすると、👩🏿「出力するはずです.
    実際,このようにアクセスする拡張はスタックオーバーフローに分布する.
    それらをくっつけて使ってもいいですよ.

    しかし、なぜSWIFTは正式に提供されないのでしょうか。


    その前に知っておくべきことがあります.
    Collectionへのアクセス方法に関するプロトコルは、大きく2つあります.(私は2つしか知らない)
    双方向収集-集合プロトコル/時間複雑度O(N)メソッドで、前後の2つの方法をサポート
    Random AccessCollection-すぐにアクセスできる集合プロトコル/時間複雑度O(1)アクセス
    Stringは双方向Collectionを継承します.
    extension String : BidirectionalCollection
    Unicode Scalarアクセスの特性から、直接アクセスではなくシーケンスアクセスであるべきです.
    一つのCharacterではUnicode Scaleがいくつあるか分からないので、順番にアクセスしてチェックする必要があります.
    Q)StringはArrayだけではありませんか?
    A) No
    他の言語では、Arrayはこのようになっているので、直接下付きでアクセスできます.
    SWIFTは演技に工夫を凝らしていたので、そうはしませんでした.
    StringはUnicode Scaleで構成されており、Characterとしてのみ使用できます.
    ここから最初の質問に戻りましょう
    1.Stringはなぜ[Int]の購読をサポートしないのですか?
    Stringは双方向の集合に従うため、直接アクセスできません.
    多くのオープンソースで使用されていますが、Stringアレイへの直接アクセスなどの開発者の誤解を招く可能性があるため、サポートされていない可能性があります.
    今では2番目の質問にも答えることができます!
    2.String長を取得する場合、なぜ時間の複雑さはO(1)ではなくO(N)なのか.
    Stringは双方向Collectionだから!
    では、伝統的なString CountとCharacter Array、countの時間的複雑さを見てみましょう.
    var longStr = ""
    (0...999999).forEach { _ in longStr += "A권凸👩👩🏿👩‍👩‍👧‍👧" } // 총 6백만 길이 문자열 생성
    let longStrArr = Array(longStr) // Character Array로 변환
    
    let start01 = CFAbsoluteTimeGetCurrent()
    print(longStr.count)
    let diff01 = CFAbsoluteTimeGetCurrent() - start01
    print("Took \(diff01) seconds") // Took 3.1775630712509155 seconds
    
    let start02 = CFAbsoluteTimeGetCurrent()
    print(longStrArr.count)
    let diff02 = CFAbsoluteTimeGetCurrent() - start02
    print("Took \(diff02) seconds") // Took 0.00018596649169921875 seconds

    従来のString count-3.17756307152509155秒


    Character Arrayに変換後のcountモード-0.00018596649169921875秒


    countを数えただけで、こんなに差があります!
    Unicode Scalarについて深く理解しました.
    思ったより長くて...
    逆にスタックオーバーフローコードを貼って勝手に使えばいいのに、なぜこんなに複雑な原因を知っているのか分からない...