USBシリアル変換ケーブル(SRC06-USB)をOS Xで使う
USBシリアル変換ケーブルに、バッファローの「SRC06USB・06USM」という製品を使うことになったのですが、あいにくバッファローのダウンロードページにはOS X向けのドライバーがありません。
そこで、Future Technology DevicesのVirtual COM Port Driverを使い、OS Xでも使えるように設定しました。OS X El Capitan(10.11.2)で確認しました。
ドライバのダウンロード
まず、Future Technology DevicesのVirtual COM Port Driversのページから、「Mac OS X 10.9 and above」にあるドライバーをダウンロードします。
ドライバのインストール
.pkgファイルがディスクイメージに含まれているので、ダブルクリックしてインストールします。
インストールが終われば、以降は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を見れば載っているのですが、最初は見ないまま始めてしまい、シェルに戻せなくなって焦ったのは内緒です)。
参考サイト
*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のバグ?
短いついでに、今日はまった面倒なバグも残しておきます。
SKView
のshowsFields
プロパティは、マニュアルによると物理フィールドのデバッグ情報を出力できるとのこと。
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)") } }
参考サイト
参考:iOS9.2での取得結果
ついでに、iOS9.2の実機上で上記ソースを実行した結果を以下に残しておきます。
続きを読むflatMap()の前にflatten()を調べる(予想外に深かった)
Swiftを理解するための勉強中です。
前回、前々回でmap()
を読んだので、次はflatMap()
…といきたいところですが、flatMap()はmap+flattenなので、先にflatten
の実装を読むことにします。
swiftlife.hatenablog.jp swiftlife.hatenablog.jp
flatten()
メソッドは、Rubyのflatten
、flatten!
と同様、配列の配列を平坦化するためのメソッドです。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 storagec.flatten().map(f)
maps eagerly and returns a new arrayc.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++のマクロって、なんだかんだ言われてもやっぱり便利なんだなと思った次第です。
参考サイト
- [swift-users] What are .swift.gyb files?
- 「.swift.gybファイルとは何か?」について、ずばり開発チームからの回答です。