質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2436閲覧

【Swift】while文の条件が無視されるのはどういうときか・その対処法

unagimochimochi

総合スコア7

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/10/12 01:05

編集2020/10/13 00:35

無視される、というのは言い方が違うかもしれませんが、この表現が一番しっくりくるのでご容赦ください。
while文を使っていて気になったことがありましたので質問させていただきます。

理想的なwhile文

Swift

1var countA = 0 2while countA < 3 { 3 print("countA: (countA)") 4 countA += 1 5}

以下がプリントされます。

countA: 0 countA: 1 countA: 2

わからないこと・困ったこと

複雑な処理をwhile文で行いたいため、これを擬似的に表現するため時間に揺らぎを与えて実行すると、条件を無視して無限に処理が続いてしまいます。

Swift

1import Foundation 2 3let dispatchQueue = DispatchQueue(label: "queue") 4 5var countB = 0 6while countB < 3 { 7 // 時間遅れに揺らぎ(0.5秒から1.0秒の間)を与える 8 let delay = Double.random(in: 0.5...1.0) 9 dispatchQueue.asyncAfter(deadline: .now() + delay) { 10 print("countB: (countB)") 11 countB += 1 12 } 13}

以下がプリントされます。

countB: 0 countB: 1 countB: 2 countB: 3 // (省略) countB: 3243 countB: 3244 countB: 3245 countB: 3246 // (止めない限り無限に続く)

順番通りに処理されるので揺らぎが効いているのか?という疑問もありますが。

また、変数checkCountBで0番目が終わったら1番目へ……と行わせるようにすると、上と同じように条件は無視されますが4回目が終わったところで止まります。

Swift

1import Foundation 2 3let dispatchQueue = DispatchQueue(label: "queue") 4 5var countB = 0 6var checkCountB = 0 7while countB < 3 { 8 if countB == checkCountB { 9 checkCountB += 1 10 // 時間遅れに揺らぎ(0.5秒から1.0秒の間)を与える 11 let delay = Double.random(in: 0.5...1.0) 12 dispatchQueue.asyncAfter(deadline: .now() + delay) { 13 print("countB: (countB)") 14 countB += 1 15 } 16 } 17}

以下がプリントされます。

countB: 0 countB: 1 countB: 2 countB: 3

このように、条件が無視されて処理が続くのはどのようなときなのか、
その対処法などありましたら教えていただきたく思います。

具体的なこと

質問内容は上記で以上なのですが、念のため実際に使っている部分も掲載します。
(ややこしいので読み飛ばしていただいても大丈夫だと思います)

データベースにIDを保存する処理(前回の質問)を、
1人目、1人目が終わったら2人目……と順番にやりたかったので、以下のように書いています。

Swift

1var planIDsOnDatabase = [String]() // メンバ変数で定義しています 2 3// ---------- ↓実際の処理 ---------- 4 5var everyone = ["sample01", "sample02", "sample03"] 6print("everyoneの要素数: (everyone.count)") // 3 7 8var count = 0 9var checkCount = 0 10 11while count < everyone.count { 12 13 if count == checkCount { 14 checkCount += 1 15 planIDsOnDatabase.removeAll() 16 17 print("count: (count)") 18 fetchPlanIDs(accountID: everyone[count], completion: { 19 // データベースの予定ID取得後に新たなIDを追加 20 self.planIDsOnDatabase.append(planID) 21 22 // 次の処理(データベースに保存) 23 self.addPlanIDToDatabase(accountID: everyone[count], newPlanID: planID, completion: { 24 // データベースに予定ID保存後、次の人へ 25 count += 1 26 }) 27 }) 28 } 29}

条件式while count < everyone.countから、0〜2までの3回実行させたいのですが、意図せず4回目が実行されてしまいエラーになります。

以下のようにプリントされます。

everyoneの要素数: 3 count: 0 count: 1 count: 2 count: 3 Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444 2020-10-12 08:25:30.191834+0900 varmeets[1929:70602] Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444 Message from debugger: The LLDB RPC server has exited unexpectedly. Please file a bug if you have reproducible steps.

補足情報(FW/ツールのバージョンなど)

Xcode 11.6
Swift 5

ここまで読んでくださり、ありがとうございます。
質問に至らぬ点があるかもしれませんが、どうぞよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

条件式は無視されてません。ちゃんとcountB<3だからループが回っています。
以下の理由によりいつまで経っても終わらないように見えるだけです。

asyncAfter()は指定時間に処理をすることを登録しているだけなので
asyncAfter()の実行自体は一瞬で終わります。
このとき、処理を登録しただけでまだcountBの値は変わっていません。
したがってwhileが高速で回り、大量の処理が次々と登録されてしまっています。
結果としていつまで経っても終わらないように見えます。

以下のようにすれば意図通りになるかと思います。

swift

1var countB = 0 2while countB < 3 { 3 let delay = Double.random(in: 0.5...1.0) 4 let count = countB 5 dispatchQueue.asyncAfter(deadline: .now() + delay) { 6 print("countB: (count)") 7 } 8 countB += 1 9}

投稿2020/10/12 01:30

編集2020/10/12 01:35
ozwk

総合スコア13553

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

unagimochimochi

2020/10/13 00:30

ご回答ありがとうございます。 お返事が遅くなり申し訳ございません。 asyncAfter() 内に countB += 1 を書くと、条件の < 3 になかなか達しないため何度も繰り返し処理が実行されてしまう、ということですね。納得しました。 ただ、もう一点よろしいでしょうか。 後半で記した、 ---------------------------------------- import Foundation let dispatchQueue = DispatchQueue(label: "queue") var countB = 0 var checkCountB = 0 while countB < 3 { if countB == checkCountB { checkCountB += 1 // 時間遅れに揺らぎ(0.5秒から1.0秒の間)を与える let delay = Double.random(in: 0.5...2.0) dispatchQueue.asyncAfter(deadline: .now() + delay) { print("countB: (countB)") countB += 1 } } } ---------------------------------------- 上記では4回目(countB: 3)が実行されたり、3回目で止まったりが半々、といった感じです。 こちらも繰り返し処理が高速で何度も行われているというのはわかるのですが、 if countB == checkCountB という条件式のすぐ後に checkCountB += 1を、揺らぎの処理後に countB += 1 を入れているため、if countB == checkCountB{} 内が4回実行される理由がよくわかりません。 また、処理の例を示してくださりとてもありがたいのですが、 そちらを実行すると揺らぎの関係で数字が順番通りに出てきませんよね。 揺らぎがあっても0から2までを確実に順番通りにプリントする方法はあるのでしょうか。 質問の段階でもう少し詳しく書いておくべきでしたが、もしお時間がございましたらご回答いただけると助かります。 とはいえ、丁寧なご回答、本当にありがとうございました!
ozwk

2020/10/13 05:06

スタート countB=checkCountB=0なのでifに入る checkCountB=1にしてasyncAfter()が走る countB=0, checkCountB=1なのでしばらくifに入らない ゆらぎ時間後、asyncAfter()で予約された処理が走る、countB=1 ifに入る ... ゆらぎ時間後、asyncAfter()で予約された処理が走る、countB=3 処理が走ったタイミングが、whileの条件判断後でifの条件判断前だとifに入る、= 4回目が走る それ以外だと3回で終わる
ozwk

2020/10/13 05:09

ゆらぎ時間内に3回処理を行いたいのか ゆらぎ時間後に1回、それが終わったらゆらぎ時間後に1回、それが終わったらゆらぎ時間後に1回なのか どっちですか?
unagimochimochi

2020/10/13 07:40

ありがとうございます。whileとifの間でcountBの値が変わると4回目が行われてしまうということで、しっかり理解できました。 やりたいことは後者のゆらぎ時間後に1回、それが終わったらゆらぎ時間後に1回、それが終わったらゆらぎ時間後に1回です。 これを必ず3回で終わるようにしたいです。 お手数ですが、お時間ございましたらどうぞよろしくお願いいたします。
unagimochimochi

2020/10/13 21:52

whileが高速で回るので合理的ではないかもしれませんが、 if countB == checkCountB && checkCountB < 3 とすればいけました。 質問タイトルとずれてしまいますが、for文で配列のindex番号を指定する方法でもやりたいことは実現できそうです。 ご回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問