tag:blogger.com,1999:blog-40683601048827845482024-03-14T16:12:26.849+09:00SKIPBITSKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.comBlogger9125tag:blogger.com,1999:blog-4068360104882784548.post-5842526068104410022015-05-08T11:18:00.000+09:002015-05-08T11:35:04.425+09:00[OS X] 写真.app ではカメラロールの削除ができない<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-SSdnfNkD0ug/VUwVNdRe73I/AAAAAAAAAFA/Y0jLKEykqV8/s1600/photo-app.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://2.bp.blogspot.com/-SSdnfNkD0ug/VUwVNdRe73I/AAAAAAAAAFA/Y0jLKEykqV8/s320/photo-app.png" width="320" /></a></div>
<br />
OS X 10.10.3 から登場した iPhoto 置き換えの 写真.app。<br />
<br />
操作感は iOS のそれに合わせてあり動作は iPhoto より軽快。iPhoto 依存機能を使い倒していたワケでもないので、このアップデートは歓迎するところだが。。<br />
<br />
iPhoto 時代はできていた「Mac に読み込み済みの写真を iPhone のカメラロールから削除」ができなくなった。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.google.co.jp/imgres?imgurl=http://gogohack.com/images/2015/02/bb12b211e6dd693fa91ed74711bc2b00.png&imgrefurl=http://gogohack.com/636&h=656&w=1000&tbnid=rtL8Ck6J1QrIWM:&zoom=1&docid=BKPipfVcNKIm-M&ei=dQpMVffADOa7mQXpzoHwCA&tbm=isch&ved=0CCMQMygIMAg"><img border="0" height="138" src="http://2.bp.blogspot.com/-JpBhXH6yA8k/VUwYSwEaZkI/AAAAAAAAAFU/mYobjRIfcRk/s320/iphoto-import.png" width="320" /></a></div>
<div style="text-align: center;">
↑コレです。移行済みでキャプチャ取れず、<a href="http://www.google.co.jp/imgres?imgurl=http://gogohack.com/images/2015/02/bb12b211e6dd693fa91ed74711bc2b00.png&imgrefurl=http://gogohack.com/636&h=656&w=1000&tbnid=rtL8Ck6J1QrIWM:&zoom=1&docid=BKPipfVcNKIm-M&ei=dQpMVffADOa7mQXpzoHwCA&tbm=isch&ved=0CCMQMygIMAg">画像検索</a>させていただいた。</div>
<br />
<br />
iPhone で撮影した画像は自動的にフォトストリームにアップロードされ、Mac 側で iPhoto を起動すればフォトストリームの画像は自動的に iPhoto ライブラリに追加されていたので、iPhone の容量節約のためにカメラロール内に残った取り込み済み画像は削除していた。<br />
<br />
つまりこんな使い方をしていた。<br />
<br />
<ol>
<li>iPhone で写真を撮影</li>
<li>自動でフォトストリームにアップロードされる</li>
<li>Mac で iPhoto.app をすると、フォトストリームの新しい写真は Mac にコピーされる</li>
<li>iPhoto で「すでに Mac に読み込まれたカメラロールの写真」を削除</li>
<li>必要な写真は iTunes 経由で改めて iPhone へ同期</li>
</ol>
<br />
新しい 写真.app では「4.」ができない。<br />
<br />
もしかして 写真.app は iPhoto と違ってフォトストリームの画像をライブラリにコピーしないのか? 読み込み済み写真もあえて取り込まないと、フォトストリームから追い出された時にその写真は消えてしまうのか?<br />
<br />
念のため iPhone でダミーの写真を撮影し、フォトストリーム経由で Mac 側 写真.app のモーメント(って言うのかな? 写真タブのトコロ)に表示されたのを確認したのち、その画像を削除してみたところ、フォトストリームとカメラロールからは削除されたが Mac には残った。<br />
<br />
つまりフォトストリーム上の写真が自動で Mac にコピーされる(フォトライブラリに恒久的に残る)挙動は iPhoto と同じ。<br />
<br />
<br />
写真.app でも「読み込み後に項目を削除」というチェックボックスはあるけど、フォトストリームの画像はすでに取り込み済み。あえて取り込み済み画像を選択してこのチェックをオンで読み込めば削除はされるけど、フォトライブラリは当然重複画像だらけになる。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-1AQZjO7aGbo/VUwX3xGzakI/AAAAAAAAAFM/WRBR8IChoh8/s1600/photo-app-import.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="206" src="http://2.bp.blogspot.com/-1AQZjO7aGbo/VUwX3xGzakI/AAAAAAAAAFM/WRBR8IChoh8/s320/photo-app-import.png" width="320" /></a></div>
<br />
動画はフォトストリーム経由で取り込めないので、「読み込み後に項目を削除」が有意な働きをするのは動画だけ。<br />
<br />
写真.app で出来なければ、イメージキャプチャで削除するまでだが。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-Q8oCBOPDH_4/VUwUNYOykTI/AAAAAAAAAE4/90SSn_4Bopw/s1600/image-capture.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="http://4.bp.blogspot.com/-Q8oCBOPDH_4/VUwUNYOykTI/AAAAAAAAAE4/90SSn_4Bopw/s320/image-capture.png" width="320" /></a></div>
<span id="goog_1285389433"></span><span id="goog_1285389434"></span><br />
<br />
なぜ無くなったのだろう?<br />
<br />
地味に面倒なのだが。<br />
<br />SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com1tag:blogger.com,1999:blog-4068360104882784548.post-37430282161586096092015-05-02T07:33:00.000+09:002015-05-02T07:43:33.349+09:00[Swift] NSApp で学ぶ AnyObject<h3>発端</h3>
<br />
とあるコードを書いていた時、以下の現象に遭遇した。
<pre><code class="swift">NSApp.active // 呼べない(error)
NSApp.activationPolicy // 呼べる(success)
NSApp.activationPolicy() // 呼べない(error)</code></pre>
はて。<br />
<br />
NSApplication クラスに active プロパティは存在するのに呼び出しエラーが発生。<br />
activationPolicy() メソッドに至っては呼び出し可能なものの関数呼び出しの () を付けられない始末。<br />
<br />
<br />
<h3>宣言を調べてみる</h3>
<br />
想定どおり active はプロパティ(computed-property)として宣言されているし、activationPolicy() はメソッドとして宣言されている。
<pre><code class="swift">var NSApp: AnyObject!
// … 中略 …
class NSApplication : NSResponder, … {
// … 中略 …
var active: Bool { get }
// … 中略 …
func activationPolicy() -> NSApplicationActivationPolicy
// … 中略 …
}</code></pre>
当然、以下のように明示的な NSApplication 型であれば定義どおりの動作となる。
<pre><code class="swift">let app: NSApplication = NSApplication.sharedApplication()
app.active // OK
app.activationPolicy // NG … というか Function 型が返る
app.activationPolicy() // OK
</code></pre>
ということは NSApplication 型ではなく AnyObject! 型になっている NSApp 変数を使用したために、この現象が起きたのは明白だ。<br />
<br />
<br />
<h3>疑問</h3>
<br />
気になるのは2つ。
<ul>
<li>なぜ AnyObject! に対する activationPolicy の呼び出しは成功するのに active 呼び出しは失敗するのか</li>
<li>なぜ AnyObject! に対する activationPolicy の呼び出しはメソッドであるにもかかわらず () をつける事ができないのか</li>
</ul>
<br />
<br />
<h3>Swift の AnyObject と Objective-C の id は少し違う</h3>
<br />
NSApp は Objective-C の場合 id 型で宣言されている。その NSApp が Swift で対応するクラスなんでも型の AnyObject が使われているのは納得がいく。<br />
<br />
Objective-C の id 型なら、中身が何であろうと(宣言さえ存在していれば)どんなメッセージでも送る事ができる。<br />
<br />
しかし Swift の AnyObject は、中身のインスタンスの正しいメンバ呼び出しでも失敗する。
<pre><code class="swift">class Foo {
var myProperty = "Foo's property"
func myFunction() { println("Foo's function") }
}
var foo: AnyObject = Foo()
foo.myProperty // error: 'AnyObject' does not have a member named 'myProperty'
foo.myFunction() // error: 'AnyObject' does not have a member named 'myFunction'
</code></pre>
Swift の AnyObject は ObjC の id のような汎用インスタンス参照として用意されたものだが、完全に型安全だ。本来の型にキャストすることなしにメンバを呼び出すことはできない。<br />
<br />
ObjC で id 型を扱った時に発生しやすいランタイムエラーを避けることができるものの、動的呼び出しのポテンシャルも損なっている。<br />
<br />
<br />
<h3>AnyObject に対するメソッド呼び出しが成功したワケ</h3>
<br />
では NSApp が AnyObject であるのに NSApplication メンバを呼び出せるのはどういうワケかというと、Swift には AnyObject 型を ObjC の id 型と互換性を取るための仕組みがある。<br />
<p><blockquote style="margin: 5px; padding: 8px; border-left: 3px dotted #d4d4d4;">
You can also can any Objective-C method and access any property without casting to more specific class type. This includes Objective-C compatible methods marked with the @objc attribute.
<p style="text-align: right;"><cite><a href="https://developer.apple.com/library/mac/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html">Using Swift with Cocoa and Objective-C</a></cite></p>
</blockquote></p>
Objective-C 互換クラスを作成するための属性 @objc を付けたクラスならば、一旦 Objective-C クラスにブリッジされるため id 型と同等の働きとなる。
ObjC 由来のクラス(NSObject)を継承しているクラスならば、すでに @objc 属性が付いたものとして扱われる。<br />
<br />
つまり NSApp(AnyObject)の実体である NSApplication は ObjC 由来のクラスなので @objc 属性付きであるため、メンバ呼び出しが ObjC のそれと同等となった。<br />
AnyObject である NSApp に対して activationPolicy が呼び出せたのは、この機能のおかげだ。<br />
<br />
<br />
<h3>AnyObject に対するプロパティ呼び出しが失敗したワケ</h3>
<br />
次の例を見れば分かる通り、本来なら @objc 属性を付けたクラスならば AnyObject 型に対してプロパティ呼び出しも成功するはず。
<pre><code class="swift">@objc class Foo {
var myProperty: String = "property"
}
var foo: AnyObject = Foo()
foo.myProperty // success</code></pre>
にも関わらず、NSApp.active 呼び出しは失敗した。なぜか。<br />
<br />
実は active の他にも NSApp という AnyObject に対して、呼び出しが失敗するプロパティは幾つかあったが、逆に成功するプロパティもあった。<br />
どうやら Swift と ObjC での宣言の違いが原因のようだ。<br />
<pre><table>
<tr><th>Swift 宣言</th><th>ObjC 宣言</th><th>AnyObject での呼び出し可否</th></tr>
<tr><td><code class="swift">unowned(unsafe) var mainWindow: NSWindow? { get }</code></td><td><code class="objectivec">@property (readonly, assign) NSWindow *mainWindow;</code></td><td style="text-align: center;">OK</td></tr>
<tr><td><code class="swift">unowned(unsafe) var keyWindow: NSWindow? { get }</code></td><td><code class="objectivec">@property (readonly, assign) NSWindow *keyWindow;</code></td><td style="text-align: center;">OK</td></tr>
<tr><td><code class="swift">var active: Bool { get }</code></td><td><code class="objectivec">@property (getter=isActive, readonly) BOOL active;</code></td><td style="text-align: center;">NG</td></tr>
<tr><td><code class="swift">var hidden: Bool { get }</code></td><td><code class="objectivec">@property (getter=isHidden, readonly) BOOL hidden;</code></td><td style="text-align: center;">NG</td></tr>
</table></pre>
成功する呼び出し(mainWindow, keyWindow)は Swift / ObjC ともに宣言が一致している。逆に失敗する呼び出し(active, hidden)は、ObjC で getter に別名が付けられている。<br />
<br />
要するに Swift 側の宣言と ObjC 側の宣言でプロパティ名が一致していれば AnyObject に対して呼び出し可能となる。逆に active と isActive のように宣言が一致していなければ Swift 側の宣言で呼び出す事ができない。<br />
<br />
@objc 属性がついた AnyObjectは ObjC ブリッジのため ObjC API でメンバを探すらしい。なので NSApp で active や hidden を呼び出したいのなら
<pre><code class="swift">NSApp.isActive // success
NSApp.isHidden // success
</code></pre>
上記のように ObjC の定義を使えば成功するのであった。<br />
<br />
<br />
<h3>AnyObject に対するメソッド呼び出しに () が付けられなかったワケ</h3>
<br />
activationPolicty 呼び出しに () が付けられなかったのも同様の問題だろうと思われる。<br />
<br />
Objective-C 側では
<pre><code class="objectivec">- (NSApplicationActivationPolicy) activationPolicy NS_AVAILABLE_MAC(10_6);</code></pre>
という宣言で、メソッドではあるけども KVC 準拠なので ObjC 側でも呼び出しはプロパティ扱いになる。
この ObjC 宣言にマッチして呼び出しが発生するので、「プロパティに () を付けるな」というエラーになっていたのだろう。<br />
<br />
@objc な AnyObject ではこういう事が起きてしまうらしい。<br />
<br />
<br />
<h3>結論</h3>
<ul>
<li>中身が @objc なクラスのインスタンスであれば AnyObject の実体に対するメンバ呼び出しはアリ</li>
<li>@objc な AnyObject の中身が Swift / ObjC 両方に定義があるクラスのインスタンスでは、ObjC 側の定義で呼び出す</li>
</ul>
<br />
<h3>参考</h3>
<ul>
<li><a href="https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html">The Swift Programming Language</a></li>
<li><a href="https://developer.apple.com/library/mac/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html">Using Swift with Cocoa and Objective-C</a></li>
</ul>
SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com0tag:blogger.com,1999:blog-4068360104882784548.post-14355323864806751682014-08-21T10:57:00.000+09:002014-08-21T10:57:14.661+09:00[Swift] 関数引数としてのタプル以前書いた「<a href="http://blog.skipbit.jp/2014/07/swift-curried-functions.html">カリー化関数の話</a>」という記事の後半で<br />
<blockquote class="tr_bq">
言われてみれば確かに「関数の引数定義」は「タプルの名前付き定義」の書き方と同じ。<br />
() は、もしかして「結合順序」と言うより、タプルの境界を定義するものと考えた方が良いのかも、しれない。</blockquote>
などと書いていたのだが、ようやくこれを試してみた(Xcode6-Beta6)。<br />
<br />
例えば以下のような関数を定義したとする。<br />
<pre><code class="swift">func f(a: Int, b: Int) {
println("a + b = \(a + b)")
}
</code></pre>
通常は次のように使う。<br />
<pre><code class="swift">f(1, 2)</code></pre>
こうなると (1, 2) はタプルに見えてくるだろうと言うことで、タプルそのものを渡せるのか。結論から言うと渡すことが出来た。<br />
<pre><code class="swift">let v = (1, 2)
f(v)
</code></pre>
これはかなりナイス。<br />
<br />
名前付き引数を要求されている場合なら、名前付きタプルで OK。
<pre><code class="swift">func f(#a: Int, #b: Int) {
println("#a + #b = \(a + b)")
}
let v = (a: 1, b: 2)
f(v)
</code></pre>
あちこちで使うようなテクニックでは無いにしろ、使えるのは便利だし、関数の引数定義はタプルを拡張したものであるようなことが伺える。<br />
<br />
しかしいくつかの制限も見られた。<br />
<br />
<br />
まず、引数に渡せるタプルは let 宣言に限られていて、var 宣言のタプルはエラーになった。<br />
しかしこれはキャストすれば回避可能であった。
<pre><code class="swift">func f1(a: Int, b: Int) {
println("a + b = \(a + b)")
}
var v1 = (1, 2)
// f(v1) // error: Missing argument for parameter #2 in call
f(v1 as (Int, Int)) // success
</code></pre>
名前付き引数なら、当然名前付きタプルでキャストすれば良い。<br />
<pre><code class="swift">func f2(#a: Int, #b: Int) {
println("#a + #b = \(a + b)")
}
var v2 = (a: 1, b: 2)
// f(v2) // error: Missing argument for parameter 'b' in call
f(v2 as (a: Int, b: Int)) // success
</code></pre>
<br />
次に、引数が1つの場合は名前付きタプルは文法上渡せなかった。<br />
<pre><code class="swift">func f3(#a: Int) {
println("#a = \(a)")
}
let v3 = (a: 1)
// f3(v3) // error: Missing argument label 'a:' in call
// f3(v3 as (a: Int)) // error: Cannot create a single-element tuple with an element label
let v4: (a: Int) = (a: 1) // error: Cannot create a single-element tuple with an element label
</code></pre>
Beta6 からは単一要素のタプルには名前がつけられなくなっている。<br />
上記の例では v3.a にはアクセスできない。<br />
<br />
元々、(Int) という宣言は、単なる Int 型として見られていたので、これが Beta6 でより強化されたのだろう<a href="#note-1"><sup>*1</sup></a>。<br />
<br />
名前付き引数でなければ問題はないし、単一要素をわざわざタプル化しないだろうから現実的に問題ではないと言って良い。<br />
<br />
<br />
「関数引数 ≒ タプル」と見た場合、総合的にはまぁ想定の範囲内の結果。<br />
気になるのは var タプルの場合のキャストくらい。しかしこれも正式版までにはまた変わりそうな気もする。<br />
<br />
<div>
...<br />
<a name="note-1">*1</a>: 余談だが Beta5 までなら上記の f3(v3 as (a: Int)) と f3(v4) は成功を確認している。<br />
</div>SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com2tag:blogger.com,1999:blog-4068360104882784548.post-33664529437541366722014-08-07T12:08:00.000+09:002014-08-07T12:08:08.412+09:00[Swift] コマンドライン(Command Line)プログラムとして使う [2] 外部モジュールの作成と使用<a href="http://blog.skipbit.jp/2014/08/swift-command-line-1.html">前回</a>の続き。<br />
思ってたより全然面倒な話だったので、試行過程も記録しておく。<br />
<br />
<br />
さて。なぜ使い回すソースコードをモジュール化するかというと、Objective-C と違い Swift の import はヘッダファイルではなくモジュールだから、ということになるだろう。<br />
<br />
<h3>
モジュールを作る</h3>
<br />
とりあえず作ってみる。<br />
xcrun swift -help を眺めると、モジュール化できそうなオプションがいくつか見つかる。<br />
<br />
まずはモジュール用のサブディレクトリを作り、モジュール用のソースコードを以下のように書いた。<br />
<pre><code>/* modules/Example.swift: モジュールとして使われる方 */
public func MyFunction(name: String) -> String {
return "Hello, \(name)"
}
public class Example {
public let name: String
public init(name n: String) {
name = n
}
}
</code></pre>
外から参照できるようにすべて public にした。そして以下のモジュール化コマンドを実行。<br />
<pre><code>% /Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift \
-frontend \
-emit-module \
-module-name Example \
-emit-module-path modules/Example.swiftmodule \
-emit-module-doc-path modules/Example.swiftdoc \
modules/Example.swift
</code></pre>
modules/ に Example.swiftmodule と Example.swiftdoc が作成される。<br />
<br />
次はモジュールを使う側のファイルを(前回からの続きの command-6.swift として)作成。<br />
<pre><code>#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift <span style="color: #ee3333;">-I ./modules</span>
import Example
println(MyFunction("Module"))
var module = Example(name: "Command Line")
println("Hello, \(module.name)")
</code></pre>
-I オプションでモジュールの場所を伝える。<br />
<br />
しかし、これを実行するとエラーとなる。<br />
<pre><code>% ./command-6.swift
LLVM ERROR: Program used external function '_TF7Example10MyFunctionFSSSS' which could not be resolved!
</code></pre>
import は成功してるけど、シンボルを解決できていない。<br />
<br />
swiftmodule の中には実行データが入ってないのか。思えば Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx には .swiftmodule、.swiftdoc の他 .dylib も一緒に入ってる。ということで dylib が必要なのだろう。<br />
<br />
<br />
<h3>
ライブラリを作る</h3>
<br />
ライブラリ化するため、今度はコンパイラ swiftc を使う。まずはオブジェクトファイルを作成。<br />
<pre><code>% /Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun <span style="color: #ee3333;">swiftc</span> \
-emit-library \
-emit-object modules/Example.swift \
-o modules/Example.o
</code></pre>
出来たオブジェクトファイル(modules/Example.o)から dylib を作成する。<br />
<pre><code>% /Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun libtool \
-macosx_version_min 10.9 \
-L/Applications/Xcode6-Beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
-dynamic \
-install_name @rpath/libExample.dylib \
-lswiftCore \
-lSystem \
-o modules/libExample.dylib \
modules/Example.o
</code></pre>
これでライブラリが modules/libExample.dylib として作成された。<br />
<br />
install_name オプションで @rpath を指定したのは executable 側が検索パスを決めるため。今回のケースでは dylib を別の場所で使うワケではないので影響しないが、.app に入れることも考えればあった方が良いオプション。<br />
<br />
作成した dylib を使用するように先の command-6.swift を修正する。<br />
<pre><code>#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift -I ./modules <span style="color: #ee3333;">-L ./modules -lExample</span>
import Example
println(MyFunction("Module"))
var module = Example(name: "Command Line")
println("Hello, \(module.name)")
</code></pre>
-L オプションでライブラリの場所を、-l でリンクするライブラリを指定した。<br />
<br />
これで実行できるかと思いきや、またもやエラーが。<br />
<pre><code>% ./command-6.swift
<unknown>:0: error: could not load shared library 'libExample'
</code></pre>
ライブラリを見つけられていない。<br />
<br />
これに小一時間ハマってしまったがなんのことはない、実行時カレントディレクトリに libExample.dylib があれば見つけてくれた。<br />
-L オプションはリンク時のライブラリの場所であって、実行時のライブラリの場所ではないってことだ。<br />
<br />
せっかくスクリプトっぽく使えると思ったのに(いちいちライブラリ化するのは我慢するとしても)ライブラリと同じディレクトリにいないと実行できないとかよろしくない。<br />
<br />
実行時ライブラリ検索は LD_RUNPATH_SEARCH_PATHS だろうから、これを指定する方法を模索したのだが、残念ながらこれも結局 swiftc でビルドするしか無い、という結論に落ち着いた。<br />
<pre><code>% sudo xcode-select -switch /Applications/Xcode6-Beta5.app
% xcrun swiftc -I ./modules -L ./modules -lExample -Xlinker -rpath -Xlinker ./modules -o ./command-6 ./command-6.swift
% ./command-6
Hello, Module
Hello, Command Line
</code></pre>
実行成功。いやビルドしちゃってるから。当たり前すぎて感動がない、っていう。<br />
<br />
リンカオプションとして rpath を指定するのだけど、この時ばかりは xcode-select -switch しないとエラーになってしまった。<br />
<br />
中で使われているであろう xcodebuild がなんか解決できなくてエラーになっていたようなので、ビルドするのなら素直に -switch しときましょう、と。<br />
<br />
<br />
<h3>
まとめ</h3>
<br />
スクリプトのように書けると思われた Swift コマンドラインプログラムだがひとたび自作の外部モジュールを使うとなると、モジュールのライブラリ化が必要で、使う方は executable としてビルドするか、もしくはライブラリをカレントにコピーしないといけない。<br />
<br />
そういうものかと思う一方で、ここら辺に関しては本来 xcrun swift に良きに計らってもらいたいという気持ちもある。<br />
<br />
Xcode を使うことでこれらの手間を軽減できるかと思いきや、現状(Xcode6-Beta5)は Command Line Tool もモジュール関係もテンプレートがない。<br />
<br />
今のところコマンドラインは、そう言う手段がある、という程度が無難かも。<br />
正式版に期待。<br />
<br />
<br />
<h3>
参考</h3>
<ul>
<li><a href="https://developer.apple.com/swift/blog/?id=7" target="_blank">Files and Initialization - Swift Blog - Apple Developer</a></li>
<li><a href="http://practicalswift.com/2014/06/07/swift-scripts-how-to-write-small-command-line-scripts-in-swift/" target="_blank">Swift scripts: How to write small command line scripts in Swift | practical swift.com</a></li>
<li><a href="http://railsware.com/blog/2014/06/26/creation-of-pure-swift-module/" target="_blank">Creation of pure Swift module | Railsware Blog</a></li>
<li><a href="http://qiita.com/FUKUZAWA-Tadashi@github/items/5e276c17af76fef7567b" target="_blank">コマンドラインでswiftモジュールを作成、リンクする - Qiita</a></li>
</ul>
<br />
<br />SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com0tag:blogger.com,1999:blog-4068360104882784548.post-73205593453625975102014-08-06T10:32:00.000+09:002015-04-30T15:11:30.938+09:00[Swift] コマンドライン(Command Line)プログラムとして使う [1]そう言えばコマンドラインとして使える話があったので、今更試してみたメモ。<br />
<br />
コマンドラインツール群はコンパイラ等と同様にアプリケーションパッケージ内にある。<br />
通常は xcode-select -print-path で表示されている Xcode 内のツールが使われるが、現状ベータなので xcode-select -switch で変更せずに直接パスを叩く方針にする。<br />
<br />
<h3>
REPL(Read-Eval-Print-Loop)を使ってみる</h3>
<br />
まずは REPL を立ち上げてみる。<br />
<pre><code class="bash">% /Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift
Welcome to Swift! Type :help for assistance.
1>
</code></pre>
または以下でも同じものが立ち上がる。<br />
<pre><code class="bash">% /Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/lldb --repl</code></pre>
<pre><code class="bash">% /Applications/Xcode6-Beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift</code></pre>
終了するには Ctrl-D を押すか、REPL に :q (または :quit) とタイプする。<br />
<br />
一通りのことは出来るようだが、出来ることに若干の違いがあるような気がする。
<br />
<br />
<br />
<h3>
ファイルを実行する</h3>
<br />
コマンドラインなので、REPL よりもファイルから実行できた方が良い。<br />
以下のようなフツーの swift ファイルを書いて実行することが出来た。<br />
<pre><code class="swift">func hello(name: String) -> String {
return "Hello, \(name)"
}
println(hello("Swift"))
</code></pre>
<pre><code class="bash">% /Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift command-1.swift
Hello, Swift
</code></pre>
古いサンプルでは xcrun swift -i の指定があるものも残っているが、Beta5 から -i オプションは必要なくなっている。<br />
<br />
通常のアプリケーションコードと違うのは、コマンドラインや REPL の場合は Playground と同様に order-dependent なので定義順にしか解釈されないこと。<br />
アプリケーションコードであっても main.swift のようにトップレベルに実行可能コードが書けるものは order-dependent となるのだが、この辺の話は Apple Swift Blog の <a href="https://developer.apple.com/swift/blog/?id=7">Files and Initialization</a> に書いてある。<br />
<br />
当たり前だが REPL や Playground とは違い、コマンドラインでは標準出力に出力しなければ結果は表示できない。<br />
<br />
<br />
よりコマンドラインらしく、shebang を書いて実行権限をつければ単体で実行可能となる。<br />
<pre><code class="bash">#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift
func hello(name: String) -> String {
return "Hello, \(name)"
}
println(hello("Swift"))
</code></pre>
<pre><code class="bash">% chmod +x command-1.swift
% ./command-1.swift
Hello, Swift
</code></pre>
まるでスクリプトのように実行できる。<br />
<br />
<br />
<h3>
引数を取る</h3>
<br />
さて、コマンドラインとして使うなら引数が取れないと意味が無い。<br />
<br />
Foundation を使えば Cocoa の流儀で取得できる。<br />
<pre><code class="bash">#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift
import Foundation
let keyName = "keyName"
let args = NSUserDefaults.standardUserDefaults()
println("\(keyName): \(args.objectForKey(keyName))")
</code></pre>
<pre><code class="bash">% ./command-2.swift -keyName value
keyName: value
% ./command-2.swift -keyName '("a", "b")'
keyName: (
a,
b
)
% ./command-2.swift -keyName '{ "k1"=1; "k2"=2; }'
keyName: {
k1 = 1;
k2 = 2;
}
</code></pre>
Array や Dictionary の指定が Swift-Style ではなく ASCII Property List Style になってしまうのは NSUserDefaults の都合上、致し方ない。<br />
<br />
特定の引数を取得するのは NSUserDefaults で良いが、引数一覧を取得する場合は NSProcessInfo を使う。<br />
<pre><code class="bash">#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift
import Foundation
for arg in NSProcessInfo.processInfo().arguments {
println("\(arg)")
}
</code></pre>
<pre><code class="bash">% ./command-3.swift a b c
/Applications/Xcode6-Beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift
-frontend
-interpret
./command-3.swift
-enable-objc-attr-requires-objc-module
-target
x86_64-apple-darwin13.3.0
-target-cpu
core2
-module-name
main
-sdk
/Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk
-color-diagnostics
--
a
b
c
</code></pre>
実はたくさんの引数がついていた。<br />
<br />
Foundation を使わずに Pure Swift で引数を取ることが出来ないか調べたところ、<a href="http://practicalswift.com/2014/06/07/swift-scripts-how-to-write-small-command-line-scripts-in-swift/">practicalswift.com</a> がヒットした。どうやら Process というのが定義されているらしい。<br />
<pre><code class="bash">#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift
for arg in Process.arguments {
println("\(arg)")
}
</code></pre>
<pre><code class="bash">% ./command-4.swift a b c
./command-4.swift
a
b
c
</code></pre>
これなら Pure Swift だし、NSProcessInfo を使うよりも実用的な引数一覧になった。<br />
<br />
しかし、Process の実態には Xcode 上で定義にジャンプすることが出来ず、どこにどのように定義されているものかわからない。<br />
<br />
この定義をどうやって見つけたのだろう?<br />
<br />
<br />
<h3>
他のモジュールを使う</h3>
<br />
先の例までで Foundation を使っていたが、当然 AppKit も使えるだろうということで、GUI テストとしてアラートを表示してみる。<br />
<pre><code class="bash">#!/Applications/Xcode6-Beta5.app/Contents/Developer/usr/bin/xcrun swift
import Cocoa
var alert = NSAlert()
alert.messageText = "Hello, Swift"
alert.runModal()
</code></pre>
<pre><code class="bash">% ./command-5.swift</code></pre>
問題なく表示できた。これは素晴らしい。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-r6VTlmbY9cI/U-A6WG4FyRI/AAAAAAAAADA/EhjNbvI5kb8/s1600/command-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-r6VTlmbY9cI/U-A6WG4FyRI/AAAAAAAAADA/EhjNbvI5kb8/s1600/command-5.png" height="116" width="320" /></a></div>
<br />
これなら GUI が必要な時はシェルスクリプト代わりに swift を使うのもアリだ。<br />
<br />
<br />
では自作のモジュールを取り込むときは?<br />
シェルスクリプトで使い回し可能コードを source して取り込むように、swift コマンドラインで使い回し可能コードを import で取り込めないものか。<br />
<br />
これを行う為には、使い回す swift コードをモジュール化する必要がある。<br />
<br />
<br />
続きは<a href="http://blog.skipbit.jp/2014/08/swift-command-line-2.html">次回</a>。<br />
<br />
<br />SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com1tag:blogger.com,1999:blog-4068360104882784548.post-8375583140491718262014-07-30T13:55:00.000+09:002014-08-05T08:55:01.060+09:00[Swift] カリー化関数の話(Curried Functions)最近目にすることの多かったカリー化関数(Curried Functions)について。<br />
<br />
カリー化(currying)とは、複数の引数を取る関数について、最終的に引数を一つにするよう部分適用を行うこと。カリー化した関数は元の関数の(多くの場合)最初の引数だけを取り、戻り値は残りの引数を取って結果を返す関数、となるようだ。<br />
<br />
言葉の定義はともかくカリー化できると、引数の値は動的だがある文脈では固定であって欲しい、という場合に便利になる。<br />
<br />
最初の例としてわかりやすかった <a href="http://ijoshsmith.com/2014/06/09/curried-functions-in-swift/">ijoshsmith.com</a> の例。を少し改変してみる。<br />
例えば、元の関数が以下のようなケース。<br />
<pre><code class="swift">func appendSeparatorToStrings(strings: [String], separator: String) -> String {
return separator.join(strings)
}
appendSeparatorToStrings([ "A", "B", "C" ], "-") // "A-B-C"
appendSeparatorToStrings([ "D", "E", "F" ], " ") // "D E F"
</code></pre>
上記は String 配列を区切り文字で結合した String を返す関数だが、あるシーンで「改行で結合する関数」が必要になった、という想定でカリー化を考える。<br />
<br />
カリー化は、戻り値が「結果を返す関数」になるので、シンプルに考えた場合は「ネスト関数(Nested Function)」として実装する。<br />
<pre><code class="swift">// ネスト関数として定義 [1]
func appendSeparator(separator: String) -> ([String] -> String) {
func appendStrings(strings: [String]) -> String {
return separator.join(strings)
}
return appendStrings
}
</code></pre>
Swift の場合は、カリー化関数の専用の記法があるらしく次のように書くことができる。<br />
<pre><code class="swift">// カリー化記法で定義 [2]
func appendSeparator(separator: String)(strings: [String]) -> String {
return separator.join(strings)
}
</code></pre>
要は、わざわざネスト関数を書かなくても良くなっている。<br />
<br />
このカリー化により、ある文脈では改行結合、またある文脈では空白結合、するような関数を手に入れることが出来る。<br />
<pre><code class="swift">let appendNewlineToStrings = appendSeparator("\n")
appendNewlineToStrings(strings: [ "A", "B", "C" ]) // "A\nB\nC"
let appendSpaceToStrings = appendSeparator(" ")
appendSpaceToStrings(strings: [ "D", "E", "F" ]) // "D E F"
</code></pre>
複数の場所で必要な関数をそれぞれ定義するよりも割が良い、ということなのだろう。<br />
<br />
以下のように、似た役割の定義を増やしていくのに比べると、カリー化関数の方が品が良いのがわかる。<br />
<pre><code class="swift">func appendNewlineToStrings(strings: [String]) -> String {
return appendSeparatorToStrings(strings, "\n")
}
func appendSpaceToStrings(strings: [String]) -> String {
return appendSeparatorToStrings(strings, " ")
}
// ...
</code></pre>
これをリファクタリングすると、やはりネスト関数のようになるだろうし、最終的にカリー化の書き方が出来た方が便利だ。<br />
<br />
もっとも、この程度の例ではクロージャを使って以下のように書けてしまうので、ネスト関数もカリー化記法も必要ない。<br />
<pre><code class="swift">func appendSeparator(separator: String) -> ([String] -> String) {
return { separator.join($0) }
}
</code></pre>
どの書き方でも意味も型も同じだし、どれが分かりやすいかという程度のものだろう。両方の記述を覚えておけば、その時に適切な書き方を選択できるし、知らない記述を前に面食らうことも無い。<br />
<br />
<br />
ネスト関数(やクロージャ)とカリー化記法の違いとしては、カリー化記法の場合は外部名(External Parameter Name)を省略できない、というのがある(外部名とアンダースコアによる省略については <a href="http://blog.skipbit.jp/2014/07/swift.html" target="_blank">前の記事</a> を参照)。試しにカリー化記法 [2] にてアンダースコア(_)による省略を行ったところ、Xcode6-Beta5 時点では定義場所で警告、呼び出し箇所でエラーとなった。<br />
<pre><code class="swift">// ネスト関数 ([1]) として定義した場合は、外部名指定は必要ない
appendSeparator("\n")([ "A", "B", "C" ])
appendNewlineToStrings([ "A", "B", "C" ])
// Swift のカリー化記法([2])を使った場合は、外部名が必要になる
appendSeparator("\n")(strings: [ "A", "B", "C" ])
appendNewlineToStrings(strings: [ "A", "B", "C" ])
</code></pre>
しかし、以下のように型指定してやることで外部名は必要なくなった(<a name="note-a">*A</a>)。<br />
<pre><code class="swift">// Swift のカリー化記法([2])で定義した appendSeparator
let appendNewlineToStrings: [String] -> String = appendSeparator("\n")
appendNewlineToStrings([ "A", "B", "C" ]) // 外部名が必要なくなった。
</code></pre>
これは推測だけど、カリー化記法はネスト関数のシンタックスシュガーで、カリー化記法の場合の第二引数(と言って良いのか?)は自動的にハッシュ(#)修飾されている、ような印象<a href="#note-1"><sup>*1</sup></a>。どちらの書き方でも同じ型(関数型)で受け取ることができる。<br />
<br />
ちなみに appendSeparator 自体の型は以下のようになる。<br />
<pre><code class="swift">let appendSeparatorFunction: String -> [String] -> String = appendSeparator
</code></pre>
-> が増えると、どことどこが結合するか分かりづらいが、「ネスト関数」の簡易記法であるという前提で、型が同じであることをふまえると、<br />
<pre><code class="swift">let appendSeparatorFunction: String -> ([String] -> String) = appendSeparator
</code></pre>
であることがわかる。右からグループ化していけば良い、ということは Apple の <a href="https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html" target="_blank">The Swift Programming Language: Types</a> に書いてあった。<br />
<br />
より冗長に、関数の記法 () -> () に乗っ取って書くとすれば以下のように書けるだろう。
<br />
<pre><code class="swift">let appendSeparatorFunction: ((String) -> (([String]) -> (String))) = appendSeparator
</code></pre>
<br />
<br />
さらに興味深いのは、インスタンスメソッドのカリー化利用が可能だ、という点。<br />
これは <a href="http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/">oleb.net</a> で紹介されていた。<br />
<pre><code class="swift">class BankAccount {
var balance: Double = 0.0
func deposite(amount: Double) {
balance += amount
}
}
</code></pre>
通常は以下のように使うが、<br />
<pre><code class="swift">let account = BankAccount()
account.deposite(100)
</code></pre>
以下のようにカリー化した書き方が出来る。<br />
<pre><code class="swift">let depositor = BankAccount.deposite
depositor(account)(100)
let accountDepositor = BankAccount.deposite(account)
accountDepositor(100)
</code></pre>
型はこんな感じになる。<br />
<pre><code class="swift">let depositor: BankAccount -> (Double -> ()) = BankAccount.deposite
</code></pre>
Apple 的には T -> U -> R な書き方していたり ole さんは T -> (U) -> (R) だったりで、どれがスタンダードになってくるのか分からないが、自分はなるべく () で結合順序が明示されていた方が分かりやすい。<br />
<br />
さらに<a href="http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/">元の記事</a>ではインスタンスメソッドのカリー化を利用した Target-Action の実装例が載っている。実践的な使い方の例としてかなり有益。<br />
<br />
<br />
ところで「結合順序」とは言ったものの、「値はすべてタプルである」という話があって、<br />
<pre><code class="swift">// すべての値はタプル
let a = 10
println( a.0 )
println( a.0.0 )
</code></pre>
dankogai さんの記事(<a href="http://qiita.com/dankogai/items/46fedc447dd93d1e0fbc">Swiftの関数の引数は、常に一つ</a>)から、関数の引数定義も実はタプルであるのが読み取れる。<br />
<pre><code class="swift">func call<A,R>(f: A->R, a: A) -> R {
return f(a)
}
func add(x: Int, y: Int) -> Int {
return x + y
}
call(add, (21, 21))
</code></pre>
詳しいことは元の記事を見てもらうとして、<br />
言われてみれば確かに「関数の引数定義」は「タプルの名前付き定義」の書き方と同じ。<br />
() は、もしかして「結合順序」と言うより、タプルの境界を定義するものと考えた方が良いのかも、しれない<a href="#note-2"><sup>*2</sup></a>。<br />
<br />
例えば、先に検証したカリー化記法で外部名を外す方法(<a href="#note-a">*A</a>)は、以下のように別名を付けることが出来た。<br />
<pre><code class="swift">// Swift のカリー化記法([2])で定義した appendSeparator
let appendNewlineToStrings: ((otherName: [String]) -> (String)) = appendSeparator("\n")
appendNewlineToStrings(otherName: [ "A", "B", "C" ]) // 別の外部名の指定が必須になる
</code></pre>
これはなかなか面白い。<br />
<br />
最初の外部名を外した例(<a href="#note-a">*A</a>)では、無名タプルで定義したので指定がいらなくなった(ラベルが外された)のに対して、この例ではラベル付きタプルで宣言されたので指定が必須になっている。<br />
<br />
<br />
今回のカリー化の話は「すべての {} は関数ブロックである」ことや「関数がファーストクラスである」ことなど、一貫性の裏付けとして非常に興味深かった。<br />
「すべての値はタプル」といい、記法がたくさんあって複雑にも見えるが、このあたりの Swift の一貫性は素晴らしいと思った次第<a href="#note-2"><sup>*3</sup></a>。<br />
<br />
<br />
<h3>
参考</h3>
<ul>
<li><a href="http://ijoshsmith.com/2014/06/09/curried-functions-in-swift/" target="_blank">Curried Functions in Swift | iJoshSmith</a></li>
<li><a href="http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/" target="_blank">Instance Methods are Curried Functions in Swift - Ole Begemann</a></li>
<li><a href="https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html">The Swift Programming Launguage: Types</a></li>
<li><a href="http://codezine.jp/article/detail/7869?p=3">Swiftのパワフルな記述力が秘められた「関数」と「クロージャ」(3/5):CodeZine</a></li>
<li><a href="http://qiita.com/dankogai/items/46fedc447dd93d1e0fbc">Swiftの関数の引数は、常に一つ - Qiita</a></li>
<li><a href="http://qiita.com/Alex_23drum/items/7a9b0ecd0800483b5ddf">Swiftで関数のカリー化(currying)入門 - Qiita</a></li>
</ul>
<br />
<div>
...<br />
<a name="note-1">*1</a>: 正直、このあたりは Apple のドキュメントでは物足りないので、詳しい説明が欲しいところ。良い解説がどこかに無いだろうか?<br />
<a name="note-2">*2</a>: 気にし過ぎない方が良いかもしれない(結果的に結合順序だし)。追々調査したい。<br />
<a name="note-3">*3</a>: 最近のモダンな言語ならばどれもそうなのだろうけど。</div>
SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com7tag:blogger.com,1999:blog-4068360104882784548.post-27008880496368863352014-07-25T11:48:00.000+09:002014-08-05T08:40:41.799+09:00[Swift] アンダースコア (_) の使いどころ色々あるのでまとめてみた。<br />
<br />
<h3>
数値リテラルの区切り文字として使用</h3>
<br />
単純に見やすさのため数値リテラル内を区切ることが出来る。評価時には無視される。<br />
<pre><code class="swift">let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
</code></pre>
<br />
<h3>
タプル展開時の無視変数として使用</h3>
<br />
特定の要素のみ展開し、不要な要素を _ で無視。<br />
<pre><code class="swift">let status = (200, "OK")
let (code, _) = status
let (_, message) = status
</code></pre>
<br />
<h3>
for-in ループ変数が必要ないときに使用</h3>
<br />
ループの現在値が必要ない時は _ で無視できる。<br />
<pre><code class="swift">let base = 3
let power = 10
let answer = 1
for _ in 1...power {
answer *= base
}
</code></pre>
タプル展開も同様。
<br />
<pre><code class="swift">for (key, _) in dictionary {
println("\(key)")
}
</code></pre>
<br />
<h3>
タプル + switch-case 文で、条件判定の無視要素として使用</h3>
<br />
ワイルドカード的な。<br />
<pre><code class="swift">let point = (1, 1)
switch point {
case (0, _)
//
case (_, 1)
//
default
//
}
</code></pre>
<br />
<h3>
関数引数の外部名を無名化するのに使用</h3>
<br />
関数やメソッドのパラメータは外部名(External name)と内部名(Local name)をわけて宣言でき、外部名を宣言した場合は呼び出し時に指定しなければならない(省略できない)。
<br />
<br />
通常、外部名は宣言しなければ呼び出し時に指定する必要はないが、以下のケースでは<u>自動的に外部名が追加される</u>。この時の外部名を無名化するために使用することができる。<br />
<br />
<h4>
関数</h4>
<br />
普通の関数は、外部名を明示的に宣言に加えない限りは無名呼び出しが可能。<br />
しかしデフォルト引数がある場合は別で、自動的に(内部名と同じ)外部名が追加され、呼び出し時に省略することは出来なくなる。<br />
<pre><code class="swift">func hello(name: String = "World") {
println("Hello, \(name)")
}
hello()
hello(name: "Swift") // デフォルト値を上書きする場合、引数名を省略できない。
</code></pre>
これを回避するため、外部名を明示的にアンダースコア(_)として宣言することができる。
<br />
*当然ながら「本来は適切な名称をつけるべし」とされている。<br />
<pre><code class="swift">func hello(_ name: String = "World") {
println("Hello, \(name)")
}
hello()
hello("Swift") // 外部名の指定が必要なくなる
</code></pre>
<strike>・・・らしいのだが、現時点の Xcode6-Beta4 ではクラッシュして動作を確認できていない。
</strike><br />
<div style="background-color: #eeeeee; padding: 4px;">
Xcode6-Beta5 でクラッシュせずに実行できる事を確認。</div>
<br />
また、以下のように内部名も無名化することができる。<br />
<pre><code class="swift">func someFunction(_: Int) {
//
}
</code></pre>
この場合は変数にアクセスできなくなる。クロージャなど、引数を無視したい(使わない引数がある)場合に活用できる。<br />
<br />
余談ながら、以下のように(必要もないのに)外部名を無名化したら警告が出た。<br />
<pre><code class="swift">func someFunction(_ localName: Int) {
//
}
</code></pre>
コンパイラが教えてくれるので、特に迷うことはなさそう。
<br />
<br />
<h4>
メソッド</h4>
<br />
メソッドにあって関数にない特性として、セカンドパラメータ以降は常に外部名が追加される、というのがある(デフォルトでは内部名 = ハッシュ修飾(#)と同じ効果)。<br />
<pre><code class="swift">// 通常
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3) // セカンドパラメータ以降は外部名指定が必要
</code></pre>
*この特性のため、セカンドパラメータ以降をハッシュ修飾(#)すると警告が出る。<br />
<br />
この挙動を抑制する方法として、_ を外部名に指定することで、メソッド呼び出し時のセカンドパラメータ名を指定しなくともよくなる。<br />
<pre><code class="swift">// 無名化
class Counter {
var count: Int = 0
func incrementBy(amount: Int, _ numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
let counter = Counter()
counter.incrementBy(5, 3) // セカンドパラメータに名称が必要ない
</code></pre>
*ファーストパラメータに対して _ を付けると警告になる。<br />
<br />
<h4>
イニシャライザ</h4>
<br />
イニシャライザの場合はさらに「全てのパラメータに対して外部名が追加される」という特性がある。<br />
<pre><code class="swift">class Color {
let red = 0.0, green = 0.0, blue: 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
}
let black = Color(red: 0.0, green: 0.0, blue: 0.0)
</code></pre>
これも関数やメソッド同様、必要であればすべてのパラメータに外部名を追加することは出来るが、アンダースコア(_)を指定することで無名化できる。<br />
<pre><code class="swift">class Color {
let red = 0.0, green = 0.0, blue: 0.0
init(_ red: Double, _ green: Double, _ blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
}
let white = Color(1.0, 1.0, 1.0)
</code></pre>
イニシャライザは関数やメソッドと違って、パーレン記号の前に関数名のような識別子を持てないため、パラメータ名は意味を持たせるための重要な役割があると言える。無名化する場合は利用するときに困らない理由(名称が省略されても意味が明白、など)が欲しいところ。<br />
<br />
外部名の自動追加は、基本的に Objective-C スタイルの(説明的な)呼び出しになるように、との配慮らしい。ObjC ではマナーに過ぎなかったことが Swift では言語で強制した感じ。<br />
<br />
<br />
<h3>
使ってはいけないところ</h3>
<br />
逆にアンダースコア(_)が使えないところとして明示されているのは、今のところ operator 定義時の優先度(precedence)値。<br />
0〜255 の整数値だが、この時の区切り文字としては使えないとのこと。<br />
<br />
<br />
<h3>
まとめ</h3>
<br />
色々あると思ったけど、結局はどこを切っても「無名ラベル」として使えると。<br />
なんなら以下のように宣言しても怒られない(使えないけど)。<br />
<pre><code class="swift">let _ = 1</code></pre>
若干変わってると言えるのは数値リテラルの区切り文字くらいだろうか。SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com0tag:blogger.com,1999:blog-4068360104882784548.post-26627439940882912182014-07-23T15:17:00.000+09:002014-07-24T04:42:05.607+09:00アイコンフォント利用のお供に - Symbol Imagine の紹介<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-WKV7mEMvNC4/U84RVM7lMkI/AAAAAAAAABM/H_szFdq2c58/s1600/placeit.png.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-WKV7mEMvNC4/U84RVM7lMkI/AAAAAAAAABM/H_szFdq2c58/s1600/placeit.png.jpeg" height="240" width="320" /></a></div>
<h3>
</h3>
<br />
アイコンフォントの利用をより便利にするユーティリティ <a href="http://symbolimagine.webflow.com/" target="_blank">Symbol Imagine</a> の紹介。
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://itunes.apple.com/us/app/symbol-imagine/id899452174?l=ja&ls=1&mt=12" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="" border="0" src="http://3.bp.blogspot.com/-Sq_IKrIDxkE/U84TtsZzA7I/AAAAAAAAABc/igtoxh7nUg0/s1600/Download_on_the_Mac_App_Store_Badge_JP_165x40_1004.png" title="Download Symbol Image" /></a></div>
<br />
<h3>
アイコンフォントについて</h3>
<br />
アイコンフォントとは、文字の代わりにアイコンがグリフとして納められているフォントであり、以下のような特徴のため Web サイトやモバイル等の開発素材として使われています。<br />
<br />
<ul>
<li>1ファイルに納められているため、ディスクアクセス(リクエスト数)を減らせる</li>
<li>ベクターデータなので、高解像度対応も容易</li>
<li>文字として描画するので、画像で表示するよりも軽い</li>
</ul>
<br />
私のような絵心に乏しい開発者には非常にありがたい存在です。<br />
しかしこのアイコンフォント、プログラムの中で使うには若干の面倒さがあります。<br />
<br />
<h4>
</h4>
<b>
使用するアイコンの指定方法が微妙</b><br />
意味を持たないアルファベットや Unicode でアイコン指定しなければなりません。<br />
この欠点をリガチャを使って解決しているフォントもあります。<br />
<br />
<h4>
</h4>
<b>
表示するまでの微調整が面倒</b><br />
フォントなので、プログラムでは属性付き文字として構築&描画することになります。<br />
テーマや状態に合わせて、表示位置やサイズ、色の調整が必要です。<br />
<br />
<b>
フォントファイルをパッケージにバンドルしなければならない</b><br />
使用するフォントは、ユーザのマシン内にインストールされているとは限らないので、フォントファイルはアプリケーションが持ちます。<br />
<br />
<br />
このような手間のため、開発時には画像であった方が便利だった、というケースがありました。もしかすると、アイコンフォントをプログラムの中で実行時に画像化してから描画している開発者もいるのではないでしょうか。<br />
<br />
というわけで、これら開発時の手間を解決するため、<br />
このたび Symbol Imagine というユーティリティアプリケーションをリリースしました。<br />
<div>
<br /></div>
<br />
<h3>
Symbol Imagine とは</h3>
<br />
以下の機能を持っています。<br />
<ul>
<li>アイコンフォントに登録されているシンボルレパートリーの一覧を表示</li>
<li>大きさ・色を変更してプレビュー</li>
<li>プレビュー結果を画像化して書き出し</li>
</ul>
<br />
アイコンフォントの特徴により、表示を確認するまでには手間あり、さくっとモックアップを作るにはいささか不便です。<br />
ふんだんに存在するアイコンフォントの中から、自分のアプリケーションとマッチしたテイストのアイコンを選定し、色、位置、サイズの調整するまでに、この手間は小さからぬ問題となります。<br />
<br />
Symbol Imagine では色、サイズをカスタマイズできますので、実際に使用する前にアイコンのプレビューを確認することが出来ます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-xPV-6y-FGyk/U84NRRSARYI/AAAAAAAAAAg/4aa7fyBJgeM/s1600/size_and_color_change.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-xPV-6y-FGyk/U84NRRSARYI/AAAAAAAAAAg/4aa7fyBJgeM/s1600/size_and_color_change.png" height="236" width="320" /></a></div>
<br />
<br />
アイコンフォントを使用する際に困るのが、アイコン指定が(リガチャが入っているフォントを除き)アルファベットや Unicode であることです。<br />
割り当てられているアイコンがフォント毎にそれぞれ異なっているし、1フォント内にもたくさんのアイコンが入っているので、とても覚えられるようなものではなく、各フォント毎にチートシートが必須です。<br />
<br />
Symbol Imagine は Unicode とグリフの一覧を出しますので、フォントのチートシート代わりにも使えます。<br />
Unicode スカラ値やグリフ名で絞り込むこともできます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://4.bp.blogspot.com/-rfWjFbTHmHw/U84QjoVMhVI/AAAAAAAAABA/-MubTs8v4b0/s1600/search_glyph-1_m.png" style="float: left;" />
<img border="0" src="http://2.bp.blogspot.com/-jdhb7O_3otA/U84QjoyFCRI/AAAAAAAAAA8/A2xS14xp8Pc/s1600/search_glyph-2_m.png" style="float: right;" />
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h3>
インストールしていないフォントも表示可能</h3>
OS X 付属の純正アプリケーション Font Book では、フォント内レパートリー一覧を表示することができますが、フォントはインストールする必要があります。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-MDOhE7KZr2k/U84Vzgbtk5I/AAAAAAAAAB4/8MGnsowODGE/s1600/fontbook.png" imageanchor="1"><img alt="" border="0" src="http://4.bp.blogspot.com/-MDOhE7KZr2k/U84Vzgbtk5I/AAAAAAAAAB4/8MGnsowODGE/s1600/fontbook.png" title="Font Book" /></a></div>
<br />
Symbol Imagine は、インストールしていないフォントファイルを指定してプレビューすることが出来ます。特定のプログラムからしか使わないフォントファイルを自分のコンピュータにインストールする必要はありません。<br />
様々なアイコンフォントを見比べて、どのアイコンを使うか吟味することが出来ます。<br />
<br />
<h3>
ライセンスにはご注意</h3>
それぞれのフォントにはライセンスが存在します。<br />
Symbol Imagine の出力結果を実際の配布物に含める際には、使用するフォントのライセンスに従ってご利用ください。<br />
<br />
フリー / オープンソースで配布されているフォントには OFL や MIT が多くあるので、それらであれば問題ないと思います。<br />
<br />
<br />
<br />
ダウンロードは<a href="https://itunes.apple.com/us/app/symbol-imagine/id899452174?l=ja&ls=1&mt=12" target="_blank">こちら</a>から。
<a href="https://itunes.apple.com/us/app/symbol-imagine/id899452174?l=ja&ls=1&mt=12"><img border="0" src="http://3.bp.blogspot.com/-Sq_IKrIDxkE/U84TtsZzA7I/AAAAAAAAABY/FHEvu59JhCc/s1600/Download_on_the_Mac_App_Store_Badge_JP_165x40_1004.png" style="float: right;" /></a>SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.comtag:blogger.com,1999:blog-4068360104882784548.post-63349999152885508662014-07-22T11:43:00.000+09:002014-08-05T08:48:17.093+09:00[Swift] @autoclosure 属性 の使いどころ<div style="background-color: #eeeeee; padding: 4px;">
Xcode6-Beta5 にて @auto_closure は @autoclosure に変更された。<br />同様に LogicValue は BooleanType になった。<br />
[新] @autoclosure ← [旧] @auto_closure<br />
[新] BooleanType ← [旧] LogicValue<br />
この変更にあわせて記事を修正した。</div>
<br />
<a href="https://developer.apple.com/swift/blog/" target="_blank">Apple Swift Blog</a> にて @autoclosure 属性による遅延評価が紹介されていた。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-SoYUQa1vDJE/U83LjaDT2SI/AAAAAAAAAAQ/B25FDfC31t4/s1600/apple-swift-auto_closure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-SoYUQa1vDJE/U83LjaDT2SI/AAAAAAAAAAQ/B25FDfC31t4/s1600/apple-swift-auto_closure.png" height="220" width="320" /></a></div>
<br />
<div style="text-align: center;">
<a href="https://developer.apple.com/swift/blog/?id=4" target="_blank">Building assert() in Swift, Part 1: Lazy Evaluation - Swift Blog - Apple Developer</a></div>
<br />
C の assert() 実装を Swift で行った場合の例が引き合いに出されていて分かりやすい。<br />
<br />
C 言語の assert 実装はマクロであるから、リリースビルドでは ((void)0) と展開されて、式の評価自体がない。<br />
同様の実装を Swift で考えたとき、以下のような関数 func assert(x: Bool) の宣言では常に式の評価が発生してしまう。
<br />
<pre><code class="objectivec,swift">func assert(x: Bool) {
<span class="hljs-preprocessor">#if !NDEBUG</span>
/*noop*/
<span class="hljs-preprocessor">#endif</span>
}
assert(someExpensiveComputation() != 42)
</code></pre>
というわけで、引数をクロージャに変更。するとシンタックスが残念な感じになる。
<br />
<pre><code class="swift">assert({ someExpensiveComputation() != 42 })</code></pre>
そこで @autoclosure の登場。<br />
@autoclosure 属性は関数の引数に指定でき、渡された式をクロージャとしてラップする。
<br />
<pre><code class="swift">func myassert(predicate: @autoclosure () -> Bool) {
<span class="hljs-preprocessor">#if !NDEBUG</span>
if predicate() {
abort()
}
<span class="hljs-preprocessor">#endif</span>
}
</code></pre>
呼び出しが自然で遅延評価可能になる。
<br />
<pre><code class="swift">myassert(someExpensiveComputation() != 42)
</code></pre>
<br />
標準ライブラリでは && 演算子も @autoclosure 引数を取るように宣言されていて、右オペランド(の式)は自動的にクロージャとしてラップされ、左オペランドが false であった場合は右オペランドの評価はされない。<br />
<pre><code class="swift">func &&(lhs: BooleanType, rhs: @autoclosure () -> BooleanType) -> Bool {
return lhs.boolValue() ? rhs().boolValue() : false
}
</code></pre>
もし rhs が単なる BooleanType であったなら、&& が実行された段階で rhs が評価されてしまうし、
通常のクロージャ引数 rhs: () -> BooleanType にすると rhs の評価を遅延させることが出来るけども、以下のように面倒な書き方になってしまう。
<br />
<pre><code class="swift">let result = A() && { B() }</code></pre>
<br />
てなワケで、評価は遅延させたいけどもクロージャではなくて単純な式を渡せた方が良い場合に @autoclosure 属性は便利ですね、という話。<br />
<br />
ちなみにこの属性が適用されたクロージャは引数を取ることは出来なかった(コンパイルエラーとなる)。式が自動的にラップされるので引数指定する機会がないし、当然か。SKIPBIThttp://www.blogger.com/profile/10007897567332913880noreply@blogger.com0