Swiftをチラ見しよう

なんかアップルから新しい言語、出たみたいですよ?

Welcome to Swift

どうせ多分一ヶ月後には忘れると思うけど、言語を一巡りしてみよう。体力ないのでLanguage Guideまでで失礼します。

ちなみに現時点において動作環境を手に入れるには、iOSデベロッパ契約が必要です。僕はありません。

このエントリは全て憶測で書いています。

このエントリは全て憶測で書いています。

このエントリは全て憶測で書いています。

このエントリは全て憶測で書いています。

ひとめぐり

(途中、私自身にしか通じないような表現が出てきます。)

// コメント
/* マルチラインコメント */
/* /* ネストしたコメントも可能! */ */

// main関数不要
println("Hello world") // C系にありがちなセミコロンも要らない

//// 変数 
// Swiftは強い型付き。
var v = 10 (型宣言しないと推論される)
var v: Double = 10.0 
var (x, y) = (1, 2)
(y, x) = (x, y) // ちなみに = 自体は値を返さないそうだ

// 定数
let c = 10

//// 文字列への埋め込み式 \(...)
let message = "I need to learn \( c ) languages."

///// コレクション
// 配列 (構造体の一種)
let list = ["apple", "bacon", "cheese"] // let list: String[] あるいはlet list: Array<String>、型[]は Array<型>の短い書き方
let intList = 0..10 // 0, 1, 2, 3, .. , 9
let intHalfList = 0...10 // 0, 1, 2, 3 .. , 10
var list = [1, 2, 3]
var doubleList = list + list // リストの結合
var mixture: Any[] = [0, "asdf", (1, 2)] // なんでもあり型はAny
list[0] // もちろんインデックスでアクセス可能
list[0..1] = ["BIG APPLE", "BACON PIE"] // 複数要素代入可能
let emptyIntList = Int[]() // 空リスト
// 構造体の一種とされているが、配列は代入によってコピーされないし、代入元とリスト内容を共にする。
// インデックスへの代入も共に影響をうける。
// しかし、配列に値を追加した場合 or array.unshare() した場合に配列がコピーされたものにいれかわる。
// 配列を代入するときに array.copy() で代入すれば最初からコピーされたものになる。

// ディクショナリ(マップ 構造体の一種)
let map = [
"New York": "The Big Apple",
"Las Vegas": "Sin City", // マップの末尾が , で終わっていてもいい
] // let map: Dictionary<String, String>
// 構造体の一種なので、ディクショナリを代入したり関数に渡したりするとディクショナリは値型のようにコピーされる。

//// 型

// deftype的な
typealias EnoughMemoryForHumanLife = UInt16

// String
// 値型の一種。
// \0, \\, \t, \r, \n, \", \'  などバックスラッシュ記法もある。
// \xnn (2桁 16進法), \unnnn (4桁 16進法), \Unnnnnnnn (8桁 16進法)
"🐶🐱" // 絵文字OK。
// そしてUTF-16エンコーディングなどされない。絵文字1文字も正確に1文字である。
// 文字数のカウント、任意位置の文字の取り出しは効率がO(n)であり、巨大文字列の文字数カウントはコストがかかる。
// カウントのコストはCと同じ。
// 1文字ずつ先頭からインデックスでイテレートしたらO(n^2)。恐怖。for-in文を使うこと。
"" // == String() 空文字

// Character
// StringはCharacterのリスト。
let c: Character = "ユニコード1文字"

// 数
// Javaとだいたい同じ
10
1000_000.000_001 // 1000000.000001
0b01010 // 10
0o12 // 10
0xc // 10

// Int8 = signed char,  UInt8 = unsigned char 相当, UInt16 なども
// キャストは構造体newと同じく キャスト後の型(値) でOK

// Bool(ean)
// if 文はBoolしかうけつけない
true
false

// Optional値。nullableってことです。
var optionalValue: String? = findSomething()
optionalValue! // これで中身アクセス
if optionalValue { }  // if文の条件とかにそのまま使える。Boolコンテキストで使える?
// Optionalにはnilを代入できる。nullではなくnilです。
// nilには厳しく、optional以外はnilを扱えません。
optionalValue = nil

// オブジェクトの作成は "クラス()" で。
let newObject = Class()

// タプル
let tupleValue: (String, Int) = ("あ", 1)
let namedTuple = (hiragana: "あ", at: 1) // namedTuple.hiragana でアクセス可能。名前も型の一部みたいな。
func returnTuple() -> (name: String, age: Int) {
  return ("ほげほげ", 100)
}
// 関数
// パラメータに=つければデフォルト値
func subtract(left: Int, right = 0) -> Int {
  return left - right
}

