USBシリアル変換ケーブル(SRC06-USB)をOS Xで使う

USBシリアル変換ケーブルに、バッファローの「SRC06USB・06USM」という製品を使うことになったのですが、あいにくバッファローダウンロードページにはOS X向けのドライバーがありません。

f:id:swiftlife:20160113104147j:plain

USBシリアル変換ケーブル | バッファロー

そこで、Future Technology DevicesのVirtual COM Port Driverを使い、OS Xでも使えるように設定しました。OS X El Capitan(10.11.2)で確認しました。

ドライバのダウンロード

まず、Future Technology DevicesVirtual COM Port Driversのページから、「Mac OS X 10.9 and above」にあるドライバーをダウンロードします。

f:id:swiftlife:20160113105104p:plain

ドライバのインストール

.pkgファイルがディスクイメージに含まれているので、ダブルクリックしてインストールします。

f:id:swiftlife:20160113105301p:plain

インストールが終われば、以降はUSBシリアル変換ケーブルをMacに接続することで、/devフォルダ下にシリアルポートのデバイスファイルが見えるようになります。

% ls -l /dev/tty.usbserial*
crw-rw-rw-  1 root  wheel   18,   4  1 13 10:54 /dev/tty.usbserial-FTE2PTDB

利用(cuコマンド)

cuコマンドを使ってアクセスするのが簡単です。なお、このページによるとcuコマンドは/var/spool/uucpフォルダを利用するので、このディレクトリの所有者である「uucp」にスイッチユーザーして実行する必要がありました*1

% ls -l /var/spool/
  :
drwxr-xr-x   2 _uucp  wheel   68  1 13 11:03 uucp
# 115200bps,パリティ無で接続
% sudo -u _uucp cu -115200 --parity=none -l /dev/tty.usbserial-FTE2PTDB

ちなみにcuコマンドを終了するには、チルダ~に続けてピリオド.を入力します(man cuを見れば載っているのですが、最初は見ないまま始めてしまい、シェルに戻せなくなって焦ったのは内緒です)。

参考サイト

qiita.com qiita.com

*1:スーパーユーザーでもいけますが、uucpで動かした方がお行儀が良いでしょう。

プロパティのアクセスコントロール

Swiftのプロパティで、「getterは外部に公開したいけれど、setterはプライベートにしたい。けれどもcomputed propertyを使って値を保持する変数をわざわざ別に持つのは面倒だ」というケースはよくあります。

そのような場合、「private(set)/internal(set)」のようなアクセスレベル修飾子を変数宣言時に用います。たとえば

private(set) var foo: Int

というように記述すると、「getterはinternal、setterはprivate」というように、getter/setterでアクセス可能な範囲に変化をつけられます。「The Swift Programming Language (Swift 2.1)」の「Language Reference - Declarations」によると、アクセスレベル修飾子には次の3種類があるようです。

  • access-level-modifier → internal internal(set) ‌- access-level-modifier → private private(set) ‌- access-level-modifier → public public(set)

publicまで用意されているというのは面白いですね。使うかどうかはともかく、getterの方がアクセスレベルが緩くなるようできますので。

RaspberryPI(Jessie環境)で固定IPアドレスを設定する

RaspberryPIにJessieを入れたところ、どれだけ/etc/network/interfacesの設定を変更しても効果がなかったので、メモ。

これまでのWheezyだと前述の/etc/network/interfacesを編集すればよかったのですが、Jessieでは同ファイルのコメントに

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

とあり、今回から(今回だけ?)は/etc/dhcpcd.confで設定することになったようです。man dhcpcd.confのstatic項に載っている、固定IPでの設定方法は以下のとおり。

interface eth0
  static ip_address=192.168.0.10/24
  static routers=192.168.0.1
  static domain_name_servers=192.168.0.1

ということで、同様の記述を/etc/dhcpcd.confの末尾に追記してみたところ、無事に指定したアドレスで動くようになりました。

環境

$ uname -a
Linux raspberrypi 4.1.13+ #826 PREEMPT Fri Nov 13 20:13:22 GMT 2015 armv6l GNU/Linux

$ hostnamectl
   Static hostname: ******
         Icon name: computer
           Chassis: n/a
        Machine ID: ********
           Boot ID: ********
  Operating System: Raspbian GNU/Linux 8 (jessie)
            Kernel: Linux 4.1.13+
      Architecture: arm

参考

SKViewのバグ?

短いついでに、今日はまった面倒なバグも残しておきます。

SKViewshowsFieldsプロパティは、マニュアルによると物理フィールドのデバッグ情報を出力できるとのこと。

showsFields Property
A Boolean value that indicates whether the view displays information about physics fields in the scene.

