Takahiro Octopress Blog

-1から始める情弱プログラミング

Swiftの基礎を理解しよう!(3) ~ コーディングテストから用途を見る ~

はじめに

さて本日は久しぶりに Swift の基礎について書きたいと思います。
と言うのも、最近オンラインでのコーディングテストを日常的に解いてみているのですが、
ほとんどの場合、 Swift の基本的な書き方を求められるからです。
(それを如何に使って算数 or 数学を解くか的な問題が多いな〜と感じる今日このごろです。)

Swift もとうとう 4.2 にまで上がってきていますし、
iOSアプリの新規開発はほぼ Swift を利用する世の中になっていると思いますので、
改めて見直して損はないでしょう。

文字列分割

まずは文字列分割について見ていきます。
コーディングテストでよくあるのが、

  • 入力値が半角スペースで区切られている文字列
  • その文字列を利用して、何らかの出力値を出す

というものです。
このケースでは下記のように配列化することが多いです。

1
2
3
4
5
6
7
8
9
// 与えられる入力値が "1 2 3 4 5"の場合
let input = "1 2 3 4 5"

// 半角スペースで分割
let array = input.components(separatedBy: .whitespace)

// 出力すると下記のようになります
print(array)
// ["1", "2", "3", "4", "5"]

半角スペース以外で区切るパターンもあるので一例を載せておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 数字で分割
let input = "a1b2c3d4e"
let array = input.components(separatedBy: .decimalDigits)
print(array)
// ["a", "b", "c", "d", "e"]

// 小文字アルファベットで分割
let input = "1a2B3う4日5キ6"
let array = input.components(separatedBy: .letters)
print(array)
// ["1", "2", "3", "4", "5", "6"]

// 小文字アルファベットで分割
let input = "1a2b3c4D5"
let array = input.components(separatedBy: .lowercaseLetters)
print(array)
// ["1", "2", "3", "4D5"]

// 記号で分割
let input = "1/2?3_4-5@6"
let array = input.components(separatedBy: .punctuationCharacters)
print(array)
// ["1", "2", "3", "4", "5", "6"]

もちろん CharacterSet 内の定義を利用せずとも下記のようにも書けます。

1
2
3
4
5
// ハイフン(-)で分割
let input = "1-2-3-4-5-6"
let array = input.components(separatedBy: "-")
print(array)
// ["1", "2", "3", "4", "5", "6"]

ループ処理

続いてループ処理です。

for in

コーディングテストでよくあるのが、

  • 1つ目の入力値は、その後入力される入力値の数を表す
  • 2つ目 〜 Xつ目の入力値を用いて何らかの出力値を出す

というものです。
このケースでは、下記のように for in 文を利用することが多いです。

入力値が

1
2
3
4
5
// 入力値
3
1 2 3
4 5 6
7 8 9

のように与えられたとすると、

1
2
3
4
5
6
let input = readLine()!

for _ in 0..<num {
  let input = readLine()!
  ...
}

のように利用して、回答したりします。

enumerated()

コーディングテストでよくあるのが、

  • 配列の偶数番目には○○をする
  • 配列の奇数番目には▲▲をする
  • 上記結果を用いて、何らかの出力値を出す

というものです。
このケースでは、配列の index を利用したいので、 enumerated() を利用することが多いです。

1
2
3
4
5
6
7
8
9
10
let input = [10, 8, 5, 9, 3]
for (key, value) in input.enumerated() {
  if key % 2 == 0 {
    // 偶数の場合
    ...
  } else {
    // 奇数の場合
    ...
  }
}

forEach

コーディングテストの問題を解いて、最後に配列の要素を出力したい場合に利用しています。

1
2
3
4
5
6
7
let result = ["おはよう", "こんにちは", "こんばんは"]
result.forEach {
  print($0)
}
// おはよう
// こんにちは
// こんばんは

配列の並び替え

そして、並び替えの処理です。
コーディングテストで、

  • スコア順に並べる
  • 逆さから文字列を読む

などが問題となることがあります。
その場合には sorted()sorted(by:) を利用しています。
下記は、単純な配列の並び替えの場合です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 単純な配列の並び替えの場合
let input = [80, 100, 35, 68]
let sortedArray = input.sorted()  // 昇順

print(sortedArray)
//  [35, 68, 80, 100]

input.sort(by: { $0 < $1 })       // 昇順
print(input)
//  [35, 68, 80, 100]

input.sort(by: { $0 > $1 })       // 降順
print(input)
// [100, 80, 68, 35]

