Skip to content

narumij/swift-ac-foundation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

206 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

swift-ac-foundation

Swift
License: CC0-1.0

利用方法

SwiftPM プロジェクトで swift-ac-foundation ライブラリを使用するには、Package.swift ファイルの dependencies に次の行を追加します。

.package(
  url: "https://github.com/narumij/swift-ac-foundation",
   branch: "release/AtCoder/2025"),

タグでの指定はC++でのunsafeFlagsの使用がありビルド拒否となるため、必要な場合は直接revision指定してください。

実行可能ターゲットの依存関係に「AcFoundation」を追加してください。

.target(name: "<target>", dependencies: [
  .product(name: "AcFoundation", package: "swift-ac-foundation")
]),

ソースコードに以下を記述してインポートします。

import AcFoundation

内容

IOReader

とりあえず使う場合

let N: Int = try read()
let (H, W): (Int, Int) = try read()
let (X, Y, Z): (Int, Int, Int) = try read()
let (A, B): (Int, String) = try read()
let (C, D): (Int, [Character]) = try read()
let A = try [Int].readLine()
let G = try [String].readLine()
let H = try [[Character]].readLine()

readLine()メソッドは、それぞれ以下とおおよそ等価で、かつ中間のSwift文字列を作成しません

let A = readLine()!.components(separatedBy: " ").map { Int($0)! }
let G = readLine()!.components(separatedBy: " ").map { $0 }
let H: [[Character]] = readLine()!.components(separatedBy: " ").map { $0.map{ $0 } }

細かい話

以下の型に対して、標準入力から空白または改行までを取得する stdin プロパティやread()メンバー関数を追加します。

  • 固定長整数
  • 浮動小数点数
  • 文字列
  • 文字配列 (Character)
  • C 文字配列 (UInt8)

入力の区切り
空白、タブ、改行を区切り文字として使用します。

let N: Int = Int.stdin
let D: Double = Double.stdin
let S: String = String.stdin
let T: [Character] = [Character].stdin
let U: [UInt8] = [UInt8].stdin

応用例 1: ペアの入力
例えば、問題の入力が H W のように並んでいる場合は、次のように記述できます。

let (H, W): (Int, Int) = try read()
let (H, W): (Int, Int) = stdin()
let (H, W): (Int, Int) = (try .read(), try .read())
let (H, W): (Int, Int) = (.stdin, .stdin)

応用例 2: 配列の入力
個数 N と数列 A を入力する場合は以下のように記述します。縦横に並ぶデータにも対応可能です。

let N: Int = Int.stdin
let A: [Int] = (0..<N).map { .stdin }

応用例 3: クエリの処理
クエリ Q とそれに応じた入力を処理する例です。

let Q: Int = Int.stdin
for _ in 0..<Q {
  switch Int.stdin {
    case 1:
      let (A, B) = (Int.stdin, Int.stdin)
      // 処理
    case 2:
      let C = Int.stdin
      // 処理
    default:
      break
  }
}

他の便利なメンバー関数

数を指定して読む場合

let A = [Int].stdin(columns: N)
let G = [String].stdin(rows: H, columns: W)
let H = [[Character]].stdin(rows: H, columns: W)
let I = [[UInt8]].stdin(rows: H, columns: W)

部分利用

IOReader 機能のみを利用したい場合は以下をインポートしてください。

import IOReader

readstdin

基礎となるメソッドや関数は命名のセオリーに従い、動詞のreadが識別子に絡んでいます。

それに対してtryを省ける便利プロパティ、便利メソッドの識別子はstdinになっています。

これはC++のcinを意識したことが一つあります。

もう一つは、stdin識別子をこんな風変わりに使う人は滅多にいないだろうという打算によりこの識別子にしています。

コンテスト中にambiguousを解決するのはかなり困難なので、極力これを避けたいからです。

風変わりな識別子というのは割と忘れやすいものなので、その点でもstdinが一番ましという判断をしています。

その他

この IOReader は、文字列分割、文字列コピー、数値化の無駄を最小限に抑えるよう設計されています。
数値が多数並んでいる場合、標準のreadLine() を使用するよりも実行時間を短縮できる場合があります。