var showsFields: Bool

Discussion
When this debugging option is enabled, each time a frame is rendered, an image is drawn behind your scene that shows the effects of any physics fields contained in the scene.

ところがこのプロパティをtrueにして実行すると、猛烈な勢いでメモリを消費してしまい、最終的にはメモリ不足でアプリが落ちてしまっていました。Instrumentsで見てみると、メモリリークは検知されなかったもののMetal周りでフレーム更新のたびにヒープを消費しまくっているところまで確認できました。

どうやっても回避できず、しばらくはshowsFieldsの使用を諦めるしかなさそうです。残念。

ちなみにiOS9.2、Xcode 7.2(7C68)で確認しました。将来のバージョンでは直っているかな?

iOS SDKで利用できるフォント名一覧を取得するスニペット

今回はちょっと短め。

UIFontのメソッドを使って次のように書くことで、実行環境にインストールされている(&利用できる)フォント名の一覧を取得できます。

SKLabelNode(fontNamed: ... )と書いてはみたものの、さて何を指定すればよかったんだっけ?と困ったときに役立ちます。

for family in UIFont.familyNames() {
    print("Font family name: \(family)")
    for fontName in UIFont.fontNamesForFamilyName(family) {
        print("    > Font name: \(fontName)")
    }
}

参考サイト

stackoverflow.com

参考:iOS9.2での取得結果

ついでに、iOS9.2の実機上で上記ソースを実行した結果を以下に残しておきます。

続きを読む

flatMap()の前にflatten()を調べる(予想外に深かった)

Swiftを理解するための勉強中です。

前回、前々回でmap()を読んだので、次はflatMap()…といきたいところですが、flatMap()はmap+flattenなので、先にflattenの実装を読むことにします。

swiftlife.hatenablog.jp swiftlife.hatenablog.jp

flatten()メソッドは、Rubyflattenflatten!と同様、配列の配列を平坦化するためのメソッドです。Swiftだと、自身の要素を繋いだ(concatenate)ものと説明されています。flatten()が使用できるのは、 CollectionType,SequenceType,LazyCollectionType,LazySequenceTypeの4種類です。

lazy系は後日きちんと調べるので、今回はCollectionTypeのみ見てみましょう。すると、CollectionTypeにおけるflattenは、extensionで次のように実装されていました。

extension CollectionType where Generator.Element : CollectionType {
    /// A concatenation of the elements of `self`.
    @warn_unused_result
    public func flatten() -> FlattenCollection<Self>
}

extension CollectionType where Generator.Element : CollectionType, Index : BidirectionalIndexType, Generator.Element.Index : BidirectionalIndexType {
    /// A concatenation of the elements of `self`.
    @warn_unused_result
    public func flatten() -> FlattenBidirectionalCollection<Self>
}

おや、てっきりArrayを返すのかと思いきや、「FlattenCollection」「FlattenBidirectionalCollection」という初めて見る型を返しています。これらの型はいったい何でしょうか?ひとまずマニュアルを読んでみます。

struct FlattenCollection<Base : CollectionType where Base.Generator.Element : CollectionType> { ... }

struct FlattenBidirectionalCollection<Base : CollectionType where Base.Generator.Element : CollectionType, Base.Index : BidirectionalIndexType, Base.Generator.Element.Index : BidirectionalIndexType> { ... }

A flattened view of a base collection-of-collections.

The elements of this view are a concatenation of the elements of each collection in the base.

The flatten property is always lazy, but does not implicitly confer laziness on algorithms applied to its result. In other words, for ordinary collections c:

  • c.flatten() does not create new storage
  • c.flatten().map(f) maps eagerly and returns a new array
  • c.lazy.flatten().map(f) maps lazily and returns a LazyMapCollection

どちらの型にも同じ説明が書かれていて、「元コレクションを平坦化したときのビュー」(意訳)とあります。ふむふむ、これらは遅延評価されることが前提の型で、あくまで元のコレクションに対するビューポイント(視点)を提供するのみだと。その代わりに、実際に使われるまでは余計なメモリを消費しないと。どちらもCollectionTypeのサブクラスなのですね。

実装はファイルstdlib/public/core/Flatten.swift.gybにあるので、続いてこちらを見てみることにします。flattenはgybファイルを使い汎化された内容で書かれているので、直接のソースコードとしては出てきませんが、たぶんこれがCollectionTypeのflatten()メソッドと思われる記述がありました。次のソースです。

extension CollectionType where ${constraints % {'Base': ''}} {
  /// A concatenation of the elements of `self`.
  @warn_unused_result
  public func flatten() -> ${Collection}<Self> {
    return ${Collection}(self)
  }
}

