Swift で「千反田える」

# プロローグ
今日は [iOSDC](https://iosdc.jp/2016/) の日でしたが、気付いたらもうチケット完売してて行けなかったorz
というわけで iOSDC の場外トーク（嘘）で、「一反田えー」「二反田びー」…「二十六反田ぜっど」「二十七反田えー」…のように定義した場合、果たして「千反田」は「える」になるのだろうか、Swift で検証してみた。

# 最低限の検証
まずは一番簡単なソースとして、`1反田a` `2反田b` `3反田c`…の出力を作ってみる：

```swift
let alphabets = Array("abcdefghijklmnopqrstuvwxyz".characters)

for i in 0 ..< 1000 {
	let alphabet = alphabets[i % alphabets.count]
	print("\(i + 1)反田\(alphabet)")
}

// 最後の行：1000反田l
```

おお、ちゃんと `1000` 番目の反田に対応してるアルファベットは `l` で間違いないようです！
ところがこれはただの `1000反田l` であり、`千反田える`までにはまだほど遠い。
というわけでプログラムを改造しよう。

# アルファベットの日本語出力
まずは `a` `b` `c` などのアルファベットの日本語出力機能をつけてあげなければ。というわけで `Character` の拡張を作ります：

```swift
extension Character {
	
	var japanese: String {
		
		switch self {
		case "a": return "えー"
		case "b": return "びー"
		case "c": return "しー"
		case "d": return "でぃー"
		case "e": return "いー"
		case "f": return "えふ"
		case "g": return "じー"
			
		case "h": return "えいち"
		case "i": return "あい"
		case "j": return "じぇい"
		case "k": return "けい"
		case "l": return "える"
		case "m": return "えむ"
		case "n": return "えぬ"
			
		case "o": return "おー"
		case "p": return "ぴー"
		case "q": return "きゅー"
		case "r": return "あーる"
		case "s": return "えす"
		case "t": return "てぃー"
			
		case "u": return "ゆー"
		case "v": return "ゔぃー"
		case "w": return "だぶりゅー"
		case "x": return "えっくす"
		case "y": return "わい"
		case "z": return "ぜっど"
			
		default: return ""
		}
		
	}
	
}
```

これでもう一度ループ回してみます

```swift
let alphabets = Array("abcdefghijklmnopqrstuvwxyz".characters)

for i in 0 ..< 1000 {
	let alphabet = alphabets[i % alphabets.count]
	print("\(i + 1)反田\(alphabet.japanese)")
}

// 最後の行：1000反田える
```

よし少しだけだけ千反田えるに近づきました！

# 数字の日本語出力
一桁しかないアルファベットより、何桁もある数字の方が難しいね。何より桁やその桁の数字によって書き方も違ってくるし。
というわけでアプローチを考えないといけないわけですが、とりあえずまずは渡された数字を桁毎の日本語を決めなければなりません。ところが普通に数学的にこの作業をやると割と面倒だし結局出力してるのは `String` 型だし、ここでちょっと変わったアプローチとして、一回数字を文字列に変換して文字列の各文字を取る、というやり方ができます。こうすることで、先ほどの `a` `b` `c` と同じように `Character` 型で日本語を定義することができます。というわけで先ほどの `Character` の拡張をちょっと手を加えてみます

```swift
extension Character {
	
	var japanese: String {
		
		switch self {
		case "0": return "零"
		case "1": return "一"
		case "2": return "二"
		case "3": return "三"
		case "4": return "四"
		case "5": return "五"
		case "6": return "六"
		case "7": return "七"
		case "8": return "八"
		case "9": return "九"
		
		// ...
		
		default: return ""
		}
		
	}
	
}
```

ところがこれはまだ「桁」という概念を考慮していない。このままだと `1000` が来ても `一零零零` の出力が出るだけです。

「桁」という概念は通常のテキストと違い、「右から左に」数えていくものです。つまり適当な数字を与えて、この数字の左から3番目の桁はどの桁なのか、というのは左から3つ数えても何もわかりません。右からこの桁まで数えて初めてわかるものです。なのでここの処理は逆順で一回処理しなければなりません。そして右から順番には「一の桁」「十の桁」「百の桁」「千の桁」となりますが、「一の桁」というのは特に「桁」を言わない（つまり例えば「30」は「三十」のように「十の桁」を指す「十」を言いますが、「3」は別に「一の桁」を指す「一」は言わない）ので、桁の言い方としては「十」「百」「千」の言い方だけ定義する必要があります。というわけでそろそろ `Int` の拡張を作ります：

```swift
extension Int {
	
	var japanese: String {
		let digits = Array("\(self)".characters).reverse()
		let pronunciations = digits.enumerate().map { (i, digit) -> String in
			let iPronunciation: String
			switch i {
			case 1:
				iPronunciation = "十"
				
			case 2:
				iPronunciation = "百"
				
			case 3:
				iPronunciation = "千"
				
			default:
				iPronunciation = ""
			}
			
			return digit.japanese + iPronunciation
		}
		return pronunciations.reverse().reduce("", combine: { (pronunciation, digit) -> String in
			return pronunciation + digit
		})
	}
	
}
```

必要なのは千の桁までなのでとりあえず「千」までを定義しておきます

ところがこの定義の場合まだ十分ではありません、例えば `100` の数字が出た場合、この定義では `一百零十零` の出力になります。我々が望んでいるのは `百` の出力です。なのでさらに追加定義が必要です。

まず最初に、桁の数字が「0」の場合、そもそもその桁を言わないように処理を入れてあげなければなりません。

そして次に、桁の数字が「1」の場合、最下位、つまり「一の桁」の場合以外は習慣的に「一」なんて言いません。

というわけで上記の処理をさらに取り入れた修正がこんな風になります：

```swift
extension Int {
	
	var japanese: String {
		let digits = Array("\(self)".characters).reverse()
		let pronunciations = digits.enumerate().map { (i, digit) -> String in
			switch digit {
			case "0":
				return ""
				
			default:
				let iPronunciation: String
				switch i {
				case 1:
					iPronunciation = "十"
					
				case 2:
					iPronunciation = "百"
					
				case 3:
					iPronunciation = "千"
					
				default:
					iPronunciation = ""
				}
				
				let digitPronunciation: String
				if digit == "1" && i > 0 {
					digitPronunciation = ""
				} else {
					digitPronunciation = digit.japanese
				}
				
				return digitPronunciation + iPronunciation
				
			}
			
		}
		return pronunciations.reverse().reduce("", combine: { (pronunciation, digit) -> String in
			return pronunciation + digit
		})
	}
	
}
```

そしてここまできたら `Character` 型も `Int` 型も `var japanese: String` の算出プロパティーがあるので、せっかくだから `protocol` を作りましょう：

```swift
protocol JapaneseConvertible {
	var japanese: String { get }
}

extension Character: JapaneseConvertible {
	
	var japanese: String {
		// ...
	}
	
}

extension Int: JapaneseConvertible {
	
	var japanese: String {
		// ...
	}
	
}
```

これで最後もう一度ループ回しましょう：

```swift
let alphabets = Array("abcdefghijklmnopqrstuvwxyz".characters)

for i in 0 ..< 1000 {
	let alphabet = alphabets[i % alphabets.count]
	print("\((i + 1).japanese)反田\(alphabet.japanese)")
}

// 最後の行：千反田える
```

やった！千反田えるができた！

# 全ソースコード
```swift
import Foundation

protocol JapaneseConvertible {
	var japanese: String { get }
}

extension Character: JapaneseConvertible {
	
	var japanese: String {
		
		switch self {
		case "0": return "零"
		case "1": return "一"
		case "2": return "二"
		case "3": return "三"
		case "4": return "四"
		case "5": return "五"
		case "6": return "六"
		case "7": return "七"
		case "8": return "八"
		case "9": return "九"
			
		case "a": return "えー"
		case "b": return "びー"
		case "c": return "しー"
		case "d": return "でぃー"
		case "e": return "いー"
		case "f": return "えふ"
		case "g": return "じー"
			
		case "h": return "えいち"
		case "i": return "あい"
		case "j": return "じぇい"
		case "k": return "けい"
		case "l": return "える"
		case "m": return "えむ"
		case "n": return "えぬ"
			
		case "o": return "おー"
		case "p": return "ぴー"
		case "q": return "きゅー"
		case "r": return "あーる"
		case "s": return "えす"
		case "t": return "てぃー"
			
		case "u": return "ゆー"
		case "v": return "ゔぃー"
		case "w": return "だぶりゅー"
		case "x": return "えっくす"
		case "y": return "わい"
		case "z": return "ぜっど"
			
		default: return ""
		}
		
	}
	
}

extension Int: JapaneseConvertible {
	
	var japanese: String {
		let digits = Array("\(self)".characters).reverse()
		let pronunciations = digits.enumerate().map { (i, digit) -> String in
			switch digit {
			case "0":
				return ""
				
			default:
				let iPronunciation: String
				switch i {
				case 1:
					iPronunciation = "十"
					
				case 2:
					iPronunciation = "百"
					
				case 3:
					iPronunciation = "千"
					
				default:
					iPronunciation = ""
				}
				
				let digitPronunciation: String
				if digit == "1" && i > 0 {
					digitPronunciation = ""
				} else {
					digitPronunciation = digit.japanese
				}
				
				return digitPronunciation + iPronunciation
				
			}
			
		}
		return pronunciations.reverse().reduce("", combine: { (pronunciation, digit) -> String in
			return pronunciation + digit
		})
	}
	
}

let alphabets = Array("abcdefghijklmnopqrstuvwxyz".characters)

for i in 0 ..< 1000 {
	let alphabet = alphabets[i % alphabets.count]
	print("\((i + 1).japanese)反田\(alphabet.japanese)")
}
```

# えるたそ〜
えるたそ〜