そして、逆さから文字列を読む場合です。

1
2
3
4
5
6
7
let input = "swift"
var result = ""
for i in input.characters.reversed() {
  result = result + String(i)
}
print(result)
// tfiws

配列要素の最大値/最小値

コーディングテストで

  • 生徒の点数を計算する
  • 最高得点と最低得点を半角スペース区切りで出力する

必要があるとしましょう。
そのケースでは max() , min() を利用します。

1
2
3
let input = [20, 30, 100, 60]
print("\(input.max()!) \(input.min()!))
// 100 20

文字列から文字を取得

コーディングテストでよくあるのが、

  • String 型の数字の末尾を削除する
  • ある文字列の○番目の文字を出力する

などのパターンでしょうか。

String 型の数字の末尾を削除するには、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 算出方法1
let str = "123456789X"
let num1 = str.prefix(str.count - 1)
print(num1)
// 123456789

// 算出方法2
let num2 = str[str.startIndex..<str.index(before: str.endIndex)]
print(num2)
// 123456789

// Swift3までの算出方法 - deprecated なので利用しないようにしましょう
let num3 = str.substring(to: str.index(before: str.endIndex))
print(num3)
// 123456789

とすれば良いでしょう。

文字列から○番目の文字を出力するには、

1
2
3
4
5
6
7
8
9
10
11
let str = "123456789"

// 3番目の文字列を取得する場合
let num = str[str.index(of: "3")!..<str.index(of: "4")!]
print(num)
// 3

// Swift3までの算出方法 - deprecated なので利用しないようにしましょう
let num = str.substring(with: str.index(str.startIndex, offsetBy: 2)..<str.index(str.startIndex, offsetBy: 3))
print(num)
// 3

とすればOKです。
Swift3とSwift4の算出方法が異なり、○番目の数え方も1ズレているので注意しましょう。

逆に初めから抽出したい文字列があり、それが何番目かを知りたい場合は、

1
2
3
4
5
let str = "ABCDEFG"
let index = str.index(of: "D")
if let offset = index?.encodedOffset {
  print(offset + 1)
}

とすればOKです。
気をつけておきたいのは、 offset には0番目から数えた場合の数字が入るため、+1しておくということです。

文字列の置換

特定の文字を見つけたら、別の特定の文字に置き換えるという問題が出た場合は下記の通りです。

1
2
3
4
var str = "ABCDEFG"
str = str.replacingOccurrences(of: "ABC", with: "abc")
print(str)
// abcDEFG

合計値を求める

英数国理社の5科目の合計点を出す必要があるとします。
どのように解決するかというと、

1
2
3
4
let subjectScore = [78, 80, 60, 55, 92]
let sum = subjectScore.reduce(0) { $0 + $1 }
print(sum)
// 365

という感じで reduce メソッドを使いましょう。

特定の条件を満たしたものを取得する

例えば、合格ボーダの点数を超過している科目があるか確認する必要があるとします。
この場合は filter メソッドを利用します。

1
2
3
4
5
// 合格ボーダの点数が40点だとします。
let input = [20, 30, 100, 60]
let result = input.filter { $0 > 40 }
print(result)
// [100, 60]

配列の要素全てに同じ処理を実行したい

例えば、半角スペース区切りの数字で構成された文字列が入力値として与えられた場合に、
数字に変換して計算に利用するケースがあるでしょう。
この場合は map メソッドを利用します。

1
2
3
4
5
let input = "1 2 3 4 5"
let array = input.components(separatedBy: .whitespaces)
let intArray = array.map { Int($0)! }
print(intArray)
// [1, 2, 3, 4, 5]

配列の要素全てに同じ処理を実行して、不要なものは切り捨てたい

イメージとしては、 mapfilter の良いとこ取りでしょうか。
以下のように compactMap を利用します。
もし、 map を利用したら B の部分でコケてしまうでしょう。

1
2
3
4
5
let input = "1 2 B 4 5"
let array = input.components(separatedBy: .whitespaces)
let intArray = array.compactMap { Int($0) }
print(intArray)
// [1, 2, 4, 5]

因みに、Swift4.0以前では flatMap というメソッド名でしたが、Swift4.1から compactMap に改名されました。

まとめ

さて、如何でしたでしょうか。
今回はコーディングテストの視点から各メソッドの用途を説明してみました。
と言っても超基本的な内容なので、今更ではあるんですけどね。

と言うことで本日はここまで。

Comments