自分自身(CollectionType)を元に${Collection}、つまりFlattenCollectionやFlattenBidirectionalCollectionをインスタンス化しています。なんとたったこれだけでした。${Collection}が指すイニシャライザでも、_baseプロパティにCollectionTypeオブジェクトを代入しているだけです。予想外の何もしてなさにびっくりしてしまいましたが、どうやらflatten()の秘密は、このFlattenCollection/FlattenBidirectionalCollection型が持つメソッドにあるようです。

FlattenCollection/FlattenBidirectionalCollectionが持つ、init()以外のメソッドは次の5種類です。

  • public func generate() -> FlattenGenerator<Base.Generator>
  • public var startIndex: FlattenCollectionIndex<Base> or FlattenBidirectionalCollectionIndex<Base>
  • public var endIndex: FlattenCollectionIndex<Base> or FlattenBidirectionalCollectionIndex<Base>
  • public subscript (position: FlattenCollectionIndex<Base> or FlattenBidirectionalCollectionIndex<Base>) -> Base.Generator.Element.Generator.Element
  • public func underestimateCount() -> Int

それぞれを読んでみたら(どれもstdlib/public/core/Flatten.swift.gybです)、ようやくflatten()の正体が見えてきました。

まずgenerate()の実装はreturn FlattenGenerator(_base.generate())とあり、FlattenGenerator型のオブジェクトを作成、返しています。このFlattenGeneratorはnext()メソッドのみ持っていて、ここで平坦化を実施しています。

startIndex()とendIndex()は、共にFlattenCollectionIndexまたはFlattenBidirectionalCollectionIndexを返し、そのsuccessor()/predecessor()メソッドによって平坦化した要素を返します。

そしてsubscriptも同じです。平坦化した状態でのpositionに相当する要素を返しています。なるほど、利用される可能性のあるメソッドすべてで、平坦化したときの要素を返すよう外堀を固めているのですね。もしflatten()を直ちに評価している場合、配列の要素をその場で全コピーすることになるので、コストは高くつきそうです。それにくらべてflatten()はこうして遅延評価を実現し、コストを最小限に抑えているんですね、おもしろいです。

ただ1点、underestimateCount()メソッドだけが次のような実装になっているのはまだ完全に理解できていません。

public func underestimateCount() -> Int {
  // To return any estimate of the number of elements, we have to start
  // evaluating the collections.  That is a bad default for `flatMap()`, so
  // just return zero.
  return 0
}

意訳すると「すべてを評価して数を返すというunderestimateCount()の振る舞いは、flatMap()にとっては都合が悪いので、常に0を返す」となりますが、逆にこの振る舞いで問題になったりしないのでしょうか?遅延評価のためだとはいえ、謎です…。

ともあれ、以上でflatten()の振る舞いをおおよそ把握することができました。はじめは単に平坦化した配列を返すだけと思っていたのですが、蓋を開けてみると別のオブジェクトを返しているだけで、遅延評価の外堀によって実行コストを抑える仕組みが整っているだけでした。とても面白いメソッドでした。

遅延評価、自分ももっと使えるようになりたいです。

参考サイト

CollectionType,SequenceTypeのmap()

前回Optional型のmap()を読んだのに続いて、今回はCollectionTypeとSequenceTypeのmap()を見てみることにします。 プロトコルの定義は次のとおりです。

func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]

これらはstdlib/public/coreディレクトリ下にCollection.swiftとSequence.swiftとで実装されています。なおmap()の実装はSequenceWrapper.swiftにもあるのですが、これは中でCollectionType,SequenceTypeのmap()を呼んでいるだけなので、今回は無視します。

CollectionTypeのmap()

ではまず、CollectionTypeから見てみます。

// CollectionType
@warn_unused_result
public func map<T>(
  @noescape transform: (Generator.Element) throws -> T
) rethrows -> [T] {
  let count: Int = numericCast(self.count)
  if count == 0 {
    return []
  }

  var result = ContiguousArray<T>()
  result.reserveCapacity(count)

  var i = self.startIndex

  for _ in 0..<count {
    result.append(try transform(self[i]))
    i = i.successor()
  }

  _expectEnd(i, self)
  return Array(result)
}

最初に要素数を数え、0だった場合には空の配列を返します。

0でなければ、型TのContiguousArrayを用意して、reserveCapacity()で要素数と同じサイズで確保します。なるほど、最初に確保しておけば再確保のコストを下げられますね。その後、自身の先頭から順に要素を取り出し、引数のtransformに渡します。その結果を配列に格納していきます。そしてArrayとして呼び側に戻しています。

