// Xcode で Editor メニュー > Show Render Markup でお読みください /*: # 中学数学と Swift 山田 泰司 ## はじめに Swift Playground を中学数学を題材に体験します。それぞれのページで、下欄の□ボタン「Execute/Stop Playground」で実行できます。 参考書は、 * 数研出版: 改訂版 チャート式® 基礎からの中学1〜3年数学 2016 を使います。 但し、プログラミングの体験が目的なので、中学数学の知識に限っているわけではなく、コードを実現する上で高校数学や大学数学の知識を使った方が簡単な場合は、積極的にその知識を使っているのでご留意ください。 ## もくじ * 1_4: 比例と反比例 * 1_7_1: 資料の整理と活用1 * 1_7_2: 資料の整理と活用2 * 2_2: 連立方程式 * 2_3: 1次関数 * 2_4: 図形の性質と合同 * 2_5: 三角形と四角形 * 2_6: 確率 * 3_1: 式の計算 * 3_2: 平方根 * 3_3: 2次方程式 * 3_4: 関数 y = 𝑎𝑥² * 3_5: 相似 ご覧の通り、参考書のすべてをプログラミング言語 Swift に落とし込めていません。例えば、手を動かすべき箇所などは、Playground では Javascript など使えませんので、そうした工夫が使えません。なので、中学数学を学びたい方は、是非とも「教科書」をあたってください。くれぐれも本稿は「中学数学を題材にプログラミング言語 Swift を体験する」材料に過ぎませんので(今後とも、私としてもチャレンジはしてみますが、現状)、ご留意のほどお願いします。 とはいえ、プログラミング言語を通して「数学」を学ぶ、これはいい機会だと思われます。是非、コードをいじってエラーで壊して困ったりして見て下さい。失敗が大きな「糧」になります。 ## Swift のはじめに Swift は「型」に厳しい言語で、実数型と整数型を明確に区別します。よって、実数 10 はいちいち「10.0」と書かないと整数型になってしまいます。 また、pow 関数など実数(Double)型を引数に渡すべきとき、n が整数(Int)型であって、pow(Double(n)) などと型変換が必要となることに注意します。そして、さらにこの結果を整数として扱っている場合、Int(pow(Double(n))) とします。プログラミング言語 C/C++ などでは「暗黙の型変換」が備わっており、こうしたことは不要です。Swift はプログラマが意図しない「暗黙の型変換」を防ぐためにこのような言語設計になっています。 さらに、コンピュータは分数を直接扱うことはできません。プログラミング言語によっては分数を扱えるものがありますが、Swift は標準ではサポートされていません。よって、ここでは単に実数で分数を表現します。 また、プログラミング言語 Swift の演算の記号と、 教科書で使われる記号はいくつか異なります。以下にまとめておきました。 演算子 | Swift | 教科書 乗算 | * | × または ∙ または 𝑎𝑏 の 𝑎 と 𝑏 の間ように「記号なし」 | 除算 | / | ÷ | 減算 | - | − | (形で区別はつきませんが文字コードがこれら異なります) 剰余 | % | mod | 等号 | == | = | 不等号 | != | ≠ | 未満 | < | < | (形で区別はつきませんが文字コードがこれら異なります) より大きい | > | > | (形で区別はつきませんが文字コードがこれら異なります) 以下 | <= | ≦ または ≤ | 以上 | >= | ≧ または ≥ | 範囲 | 1...n | 1 ⋯ 𝑛 | 範囲 | 1.. Bool { // 整数の配列を引数に渡し、真偽型を返す関数 var sum = 0 var odd = false // 奇数番目か for d in a.reversed() { // 末尾の数値から調べる var d = d // d は定数であるが、値を変えたいので新たな変数 d を用意 assert(0 <= d && d <= 9) // 0 ≦ d ≦ 9 でなければならない if odd { d *= 2 if d > 9 { d -= 9 } } sum += d odd = !odd } return sum % 10 == 0 } /*: 1. [Wikipedia, Luhnアルゴリズム](https://ja.wikipedia.org/wiki/Luhn%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0) */ do { print(isLuhn(a: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])) // 正しい true print(isLuhn(a: [ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ])) // 正しくない false /*: 使い方の例として、乱数で適当にたくさん生成した 11 桁のクレジット番号が正しいかを調べてみます。 */ do { var list = [[Int]]() for _ in 0..<100 { var a = [Int]() for _ in 0..<11 { a.append(Int.random(in: 0...9)) } if isLuhn(a: a) { // 「a」というラベルの引数を一つもつ関数 list.append(a) } } print(list.count, list) } // 適当に生成した数字では9割方正しくないことがわかる } /*: しかし、正しいか否か、だけでなく、最初の数字がいくつであれば「正しい」とされるのかも知りたいとき、複数の返り値が必要です。その場合、返り値にタプル型を使います */ func isLuhnWithNumber(a: [Int]) -> (isCorrect: Bool, firstNumber: Int) { // 整数の配列を引数に渡し、(真偽型, 整数)の「タプル型」を返す関数 var b = a if isLuhn(a: a) { // 先の関数を利用する return (true, a[0]) } for n in 0...9 { b[0] = n if isLuhn(a: b) { break } } return (isCorrect: false, firstNumber: b[0]) } do { print(isLuhnWithNumber(a: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])) // 正しい (isCorrect: true, firstNumber: 0) print(isLuhnWithNumber(a: [ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ])) // 正しくない (isCorrect: false, firstNumber: 3) print(isLuhnWithNumber(a: [ 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ])) // 正しい (isCorrect: true, firstNumber: 3) /*: 使い方の例として、乱数で適当にたくさん生成した 11 桁のクレジット番号が正しいかを調べて、正しくなければ正しくなる数字に修正します。 */ do { var list = [[Int]]() for _ in 0..<100 { var a = [Int]() for _ in 0..<11 { a.append(Int.random(in: 0...9)) } let r = isLuhnWithNumber(a: a) if r.isCorrect { list.append(a) } else { a[0] = r.firstNumber if isLuhn(a: a) { list.append(a) } } } print(list.count, list) } // 適当に生成した数字でも正しくなるように先頭の数字を修正しているので、すべて登録されている } /*: #### 文字列の処理 配列型、辞書型のほかに集合型のような型を「コレクション型」といいます。 文字列型もコレクション型のように for 文やアルゴリズムが使えます。 */ do { let string = "9784774196909" for c in string { print("\(c) ", terminator: "") } // 9 7 8 4 7 7 4 1 9 6 9 0 9 print() } /*: 13 桁の ISBN 番号が正しいかを判定する関数 */ func isISBN13(a: [Int]) -> Bool { var s = 0, w = 1 for d in a.reversed() { s += w * d w = 4 - w } return s % 10 == 0 } func isISBN(string: String) -> Bool { let a = string.map({ Int(String($0)) ?? 0 }) // map は要素ごとにクロージャを適用する関数で、ここでは String.Element 型を String 型に直してから Int 型に変換している。それは失敗する可能性があるのでオプショナル型の Int 型で返されるので、失敗した場合は 0 とする return isISBN13(a: a) } print(isISBN(string: "9784774196909")) // 正しい true print(isISBN(string: "9784774196900")) // 正しくない false func isLuhn(string: String) -> Bool { let a = string.map { Int(String($0)) ?? 0 } // 括弧がなくてもよい return isLuhn(a: a) } print(isLuhn(string: "00000000000")) // 正しい true print(isLuhn(string: "00123456789")) // 正しくない false /*: #### さまざまな整数型 Swift では基本的に符号付き整数 Int 型を使うことが推奨されますが、用途に応じて他の整数型も必要となります。 以下にそれぞれの整数型の表すことのできる「範囲」を示しておきます。但し、UInt128 以上は現時点で Swift 標準ではありません。 */ print("符号つき Int8 型", "表せる範囲: \(Int8.min)〜\(Int8.max)") print("符号つき Int16 型", "表せる範囲: \(Int16.min)〜\(Int16.max)") print("符号つき Int32 型", "表せる範囲: \(Int32.min)〜\(Int32.max)") print("符号つき Int64 型", "表せる範囲: \(Int64.min)〜\(Int64.max)") print("符号つき Int 型", "表せる範囲: \(Int.min)〜\(Int.max)") print("符号なし UInt8 型", "表せる範囲: \(UInt8.min)〜\(UInt8.max)") print("符号なし UInt16 型", "表せる範囲: \(UInt16.min)〜\(UInt16.max)") print("符号なし UInt32 型", "表せる範囲: \(UInt32.min)〜\(UInt32.max)") print("符号なし UInt64 型", "表せる範囲: \(UInt64.min)〜\(UInt64.max)") print("符号なし UInt 型", "表せる範囲: \(UInt.min)〜\(UInt.max)") print("符号なし UInt128 型", "表せる範囲: \(UInt128.min)〜\(UInt128.max)") print("符号なし UInt256 型", "表せる範囲: \(UInt256.min)〜\(UInt256.max)") print("符号なし UInt512 型", "表せる範囲: \(UInt512.min)〜\(UInt512.max)") //print("符号なし UInt1024 型", "表せる範囲: \(UInt1024.min)〜\(UInt1024.max)") // とんでもなく遅いので、やらない /*: 符号つき Int8 型 表せる範囲: -128〜127 符号つき Int16 型 表せる範囲: -32768〜32767 符号つき Int32 型 表せる範囲: -2147483648〜2147483647 符号つき Int64 型 表せる範囲: -9223372036854775808〜9223372036854775807 符号つき Int 型 表せる範囲: -9223372036854775808〜9223372036854775807 符号なし UInt8 型 表せる範囲: 0〜255 符号なし UInt16 型 表せる範囲: 0〜65535 符号なし UInt32 型 表せる範囲: 0〜4294967295 符号なし UInt64 型 表せる範囲: 0〜18446744073709551615 符号なし UInt 型 表せる範囲: 0〜18446744073709551615 符号なし UInt128 型 表せる範囲: 0〜340282366920938463463374607431768211455 符号なし UInt256 型 表せる範囲: 0〜115792089237316195423570985008687907853269984665640564039457584007913129639935 符号なし UInt512 型 表せる範囲: 0〜13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 符号なし UInt1024 型 表せる範囲: 0〜179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 */ //: [Next](@next)