Swiftのmapは処理だけしてVoid返すことは出来ないから気ィ付けや
追記
この記事の情報は古いので注意して下さい。
最新のXcodeでは、mapに返り値を持たないクロージャを渡しても問題ありません。
追記ここまで
SwiftのArrayは、非破壊的なメソッド、map
を持っています。素晴らしい。
enumerateObjectsUsingBlock:
とか書いてられませんよホントに。メソッド名が長いのはともかくBlocksの記法がfuckingblocksyntaxでうんざりします。
map
の宣言はfunc map<U>(transform: (T) -> U) -> Array<U>
となっています。
Haskellでいうところのmap :: (t -> u) -> [t] -> [u]
ですね。
T型の要素を持つArrayと、T型を引数に取ってU型を返すクロージャがあります。T型のArrayの全ての要素にクロージャを適用し、U型のArrayを返すのがmap
です。
まずはInt
のArrayをmap
してInt
のArrayを返すコード
let numbers :Array<Int> = [1,2,3,4] // Int型のArray let doubled :Array<Int> = numbers.map { (n :Int) -> Int in return n * 2 // 全ての要素を2倍する } println(doubled) //=> [2, 4, 6, 8]
次に、Int
のArrayをmap
してString
のArrayを返すコード
let numbers :Array<Int> = [1,2,3,4] // Int型のArray let repeated :Array<String> = numbers.map { (n :Int) -> String in var retVal :String = "" for _ in 0..n { retVal += "★" } // ★をn回繰り返す return retVal } println(repeated) //=> [★, ★★, ★★★, ★★★★]
最後に、Int
のArrayをmap
するが、処理だけして値は返さない--Voidを返す--コード
let numbers :Array<Int> = [1,2,3,4] // Int型のArray numbers.map { (n :Int) -> Void in // ここでランタイムエラー println(n) }
死にます。ちなみにビルドは通り、実行時に死にます。
Rubyなんかでは、こういうmapは普通に書けます。
numbers = [1,2,3,4] numbers.map { |n| print n } #=> 1234
考察
まず、前提として「Void型を要素にもつArray」は宣言できます。つまり、コンパイルが通ります。
var voids :Void[] var voids2 :()[]
これに、実際にVoidのArrayを代入すると、コンパイルは通りますが、RuntimeErrorで死にます。
var voids :Void[] voids = [()] // ここで死ぬ
空のArrayですら代入できません。コンパイルは通りますが、同様にRuntimeErrorで死にます。
var voids :Void[] = [] // ここで死ぬ
Swiftのmap
は非破壊的なので、map自体は(再)代入を行いません。
そのため、このコードでは代入は行われませんが、やはり死んでしまいます。
let numbers :Array<Int> = [1,2,3,4] // Int型のArray numbers.map { (n :Int) -> Void in // ここでランタイムエラー println(n) }
あくまで推測ですが、map
を走らせる際に、内部的にはArray<Void>
のインスタンスを作って処理結果を随時代入しようとしていて、その際に死ぬのでしょう。
「Swiftのmapは処理だけしてVoid返すことは出来ない」と同時に、そもそもArray<Void>
は使ってはいけない、と考えてよいと思います。
なお、配列に対して副作用だけ起こしてなにも値を返したくない際は、大人しくfor in
を使えばよいです。
let numbers :Array<Int> = [1,2,3,4] // Int型のArray for n :Int in numbers { println(n) } // 出力 1 2 3 4
追記
Twitter等で「Voidを返す」って表現がアレ、との指摘があります。 たしかにアレなんですが、ドキュメントに「値を返さないってのはな、実は()を返してるんだ」とあるので見逃して下さい。
Strictly speaking, the sayGoodbye function does still return a value, even though no return value is defined. Functions without a defined return type return a special value of type Void. This is simply an empty tuple, in effect a tuple with zero elements, which can be written as ().
Swiftでデバッグ出力(日時、メソッド名、行番号)
メソッド名と行番号
かつてObjective-Cではこのように書いていた時代があった。
NSLog(@"%s, %d", __PRETTY_FUNCTION__, __LINE__);
今日からはこう書ける!*1
println(__FUNCTION__, __LINE__) //=> (someFunction(), 60)
Swiftのprintln
は、NSLog
と違って現在時間が表示されない。
出力したければNSDate
を使えばよい。
println(String(NSDate.date().description), __FUNCTION__, __LINE__) //=> (2014-06-04 13:34:18 +0000, someFunction()someFunction(), 63)
追記: 普通にNSLogも使えるので使えばよい。
特殊リテラルには以下があるので好きに使うと良い
__COLUMN__
__FILE__
__FUNCTION__
__LINE__
デバッグ用出力
Swiftは文字列中で変数を展開できる。
let name = "oropon" let greeting = "My name is \(name)" println(greeting) //=> My name is oropon
ここで\( )
の中に入れられるのは「文字列化」できる変数のみ。
自分で定義したクラスを「文字列化」できるようにするには、Printableプロトコルに準拠してやる必要がある。
class MyClass :Printable { var description: String { return "MyClassだよー" } }
要するに、println(hoge)
や、"this is \(hoge)"
のように文字列化するhoge
はdescriptionメソッドを持っていてPrintableプロトコルに準拠してないとダメだよ、という話。
以下余談
Printableプロトコルと似たようなプロトコルに、DebugPrintableプロトコルというものがある。なんだか夢が膨らみそうな名前だ。
ドキュメントにはこのように書いてあるが、やってみてもうまく動かないし、正直DebugPrintableプロトコルが何のためにあるのかよく分からない。。
// set a breakpoint and type p value in the LLDB console
// you'll see "MyType: Untitled"
*1:全然変わってない
Swiftでコマンドライン引数を取る
OS XのコマンドラインアプリをSwiftで作る時に役立ちます。どこにあるんだそんな需要。*1
StackOverflowではC_ARGC
を使う方法が示されています。
これでも良いと思いますが、ちょっと冗長ですね。
osx - How do you access command line arguments in Swift? - Stack Overflow
ここはFoundationのNSProcessInfo
を使いましょう。
こんなコードで、コマンドライン引数をSwiftのString型の配列として取得できます。
import Foundation let arguments :String[] = NSProcessInfo.processInfo().arguments.map{String($0 as NSString)} println(arguments)
実行するとこのようになります。
% ./SwiftCLISample hoge fuga piyo %[/path/to/binary/SwiftCLISample, hoge, fuga, piyo]
swiftでfizzbuzz
とりあえずfizzbuzzを書いてみる。
func fizzbuzz (#max: Int) { func fb(n:Int) -> String { let isFizz = n % 3 == 0 let isBuzz = n % 5 == 0 switch true { case isFizz && isBuzz: return "fizzbuzz" case isFizz: return "fizz" case isBuzz: return "buzz" default: return String(n) } } for n in 1...max { println(fb(n)) } } fizzbuzz(max: 100) // 出力 1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 . . .
RMagickを使ってアプリ用のサンプル画像を量産しよう
やりたいこと
あなたはiOSアプリを開発しているとします。
一週間後に、デザイナーからimage1.png
からimage100.png
までの100枚の画像リソースが届きます。あなたはこの画像をアプリに組み込んで表示しなければいけません。
しかし、デザインが届くのを待ってから開発に着手するのでは開発が遅れてしまいます。お客さんが激おこぷんぷん丸になります。
仕方ないので、とりあえずは仮の画像を当てて作業することにします。
Placehold.it - Quick and simple image placeholders などを利用して仮の画像を作ってもいいのですが、それでは..
「image15.png
を入れるべき場所にimage37.png
を入れちまってた!!やべえ!!」
っていうケアレスミスを開発時点で気づくことができません。
それでは、画像自体にidを記載してしまいましょう。
image1.png
には「1」って書いて、image57.png
には「57」って書いて、image100.png
には「100」って書けばいいんです。
...でも、どうやって?手作業??100枚も???
「ちょっとした画像加工を多数の画像に対して行う」のを手作業で行うなんて耐えられない
— 定食屋 おろポン (@oroponya) 2014, 5月 13
僕は耐えられません。目の前の箱に働いてもらいましょう。
※ 状況設定はフィクションです。スマホアプリ開発に限らず、Webアプリ開発でもなんでも使えます。
準備する
Homebrew
でimagemagick
を入れる。
% brew install imagemagick
bundler
でrmagick
を入れる。別にgem install rmagick
でもいい。
% bundle init % echo gem \"rmagick\" >> Gemfile % bundle install Fetching gem metadata from https://rubygems.org/.. Resolving dependencies... Installing rmagick 2.13.2 Using bundler 1.6.2 Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
Sample用にoctcat.png
を仕入れてくる。octcatかわいい。
% curl -o octcat.png https://octodex.github.com/images/original.png
情報収集
準備は万端。ドキュメントから必要な情報を仕入れる。
http://www.imagemagick.org/RMagick/doc/usage#drawing_on.html
The annotate method draws text on an image.
「annotateメソッドは画像にテキストを描画します。」
面倒くさいので英語はすっ飛ばしてサンプルコードだけ見る。
#! /usr/local/bin/ruby -w require 'RMagick' # Demonstrate the annotate method Text = 'RMagick' granite = Magick::ImageList.new('granite:') canvas = Magick::ImageList.new canvas.new_image(300, 100, Magick::TextureFill.new(granite)) text = Magick::Draw.new text.font_family = 'helvetica' text.pointsize = 52 text.gravity = Magick::CenterGravity text.annotate(canvas, 0,0,2,2, Text) { self.fill = 'gray83' } text.annotate(canvas, 0,0,-1.5,-1.5, Text) { self.fill = 'gray40' } text.annotate(canvas, 0,0,0,0, Text) { self.fill = 'darkred' } canvas.write('rubyname.gif') exit
ImageList.new
でキャンバスを初期化
Draw.new
で描画オブジェクトを初期化
描画オブジェクトにプロパティを設定し、annotate
メソッドで描画を行う。
annotate
メソッドに渡すブロック内でfill
を指定することで、文字の塗りつぶしを行う。
write(fpath)
でファイルに書き込む。
このコードではテキストの描画位置と色を変えて複数回描画することで、凹凸(ベベルとエンボス)を表現している。
fmfm。なんとなく理解したらおk。次に進む。
書いてみる
サンプルコードを元に細かい修正を加える。 細かいところはコメントに書いた。
require 'RMagick' include Magick # 面倒くさいのでinclude Text = "Hello, My name is Octcat!" img = ImageList.new 'octcat.png' # ImageList.new file_path で画像読み込み text = Draw.new # 描画オブジェクトを初期化 text.font = '/System/Library/Fonts/Avenir Next.ttc' # フォントはフォント名だけではなく絶対パスで指定 text.pointsize = 52 text.gravity = CenterGravity # 上下中央寄せしてくれるみたい text.annotate(img, 0,0,0,0, Text) { self.fill = 'gray' # カラーコードか色名を入れる } img.write('octcat_greeting.png') # 書き出し
ね、簡単でしょう?
注意点として、少なくとも手元のMacでは、fontにフォントの絶対パスを指定する必要があった。
パスがわからない場合は、Font Book.app
でフォントを検索して、右クリックからShow in Finder
してファイルパスを調べればおk。
量産してみる
文字を縁取りし、量産してみる。
注意するところは、Line29のimg.copy
。copy
メソッドを呼んでdeep copyを行わないと、同じ画像に対して何度もテキストを上書きすることになってごちゃごちゃになる。
iOS用にRetina用と非Retina用の画像を量産するならこんな感じで行ける。(てきとう)
require 'RMagick' include Magick id_min = 0 id_max = 100 def annotate_id img, id, thumb=false fname = "Octcat_%03d#{'@2x' unless thumb}.png" % id scale = thumb ? 0.5 : 1.0 img = img.scale(scale) text = Draw.new text.font = '/System/Library/Fonts/Avenir Next.ttc' text.pointsize = (1500 / fname.length) * scale text.gravity = CenterGravity text.annotate(img, 0,0,0,0, fname) { # 画像の色にかかわらず文字が読めるように縁取りする self.stroke_width = 3 * scale self.stroke = 'white' self.fill = 'gray30' } img.write(fname) end img = ImageList.new 'octcat.png' id_min.upto(id_max) do |i| annotate_id img.copy, i annotate_id img.copy, i, true end
まとめ
ImageMagickを使ったことなくて焦ったけど、意外と簡単に使えた。みんなも使おう。
目の前の箱のマスターとなってビシバシ働いてもらおう。
GitHub Flavored Markdownを書くならAtomで決まりでしょ
先日、こんな記事を書きました。
AtomがGithub Flavored Markdownのエディタとして使える件について - 定食屋おろポン
要約すると、
- GitHubがAtomってエディタ出したよね
- GitHub Flavored Markdown、いわゆるGFMてのがあるよ
- いわゆるMarkdown(Traditional Markdown)より、GFMのほうがイケてる👍
- GitHubチームの作ったAtomはGFMに最初っから対応してる!Cool!
- ライブプレビューもできます。左のビューで編集したら右のビューでリアルタイムにどう表示されるかを確認できる。Cool!
- ただ、HTMLで出力・保存できないから、印刷もできないし。。困った😥
ってことです。
HTML化、対応してた
どうせならPullRequest送ってやろうと思い立ち、「まずはCoffee Scriptとnodejsを覚えるところから..」などと、「頼んだハンバーグまだかなあ父ちゃん」「ハハハ、今ごろ店員さんが牧場で牛を捕まえているところさ」みたいな悠長なことを言って放置していたら対応されていましたよ。HAHAHA
Support copy/save to HTML by kevinsawicki · Pull Request #68 · atom/markdown-preview · GitHub
数日中にはこの変更を含んだmarkdown-preview 0.65.0がリリースされると思います。
待ちきれない僕は一足先に試してみました。 最新版を試したり編集するには、Contributing to Atom Packagesを参考に開発モードを使用します。
実際に使用してみたので見てみましょう。
このように、プレビュー画面右クリックでHTMLのコピーやセーブのコンテキストメニューが表示されます。
また、Cmd+Shift+P
からも使用できます。
こんなかんじのHTMLがコピーされます。
<h1 id="h1">h1</h1> <h2 id="h2">h2</h2> <h3 id="h3">h3</h3> <h4 id="h4">h4</h4> <h5 id="h5">h5</h5> <h6 id="h6">h6</h6> <p><strong>Bold</strong><br><em>Italic</em></p> <ul> <li><label> <input type="checkbox" class="task-list-item-checkbox"> checkbox </label></li> <li><label> <input type="checkbox" class="task-list-item-checkbox" checked=""> checked </label></li> </ul> <hr> <p>Table</p> <table> <thead> <tr> <th style="text-align:left">Key</th> <th style="text-align:right">Value</th> </tr> </thead> <tbody> <tr> <td style="text-align:left">hoge</td> <td style="text-align:right">100</td> </tr> <tr> <td style="text-align:left">fuga</td> <td style="text-align:right">200</td> </tr> </tbody> </table> <pre class="editor-colors"><code class="lang-javascript"><div><span class="source js"><span class="entity name type object js firebug">console</span><span class="support function js firebug">.log</span><span class="meta brace round js">(</span><span class="string quoted double js"><span class="punctuation definition string begin js">"</span>hello, world<span class="punctuation definition string end js">"</span></span><span class="meta brace round js">)</span><span class="punctuation terminator statement js">;</span></span></div></code></pre>
これで快適GFMライフ!
印刷したりPDF化したければ、HTMLをブラウザで開いて印刷なりPDF化なりすればよいわけです。Sublime Textでいうところのprint-to-htmlパッケージみたいのは欲しいところですが。*1
あと、
- 保存されたHTMLにCSSが含まれていないので見た目がアレ。特にシンタックスハイライトがアレ。
- 絵文字は
<img class="emoji"src="略/emoji-images/pngs/smile.png">
みたいになるので、当然表示できない
みたいな問題もあるけど、些細な事です。*2