chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

Swiftの言語仕様を学ぶ・・・型変換でstack trace... うーん。。。

「詳解Swift」という本を買って、サンプルを打ち込んで勉強中。今はデータ型を理解。。Swiftは型チェックが厳しく、また暗黙の型変換はなされない、ということで、型変換の関数(正確には関数ではなくイニシャライザらしい)が用意されている。Character型は1文字でString型は複数文字とのことで違いがよく分からない(なぜわざわざ1文字のCharacter型を作ったのか)(Cで言うところの、char / char[]の違いではなさそうだし)のだが、、分からないなりに無理やりString->Characterに変換させてみた。すると、、Fatal errorでスタックトレースが表示された。たしかに誤った操作でエラーかもしれないが、、、容易に発生しうる間違った型変換と思うのですが、スタックトレースしますかね。。それほど許せない操作なのか??

  7> Character("hello")
Fatal error: Can't form a Character from a String containing more than one extended grapheme cluster: file Swift/Character.swift, line 177
Current stack trace:
0    libswiftCore.so                    0x00007ffff7c21810 swift_reportError + 50
1    libswiftCore.so                    0x00007ffff7c95370 _swift_stdlib_reportFatalErrorInFile + 115
2    libswiftCore.so                    0x00007ffff797a16e <unavailable> + 1397102
3    libswiftCore.so                    0x00007ffff7979cc7 <unavailable> + 1395911
4    libswiftCore.so                    0x00007ffff79799a3 <unavailable> + 1395107
5    libswiftCore.so                    0x00007ffff7979410 _assertionFailure(_:_:file:line:flags:) + 511
6    libswiftCore.so                    0x00007ffff797989c <unavailable> + 1394844
7    libswiftCore.so                    0x00007ffff79873a0 Character.init(_:) + 290
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

ちなみに、、動かしているのはMac上ではなく、WIndows上のUbuntuにswiftを入れてインタプリタモード(REPL)で動かしている。MAC OSだとこうはならないのだろうか。

■補足
勉強のため「詳解Swift」という本を買いました。本屋で「実践Swift」と読み比べたけど、どちらも同じような細かさの解説本と思いました。フォントのせいなのかレイアウトのせいなのか分かりませんが、ぱっと見た感じ、「詳解Swift」の方が読みやすいと思ったのでこちらを買いました。

詳解 Swift 第5版

詳解 Swift 第5版

確かにページ数も多いですが、基本的な説明と、メモリ管理、拡張(extention)、ジェネリクス等、高度な概念に分かれており、まず基本部分をざっと読めば最低のコードは組めるのではと期待*1。。章立てがうまくなされているので、基本仕様の解説を選んで、サンプルを打ち込みながら勉強することが可能。細かすぎて辞書のようにしか使えないということはありません。まだ少ししか読んでいませんが、筆者(萩原氏)の思いが伝わってくる本です。

■追記
少し勉強して分かったこと。swiftのソースコードを見ていると、頻繁に _ が出てくるけど、 _ は一体何なのかわからなった。 _ とは、使わない、指定しない、捨てる等、  を表すキーワードであった。
あと、文字列操作がめちゃくちゃめんどくさいことが分かった。 "abc"[3]のようなことは書けない、Stringの位置を指定するには、Index型を作って位置指定が必要であった。さらに、位置指定で取り出された結果はString型ではなくて、String.SubSequenc型であった。StringイニシャライザでString型に変換が必要らしかった。 勉強を兼ねてsubstringを書くと以下のようになると思う。もっと勉強すると、関数の冒頭でguardとか使えると思うが。

Swiftで書いた文字列抜き取り関数

func substring(_ str:String, _ start:Int, _ len:Int) -> String? {
   if start > (str.count - 1){
         return nil
   }
   if (start + len - 1) > (str.count - 1){
         return nil
   }
   let from = str.index(str.startIndex, offsetBy:start)
   let to = str.index(str.startIndex, offsetBy: start + len)
   let pick = String(str[from ..< to])
   return pick
}

prefixとsuffixを組み合わせたバージョン

func substring(_ str:String, _ start:Int, _ len:Int) -> String? {
   if start > (str.count - 1){
         return nil
   }
   if (start + len - 1) > (str.count - 1){
         return nil
   }
   let pick = str.suffix(str.count - start)      // 文字列末尾から切り取る
   return String(pick.prefix(len))                //文字列先頭から切り取る
}

Swiftは型にとことんこだわる言語ということで、気をぬいて実装するとすぐに型不一致で怒られるだろう。型を確認する関数を作った。

