日記
今日書いたコード
unicode scalarを読むことにした · Gurrium/SwiftKilo@a8e0312 · GitHub
感想
昨日最悪な気分になりながら寝ようとしたら、unicode scalarをそのまま読めばいいのでは?と思いついた。どうせ表示する時はString
に入れるわけだし問題ないはず。SwiftのString
のカウントはextended grapheme cluster単位だから消すときも多分大丈夫。
unicode scalarを読むことにした · Gurrium/SwiftKilo@a8e0312 · GitHub
昨日最悪な気分になりながら寝ようとしたら、unicode scalarをそのまま読めばいいのでは?と思いついた。どうせ表示する時はString
に入れるわけだし問題ないはず。SwiftのString
のカウントはextended grapheme cluster単位だから消すときも多分大丈夫。
使えそうなプロパティのテストを書いた · Gurrium/SwiftKilo@31d1555 · GitHub
characters
を使ってみたが次のクラスタを入力するまで読めないのが回避できなかったのでbytesを使ってみることにした…あれ??より退化してない??アホくさくなってきたのでもう諦めようかな。
charactersを使う · Gurrium/SwiftKilo@c4be96b · GitHub
https://developer.apple.com/documentation/foundation/filehandle/asyncbytes/3766657-characters で良かった。ただ次の文字を入力しないと読んでくれないのでタイムアウトは自分で実装する必要がある。
イテレータをラップするかstdinに\0を入れるか?
困りを再現するテストを書いた · Gurrium/ReadGraphemeCluster@8d2f4ab · GitHub
"ここ"がないとスタックする。FileHandle.bytes.unicodeScalars
をfor-awaitで待つとタスクがキャンセルされてもawaitで待ち続けるっぽい。(もしくは、awaitで待ち続けるのでタスクがキャンセルされない?)
1つのgrapheme clusterを入力したあとにタイムアウトするようにコードを書いているので、scalarを読むたびにタウムアウトする以上に待つ必要があると複数のscalarからなるgrapheme clusterを読めなくなる。
適当なAsyncSequenceを実装すると同じところでスタックしなかったのでFileHandle.bytes.unicodeScalars
の実装が悪いのかも。
actor Flag { var content: Bool init(content: Bool) { self.content = content } func set(_ content: Bool) { self.content = content } } func test_cancellingTask() async throws { let pipe = Pipe() let handle = pipe.fileHandleForReading let flag = Flag(content: true) try pipe.fileHandleForWriting.write(contentsOf: Array("a".utf8)) let groupResult = try await withThrowingTaskGroup(of: Bool.self) { group in group.addTask { for try await scalar in handle.bytes.unicodeScalars { // var it = handle.bytes.unicodeScalars.makeAsyncIterator() // while let scalar = try await it.next() { // これでも同じ print(scalar) await flag.set(false) try await Task.sleep(nanoseconds: 50_000) // ここ } return true } group.addTask { while await flag.content { try await Task.sleep(nanoseconds: 1_000) } print("timeout") return false } let ret = try await group.next()! group.cancelAll() XCTAssertFalse(try XCTUnwrap(ret)) return ret } print("groupResult:", groupResult) }
ところでFileHandle.bytes.characters
*1が生えてたので終わり。あほくさ。
"ここ"が呼ばれるまでwithThrowingTaskGroupがスタックしてそうで詰まっている。
// [https://github.com/Gurrium/ReadGraphemeCluster/commit/d009232e69d957413022f07ae7df1e8710e93da8#diff-102f0c74b4bceb38182a3d1f4823344c81ab3d0d92662ce8f740d1f595889185R154] withThrowingTaskGroup { taskGroup in for try await scalar in fileHandle.bytes.unicodeScalars { if Task.isCancelled { return nil // ここ } // ... } // ... taskGroup.cancelAll() // ... }
こうあるのでcancelAll()
を呼べばいいと思うんだが…
A group waits for all of its child tasks to complete, throw an error, or be canceled before it returns https://developer.apple.com/documentation/swift/withthrowingtaskgroup(of:returning:body:)#discussion
飽きたので終わり。
GraphemeClusterBreak.init(scalar:)を追加した · Gurrium/ReadGraphemeCluster@e88a181 · GitHub
今日もだめでした
matchのバグを修正した · Gurrium/ReadGraphemeCluster@26750a3 · GitHub
.any
よりあとにしたやつは使われていないやつなので、GraphemeClusterBreak.allCases.first { $0.match(scalar) }!
としたときに先にmatchしてほしい。あまり筋がよくないと思う。
他にも色々やったけど駄目だった。疲れた。