ところで、return直前にある「_expectEnd(i, self)」とはいったい何でしょう。最終要素のインデックス番号と、自分自身を渡しています。coreフォルダ内を探してみると、Arrays.swift.gybファイルで次のように定義されていました。どうやらデバッグ用の処理のようで、iと自身の最終要素のインデックス番号とが同じかどうかをチェックしていました。

/// A _debugPrecondition check that `i` has exactly reached the end of
/// `s`.  This test is never used to ensure memory safety; that is
/// always guaranteed by measuring `s` once and re-using that value.
internal func _expectEnd<C : CollectionType>(i: C.Index, _ s: C) {
  _debugPrecondition(
    i == s.endIndex,
      "invalid CollectionType: count differed in successive traversals"
    )
}

ちなみに「〜.swift.gyb」というファイルはCの#defineマクロのようなプリプロセッサでのテンプレートを実現するファイルで、Swiftの開発チームによって使われているファイルとのこと。utils/gyb.pyを使ってコンパイル前にソースコードへ適用されるようです。

GYB: Generate Your Boilerplate (improved names welcome; at least 3 # this one's short). See -h output for instructions

GYBって「Generate Your Boilerplate」のことなのですね。

SequenceTypeのmap()

次はSequenceTypeのmapです。こちらはSequence.swiftに実装があります。

// SequenceType
@warn_unused_result
public func map<T>(
  @noescape transform: (Generator.Element) throws -> T
) rethrows -> [T] {
  let initialCapacity = underestimateCount()
  var result = ContiguousArray<T>()
  result.reserveCapacity(initialCapacity)

  var generator = generate()

  // Add elements up to the initial capacity without checking for regrowth.
  for _ in 0..<initialCapacity {
    result.append(try transform(generator.next()!))
  }
  // Add remaining elements, if any.
  while let element = generator.next() {
    result.append(try transform(element))
  }
  return Array(result)
}

基本的な処理の流れは同じですが、CollectionTypeとは細かいところで少しずつ違っています。要素数をカウントする処理でunderestimateCount()を使っていて、CollectionTypeのcountプロパティとは異なります。SequenceTypeはcountプロパティを持たず順繰りに辿るしかないためですが、配列の再確保コストを考えると、O(N)でもunderestimateCount()を使っているのでしょう。

あれ、でもinitialCapacity==0だったとしてもそのまま処理を続けていますね。空なら空配列を返した方が良さそうですが、なぜなんでしょう??

その後、generate()メソッドイテレータを取り出し、順番に「Unwrapしながら」transform()に渡しています。その後、もしまだnext()で取り出せるようなら、配列が再確保されるのを覚悟で追加しています。

・・・ここも疑問の残るコードです。はじめからwhileとOptional Bindingを使えばいいように思うのですが、この方が処理が速いのでしょうか。この記事にあるBenchmarkクラスを使って確かめてみます。

for num in [10, 100, 1000, 10000, 100000, 1000000, 10000000] {
    var src_array = [Int?]()
    for i in 0..<num {
        src_array.append(i)
    }

    var dst_array = ContiguousArray<Int>()
    dst_array.reserveCapacity(num)

    // 1) forによる実行
    var generator = src_array.generate()
    Benchmark.measure("for with \(num)") {
        for i in 0..<num {
            dst_array.append(generator.next()!!)
        }
    }

    dst_array.removeAll(keepCapacity:true)

    // 2) whileによる実行
    generator = src_array.generate()
    Benchmark.measure("while with \(num)") {
        while let v = generator.next() {
            dst_array.append(v!)
        }
    }
}

結果は次のとおり。コンパイル時の最適化レベルはデフォルトのままです。

素数 (1)for (2)while
10 0.000(s) 0.000(s)
100 0.000(s) 0.000(s)
1000 0.000(s) 0.000(s)
10000 0.004(s) 0.004(s)
100000 0.027(s) 0.026(s)
1000000 0.252(s) 0.260(s)
10000000 2.550(s) 2.507(s)

…うーん、同じですね。最適化レベルを「-Ounchecked」にしても試してみたのですが、むしろforの方が遅い結果になっています。何なんでしょう、「昔のバージョンは違った」とかあるのかもしれません。

疑問の残る結果でしたが、ソースから実装を確認できました。

まとめ

今回の気づいた点をまとめます。

  • CollectionTypeとSequenceTypeでは実装が異なる
  • ArrayはContiguousArray+reserveCapacity()で先に領域確保
  • SequenceTypeの実装は疑問の残る内容
  • Swift開発には.swift.gybファイルによるプリプロセッサ(もどき?)を使っている

C/C++のマクロって、なんだかんだ言われてもやっぱり便利なんだなと思った次第です。

参考サイト