一方で、非常に長い文字列を1行丸ごと読み取る場合は、readLine() の方が高速です。
Swift の文字列操作は AtCoder の問題によって TLE となりやすいため、問題に応じた使い分けを推奨します。


IOUtil

stdoutやstderrによる方法は廃止しました

print 関数の to: パラメータで利用できる標準出力と標準エラーのTextOutputStremを提供します。 これにより、以下のような記述で標準エラーへの出力が可能になります。

import Foundation
import IOUtil

print("Hello, world!", to: &FileOutputStream.standardError)

他に、性能を比較したい場合にI/O負荷を軽減する目的で整数専用のfastPrintというものを追加してあります。

部分利用

個別importでしか提供していません。

IOUtil 機能を利用したい場合は以下をインポートしてください。

import IOUtil

Bisect

Python の bisect を移植したもので、Swift で二分探索を利用可能にします。

let sortedList = [1, 4, 8, 100, 1000]
print(sortedList.bisectLeft(99)) // 3

探索範囲の限定
ArraySlice を使用することで探索範囲を限定できます。

let sortedList = [1, 4, 8, 100, 1000]
print(sortedList[0..<3].bisectLeft(99)) // 3

部分利用

Bisect 機能のみを利用したい場合は以下をインポートしてください。

import Bisect

Pack

SE-283が凍結になっているため、辞書のキーにタプルを使うことは不可能なままのようです。 このため、例えばABC393Cなど、グラフ問題を盆栽なしにSwiftで解こうと思った際、C++のPairに相当するものもないため、 辞書のキーに、都度構造体とそれにHashable適用をするコードを書く必要が生じます。 カジュアルに競技プログラミングを楽しもうとしている人にそこまでプロトコル知識を要求するのはどうかと思い、用意しました。

これを使うことでABC393Cは以下のようにコンパクトな提出でACできる、予定です。

import AcFoundation

let (_, M): (Int, Int) = stdin()
nonisolated(unsafe) var m: [Pack<Int, Int>: Int] = [:]
nonisolated(unsafe) var ans = 0
for _ in 0 ..< M {
  var (u,v): (Int, Int) = stdin()
  if u == v {
    ans += 1
    continue
  }
  if u > v {
    swap(&u, &v)
  }
  m[.init(u, v), default: 0] += 1
}
m.forEach {
  ans += $0.value - 1
}
print(ans)

Comparableにも対応しているため、dijkstraを書く際にも利用できます。

コンパイルで問題が生じた場合、迂回用のPack2またはPack3もお試しください。

部分利用

Pack 機能のみを利用したい場合は以下をインポートしてください。

import Pack

CxxWrapped

std::gcdとstd::lcmが利用できます。

std::gcdっぽいものではなく、実際にstd::gcdを呼んでいます。

std::lcmっぽいオレオレ実装ではなく、実際にstd::lcmを呼んでいます。

CxxInteropで呼ぶつもりでしたが軽く挫折したため、extern "C"して、C Interopで呼んでいます。

import AcFoundation

print(gcd(12,16)) // 4

print(lcm(12,16)) // 48

部分利用

CxxWrapped 機能のみを利用したい場合は以下をインポートしてください。

import CxxWrapped

CharacterUtil

文字列問題では、Swiftの文字列、Characterの配列、UInt8の配列のどれを使うのか選択する必要があります。それぞれに一長一短ありますが、コンテストではCharacterの配列をおすすめしています。

文字の配列を利用する際に困るのが、文字のループを書くのがやや面倒くさいことと、辞書順比較がわかりにくいところです。

このモジュールではまず、ascii文字限定ですが、CharacterにStridableを付与します。

これがどう嬉しいかというと、文字のループが以下のように書けるようになります。

import AcFoundation

for c: Character in "a"..."z" {
  print(c) // aからzまで順に出力する
}

このモジュールでは他に、Characterの配列に辞書順比較を行う比較演算子を追加します。

import AcFoundation

print(Array("abc") < Array("abd")) // true