// あるいは
func subtract(l left: Int, r right: Int) { ... }
func subtract(#l: Int, #r: Int) -> Int { ... }
// で、呼ぶときはsubtract(l: 20, r: 10) といったように名前付き引数で指定可能。

// 引数変更したい人はパラメータにvarつけりゃいいよ
func foo(var youCanAssignThisVar: string) { ... }

// 参照渡ししたい人はパラメータにinoutつけりゃいいよ
func inc(inout i: Int){ return i++ }
var foo = 3
inc(foo)
foo // == 3
inc(&foo)
foo // == 4

// 関数は値として使える。レキシカルスコープを持つ。
// "-> 型" で関数の返りの型
func greeting(name: String) -> String {
  return "Hey, \( name )-san."
}
// func greeting(name: String...) と書けば可変長引数(VLA)

// レキシカルスコープかつ関数は値として使えるのでこういうのも可能
func curryAdd(x: Int) -> (Int -> Int) {
  func internalAdd(y: Int) -> Int {
    return x + y
  }
  return internalAdd
}
curryAdd(10)(20) // => 30

// 関数名を省略して、こうも書ける
func curryAdd(x: Int) -> (Int -> Int) {
  (y: Int) -> Int in
  return x + y
}

// 型がわかっている場合はこれも可能
func double(intList: Int[]) {
  intList.map({ eachInt in each 2 * eachInt })
}
double([1..100])

// クロージャ内の引数は "$数字" でも参照可能(ゼロベース)
{ $0 > $1 }

//// 演算子オーバーロード
// 中置演算子 + をオーバーロード       
@infix func + (left: MyType, right: MyType) -> MyType { return ... }
// ほか @prefix func - (a: A) -> A { return ... }
// @assignment func += (inout left: A, right: A){ left = ... }
// @prefix @assignment func ++ (inout a: A) -> A { a = ... return a }
// @infix func ==, @infix func != 

// カスタム演算子の定義(のち、演算子オーバーロード)
// prefix / infix / postfix で出現位置を指定:
operator prefix +++ {}

// クラス。C#と同じく対立する値型の概念として構造体がある。
// 継承関係は : のあとに親クラスでOK
class Hoge : Fuga{
  // クラスフィールド
  var myName
  @lazy var myStrictName = getMySecretNameFromFarDB() // @lazy使うと最初のアクセス時まで初期化を遅らせられる

  // コンストラクタはinit, thisはself, superはsuper
  init(name: String) {
    self.myName = "Pretty \(name)-chan"
  } // deinit() がデストラクタ

  // 普通のメソッド。オーバーライドを防ぎたい場合は@final fun thisIsAMethod() みたいにする。
  func thisIsAMethod() -> String {
    return myName
  }

  override func overwrittenMethod() -> Int {
    // オーバーライドしたメソッドはoverrideと絶対に書かなければならない
  }

  class func methodBelongingToHoge() {
    // Javaのstaticメソッド. クラスメソッド. Hoge.methodBelongingToHoge()
    self // ここでのselfは型自体
  }

  var height: Double
  var weight: Double
  // C#のようなプロパティ構文
  // willSet {}, didSet {} でプロパティ設定前後の動作を指定可能
  // overrideしてる場合は override var bmi: Double { ... } みたいにしてね
  var bmi: Double {
    get {
      return weight / (height * height)
    }
    set {
      // newValueとして代入文の右辺の値を貰える
      height = sqrt( 1 / ( newValue / weight ) )
      // newValueが気に入らなければ set( newBMI ) とかすればnewBMIという名前にできる
    }
  }
  // get { ... } のみなら、 get というキーワードは省略可能

  // subscript
  // 要するに hoge[...] のインデックス指定のアクセサ構文。
  // 1個以上の引数を指定可能.
  subscript(index: Int) -> String {
    get { ... } // indexを使って内部的なリストから値引っ張るなり
    set { ... } // indexを使って内部的なリストに値を引っ張り込むなり
  }

  // Nested types
  // 中に入れ子のenum / structure / classが定義可能
  // (たぶんJavaみたいに複雑な非static/staticクラスの区分けみたいのは無く、一律staticな感じかも)

}
// プロパティ/メソッドは . でアクセスできる
foo.bar
foo.bar()
// Optionalな変数の場合は ?. でアクセスできる
optionalFoo?.bar

// 構造体
// C#と似たらしい感じ。オブジェクトなら参照がコピーされるところで値ごとコピーされる。
struct Color {
  var r: Int
  var g: Int
  var b: Int
  func distance() -> Double {
    return sqrt( r * r + g * g + b * b)
  }

  // static変数もちたければどうぞ
  static var asdf = 1
  static func modifyAsdf(newVal: Int) {
    asdf = newVal
  }

  // 構造体のプロパティを更新するものはmutating修飾子が必要
  mutating func superRed() {
    self.r = 255
    self.g = 0
    self.b = 0
  }

  // そのままself自体入れ替えも可能
  mutating func superRed2(){
    self = Color(255, 0, 0)
  }

  // func init(){ } コンストラクタもあり
}
var c: Color = Color(255, 255, 255)

// Enum
enum Foods: Int {
  case Apple = 1
  case Bacon, Cheese, Donuts
  func tastes() -> String{
    switch self { // Enumのswitchは
    case .Apple, .Donuts:
      return "Sweet"
    case .Bacon, .Cheese:
      return "Salty"
    default:
      return "What the heck is this?"
    }
  }
  // enumもmutating持てる
  mutating func iAmAnApple() {
    self = Apple
  }
}
Foods.Apple.tastes() // "Sweet"
Foods.Apple.toRaw() // 1
fromOne: Foods? = Foods.fromRaw(1) // == Foods.Apple が来るだろうけどOptional
// 代数的データ型 (Algebraic Data Type) みたいな? のも可能
enum TreeEntry {
  case Node(TreeEntry, TreeEntry)
  case Leaf(String)
}
var t:TreeEntry = TreeEntry.Node(TreeEntry.Leaf("Big bro"), TreeEntry.Node(TreeEntry.Leaf("Me"), TreeEntry.Leaf("You")))

func traverse(t: TreeEntry){
  switch t {
  case let .Node(leftNode, rightNode):
     traverse(leftNode)
     traverse(rightNode)
  case let .Leaf(name):
     println("Found \(name).")
  } 
}

//// Protocol
// インタフェースみたいなの
protocol HavingName {
  var name: String { get }
}
class Cat: HavingName {
  // プロトコルの実装
  var name: String = "吾輩は猫である"
}
// プロトコルを複数実装したオブジェクトの型は obj: protocol<ProtocolA, ProtocolB> みたいに扱うことも可能で
// それぞれ obj.protocolAMember obj.protocolBMember みたいにそのままプロトコルメンバにアクセス可能   

//// Extension
// C#のExtension, Rubyにも似たようなのあったね
extension Int { // よく Int : ProtocolName1, ProtocolName2 のようにプロトコルをおっかぶせたりするらしい
  func double() -> Int{
    return self * 2
  }
}
let pi = 3
pi.double() // == 6

//// Generics
enum Tree<LeafType> {
  case Node(Tree<LeafType> left, Tree<LeafType> right)
  case Leaf(LeafType)
}
func findAll<LeafType>(t: Tree<LeafType>) -> LeafType[] {
  switch(t) {
    case let t.Node(l, r):
      return findAll(l) + findAll(r)
    case let t.Leaf(v):
      return [v]
  }
}
// 型の制約
// <A, B, C where ... > というように where 以降に書く。細かい制約もかけるっぽい。

//// 演算子 だいたい普通の演算子は使える。
i++
i += 10
9.2 % 3.0 // == 0.2 doubleの除算もできる
bool ? true-case : false-case // いわゆる三項演算子
a === b // アイデンティティ比較
a is Type // a は Typeを実装しているか?
a as DownCastType // a を DownCastTypeとしてキャスト
a as? DownCastType // a を DownCastType?としてキャスト(キャストできなきゃnil)

//// 制御構造
//// if 
if Bool式 { } else if { } else { }

//// Optional Binding(if-let 式)

if let foo = optionalな値を返す式 {
  message = "The found value is \(foo)" // ちゃんと値があった場合の分岐 
} else {
  message = "The value is not found." // デフォルト値処理とかこっちでやればいいんじゃない?
}

//// for-in
for member in memberList {
}

// for-in+分配。マップのエントリごとループなど。
let map = [ "New York": "The Big Apple", "Las Vegas": "Sin City"] 
for (cityName, cityNickname) in map {
}

// 普通のfor文
for var i = 0; i < 10; ++i {
}

// while / do-while
while i > 0 { }
do { } while i > 0

// switch
switch customerName { // スイッチ対象の値の型はいろいろできるらしい
case "knjname":
  println("All our property are belong to you.")
case "knjname2": // 基本的にfall-throughしない
  println("You're faking.")
case "knjname3", "knjname4" // 複数の値にマッチさせたい場合
  println("3 or 4")
case "knjname5"
  println("5 is good and also ...")
  fallthrough // fall throughしたい違いのわかる大人用
case "knjname6"
  println("6 is better.")
default: 
  println("Your life is under knjname's control.")
}

// こういうこともできる
switch hoge {
  case 0 as Int:
    ...
  case 0 as Double:
    ...
  case is Double:
    ...
  case let (x, y) as (String, String):
    ...
  case 0..10:
}

case 1..100: // みたいなのも数値の振り分けには使える

// ラベル
// Javaと同じ。制御構造にラベルをつけてbreak/continueに飛べる
outside: while ... {
  while ... {
    if(...) break outside
  }
}

感想

  • とーってもC#っぽい。Optionalとか値型とかプロパティとか。ここらへんの感想は人によって違うんだろうけれども。
  • 正直、言語としてパクりだらけだとか、新しいアイディアがないとか、そういうのはわかりますけど、ちょっと違うと思うんです。既存に比べて、使いやすい、わかりやすい、これ大事じゃないでしょうか。
  • 文字列は文字のリストなのは普通だけど、文字がユニコード1文字というのはすごい。UTF-16でお茶を濁す言語とちがって、🐶(絵文字)が1文字です。当然文字数カウントはO(n)みたいです。
  • 意外に言語の機能多い。