func showType<T>(_ tgt:T){
   print("type is",type(of: tgt))
}

■追記
クロージャを勉強。クロージャサンプルを作ってみる。。。UDP通信のコールバックはクロージャで表現されていると思う。多分。。

//クロージャ型変数を定義
var cat : (String, String) -> String

//クロージャを定義して変数に代入
cat = { "!!>>\($0), \($1)<<!!" }
print(cat("hello","bye"))         // クロージャを実行

//クロージャを引数とする関数を定義
func test(_ str1:String, _ str2:String, by clos:(String,String)->String) -> String {
  clos(str1, str2)
}

print(test("foo", "bar", by:cat))  // 関数を実行
print(test("aaa","bbb", by:{ "__\($0),\($1)__" }))

// 接尾クロージャ (trailing closure)で表現して実行
print(test("aaa","bbb"){ "_x_\($0),\($1)_x_" })

/*  
  output

!!>>hello, bye<<!!
!!>>foo, bar<<!!
__aaa,bbb__
_x_aaa,bbb_x_

*/

■追記
オプショナル型を学ぶ。nilを取りうる場合にoptional型を使い、値を使う時はxxx!によりラッピングを剥いて使うと理解。

//
//  option
//

func showType<T>(_ tgt:T){
   print("type is",type(of: tgt))
}

var x : Int?  // set nil to x
var y : Int   // set 0 to y

x = nil
y = 0

print("x = nil")
print("y = 0")
showType(x)
showType(y)

x = 0
y = 0

print("x = 0")
print("y = 0")
showType(x)
showType(y)


//print(y)
//if y > 0 { // ok
//   print(" y > 0 ")
//}
// x > 0 // Exception
//x!  //  Fatal error
//x+2 //  error

if x == nil {
  print("nil")
}else{
  print(x!)
}

print("-------------")
print("x = 1")
x = 1
if var xx  = x {
   showType(xx)
   xx = xx + 1
   print("u can use x, value:\(xx)") 
}else{
   showType(x)
   print("u can not use x") 
   print("x is.. \(String(describing:x))")
}

print("-------------")
print("x = nil")
x = nil
if var xx  = x {
   showType(xx)
   xx = xx + 1
   print("u can use x, value:\(xx)") 
}else{
   showType(x)
   print("u can not use x") 
   print("x is.. \(String(describing:x))") 
}
print("-------------")
print("x = 1")
x = 1
if var x  = x {
   showType(x)
   x = x + 1
   print("u can use x, value:\(x)") 
}else{
   showType(x)
   print("u can not use x") 
   print("x is.. \(String(describing:x))")
}

print("-------------")
print("x = nil")
x = nil
if var x  = x {
   showType(x)
   x = x + 1
   print("u can use x, value:\(x)") 
}else{
   showType(x)
   print("u can not use x") 
   print("x is.. \(String(describing:x))") 
}



x = nil
var val  = (x != nil) ? x! : -1
print(val)

// nilcoalescing operator ??
print("-----------------")
print("nil coalescing operator \"??\"")
val = x ?? -1
showType(val)
val += 1
print(val)


//
// IUO  implicitly unwrapped optional
//
print("-----------------")
print("IUO")
var xx : Int! = nil

xx = nil
if xx == nil {
  print("xx is nil")
  showType(xx)
  print(String(describing:xx))
}else{
  print("xx is not nil")
  xx += 1
  print(xx!)
}
xx = 1
if xx == nil {
  print("xx is nil")
  print(String(describing:xx))
}else{
  print("xx is not nil")
  showType(xx)
  xx += 1
  print(xx!)
}

/*********************************************:

x = nil
y = 0
type is Optional<Int>
type is Int
x = 0
y = 0
type is Optional<Int>
type is Int
0
-------------
x = 1
type is Int
u can use x, value:2
-------------
x = nil
type is Optional<Int>
u can not use x
x is.. nil
-------------
x = 1
type is Int
u can use x, value:2
-------------
x = nil
type is Optional<Int>
u can not use x
x is.. nil
-1
-----------------
nil coalescing operator "??"
type is Int
0
-----------------
IUO
xx is nil
type is Optional<Int>
nil
xx is not nil
type is Optional<Int>
2

**********************************************/

クラスの章をまったく読んでいないけど。。だんだん疲れてきたので、、文法はこれぐらいで、SwiftUIでPGしてみたい。

自分用メモ・・・プロトコルはswift全体にわたる基本概念だから読んだ方がいい

*1:オプショナル型だけはちゃんと理解する必要あり。