本モジュールを利用するには個別importが必要です。以下をソースの割と先頭に記述してください。 (readLine関数の追加で型の記述に不便が生じる懸念があり、一括importから外しました)

import CharacterUtil

UInt8Util

文字列問題では、Swiftの文字列、Characterの配列、UInt8の配列のどれを使うのか選択する必要があります。それぞれに一長一短ありますが、コンテストではCharacterの配列をおすすめしています。おすすめしてはいますが、CやC++に慣れてる人にはUInt8の配列のほうが取り扱いが楽だったりもします。0x0端のcString関連メソッドがdeprecatedになりはじめたので、CCharではなく、UInt8を推しています。

UInt8を利用する場合に困るのが、文字定数が使えないことです。本モジュールではこれをカバーする拡張を追加してあり、以下のように書けます。

let c: UInt8 = "A"

本モジュールでは他に、UInt8の配列に辞書順比較を行う比較演算子を追加します。

let abc: [UInt8] = "abc".compactMap(\.asciiValue)
let abd: [UInt8] = "abd".compactMap(\.asciiValue)
print(abc < abd) // true

Characterのプロパティ相当のものもいくつか追加してあります。

本モジュールを利用するには個別importが必要です。以下をソースの割と先頭に記述してください。

import UInt8Util

StringUtil

文字列問題では、Swiftの文字列、Characterの配列、UInt8の配列のどれを使うのか選択する必要があります。それぞれに一長一短ありますが、コンテストではCharacterの配列をおすすめしています。おすすめしてはいますが、不慣れな人にこれを強いるのは酷だろうし、かといってSwiftの文字列は整数の添え字が使えず、ここが初心者泣かせなので、文字列に便利メソッドを用意しました。あくまで初心者用であることをご承知の上お使いください。Swiftの文字列はリッチが過ぎるため、競技プログラミングではTLEになりやすいです。この点も併せてご承知ください。

ABCのA問題B問題ぐらいは楽に書きたい、といった場合にどうぞ。

let s = "abcdef"
// 1文字取得
print(s[0]) // "a"

// 文字列取得
print(s[0..<s.count]) // "abcdef"
print(s[0..<s.count]) // "abcdef"
print(s[0...]) // "abcdef"
print(s[..<s.count]) // "abcdef"
print(s[2..<4]) // "cd"
print(s[2...]) // "cdef"
print(s[..<4]) // "abcd"
print(s[...4]) // "abcde"

本モジュールを利用するには個別importが必要です。以下をソースの割と先頭に記述してください。

import StringUtil

Miscellaneous

これ以外の分類で浮いてしまっているものたちです。

部分利用

import Miscellaneous

Convinience

横着用です。個別importでしか提供していません。

import Convinience

MT19937

メルセンヌツイスターです。疑似乱数です。 AHC等で、再現性のある乱数が使いたい場合にご利用ください。

var mt = mt19937_64(seed: 0)
let randomInteger = Int.random(in: Int.min...Int.max, using: &mt)
let randomDouble = Double.random(in: 0...1, using: &mt)

個別importでしか提供していません。

import MT19937

その他

modintやBigIntをIOReader対応にして利用するには以下のコードが必要です。

extension static_modint: IOIntegerConversionReadable {
  public static func convert(from: Int) -> Self { .init(from) }
}
extension BigInt: IOStringConversionReadable {
  public static func convert(from: String) -> Self { .init(from)! }
}

制約によっては、以下で速度が多少稼げます。

// 入力の制約がInt.minからInt.maxまでの場合利用可
extension BigInt: IOIntegerConversionReadable {
  public static func convert(from: Int) -> Self { .init(from) }
}
// 入力の制約が0からmod未満までの場合利用可
extension static_modint: @retroactive IOUnsignedIntegerConversionReadable {
  @inlinable @inline(__always)
  public static func convert(from: UInt) -> Self { .init(rawValue: from) }
}

ライセンス

このライブラリは CC0-1.0 ライセンスで提供されていますが、一部Apache 2.0 Licenceのコードを含みます。

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages