定食屋おろポン

おろしポン酢と青ネギはかけ放題です

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[] = [] // ここで死ぬ

Swiftmapは非破壊的なので、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を返す」って表現がアレ、との指摘があります。 たしかにアレなんですが、ドキュメントに「値を返さないってのはな、実は()を返してるんだ」とあるので見逃して下さい。

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

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]

*1:CSVとかから.plistを作りたいときとか役に立ちます。いちおう。

swiftでフィボナッチ数列

フィボナッチ数列再帰で(遅いやつ)

func fib(n:Int) -> Int {
    if n < 1  {return 0}
    if n == 1 {return 1}
    
    return fib(n-1)+fib(n-2)
}

func fibs(n:Int) -> Int[] {
    if n == 0 {return []}
    if n == 1 {return [1]}
    
    return fibs(n-1) + [fib(n)]
}

println(fibs(10))

// 出力
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

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枚も???

僕は耐えられません。目の前の箱に働いてもらいましょう。

※ 状況設定はフィクションです。スマホアプリ開発に限らず、Webアプリ開発でもなんでも使えます。

準備する

Homebrewimagemagickを入れる。

% brew install imagemagick

bundlerrmagickを入れる。別に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

f:id:oropon:20140514024820p:plain

情報収集

準備は万端。ドキュメントから必要な情報を仕入れる。

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') # 書き出し

f:id:oropon:20140514024849p:plain

ね、簡単でしょう?

注意点として、少なくとも手元のMacでは、fontにフォントの絶対パスを指定する必要があった。 パスがわからない場合は、Font Book.appでフォントを検索して、右クリックからShow in Finderしてファイルパスを調べればおk。

量産してみる

文字を縁取りし、量産してみる。

annotate fname

f:id:oropon:20140514032150p:plain

f:id:oropon:20140514032100p:plain

注意するところは、Line29のimg.copycopyメソッドを呼んで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

f:id:oropon:20140514034738p:plain

f:id:oropon:20140514034742p:plain

まとめ

ImageMagickを使ったことなくて焦ったけど、意外と簡単に使えた。みんなも使おう。

目の前の箱のマスターとなってビシバシ働いてもらおう。

GitHub Flavored Markdownを書くならAtomで決まりでしょ

先日、こんな記事を書きました。

AtomがGithub Flavored Markdownのエディタとして使える件について - 定食屋おろポン

要約すると、

  • GitHubAtomってエディタ出したよね
  • 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のコピーやセーブのコンテキストメニューが表示されます。

f:id:oropon:20140425044203p:plain

また、Cmd+Shift+Pからも使用できます。

f:id:oropon:20140425044356p:plain

こんなかんじの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

*1:やるやる詐欺リストに入れておきます

*2:やるやる詐欺リストに入れておきます