読者です 読者をやめる 読者になる 読者になる

ぼろぼろ平原

困った

るびまRubyist Hotlinks - インタビュイーの好きなメソッド・嫌いなメソッドまとめ

Rubyist Magazine』、略して『るびま』には、著名なRubyistにインタビューを行う「Rubyist Hotlinks」という企画がある。 そこでは「好きなメソッド、嫌いなメソッドはなんですか?」という質問が毎回される。

今回はその解答をまとめた。メソッドにはRuby 2.2.0リファレンスマニュアルへのリンクをつけてある。

Ruby 2.2.0に合わせてレシーバを変えた。例:Object#instance_evalBasicObject#instance_eval

No. インタビューイ 好きなメソッド 嫌いなメソッド
第1回 まつもとゆきひろさん Kernel.#eval Kernel.#eval
第2回 前田修吾さん Enumerable#collect String#gsub!
第3回 かずひこさん Object#=== Kernel.#eval
第4回 ただただしさん みんなだいたい好き String#scan
第5回 増井俊之さん Array#eachEnumerable#collect、yield 特になし
第6回第7回 江渡浩一郎さん BasicObject#method_missing、Enumerable 特になし
第8回 田中哲さん Regexp#to_s ブロック付きメソッドの中でのretry *1
第9回 中田伸悦さん yycompile *2 特になし
第10回 わたなべひろふみさん Kernel.#p Object#initializeMutex#synchronize
第11回第12回 後藤謙太郎さん Kernel.#p Object#inspect *3
第11回第12回 後藤裕蔵さん Enumerable#inject 特になし
第13回 関将俊さん Enumerable#inject 特になし
第14回 角谷信太郎さん Object#extendModule#const_get Test::Unit::Assertions#assert_equal *4
第15回 artonさん Object#send 自分の理解できないもの
第16回 essaさん Enumerable#collect 特になし
第17回 青木峰郎さん String#slice(regexp, n) *5 Enumerable#find_all
第18回 立石孝彰さん Enumerable#inject Kernel.#eval
第19回 伊尾木将之さん Array#eachString#next String#+
第20回 石塚圭樹さん Module#module_eval Array#nitems *6
第21回 原信一郎さん Enumerable#map Enumerable#inject
第22回 nariさん GC#enable GC#disable
第23回 yharaさん Enumerable 特になし
第24回 okkezさん Enumerable#map Kernel.#set_trace_func
第25回 加藤勇也さん BasicObject#instance_eval BasicObject#instance_eval
第26回 cuzicさん Module#class_eval Kernel.#p
第27回 小波秀雄さん Enumerable#map Kernel.#printf
第28回 Yuguiさん BasicObject#instance_eval Math.sin! *7
第29回 松田明さん 言及なし 言及なし
第30回 遠藤侑介さん Array#packString#unpack Module#define_method
第31回 樽家昌也さん Thread.new ObjectSpace
第32回 卜部昌平さん Enumerable#lazy 特になし
第33回第34回 大場光一郎さん do 特になし
第33回第34回 大場寧子さん Enumerable#map Object#nil?
第35回 島田浩二さん 言及なし 言及なし

*1:Ruby 1.9で廃止。今はbeginとendの中でのみ使える

*2:MRIの関数

*3:これをデバッグする作業が嫌い

*4:スティングフレームワーク周りはゴタゴタしていたのでRuby 2.2.0のリファレンス・マニュアルにこのメソッドはない。参考: Rubyのテスティングフレームワークの歴史(2014年版) - ククログ(2014-11-06)

*5:引数も重要

*6:Ruby 1.9で廃止。nilではない要素の数を返す

*7:昔はあったらしいが発見できなかった。CMath.#sin!なら今でもある。

App Storeのトップセールスアプリ200件を雑に分析する

App Storeの「トップセールスApp」は売上高順にアプリが並ぶ。今回はiPhoneアプリを対象にこれらをRubyで雑に分析する。

データの取得

iTunes APIではトップセールスは取得できないので、代わりにAppleRSSRSS Generator)から取得する。

XMLのパースにはYorickPeterse/oga · GitHubを使う。

今回はRubyコードでiPhoneトップセールスアプリ200件の

  1. アプリ名
  2. 販売元
  3. カテゴリ
  4. 価格

の4つを取得する。

Rubyコード

#!/usr/bin/env ruby

require "oga"
require "open-uri"

file = open("https://itunes.apple.com/jp/rss/topgrossingapplications/limit=200/xml", "r:UTF-8")
doc = Oga.parse_xml(file)

data = doc.xpath("//feed/entry").map {|entry|
  {
    name: entry.at_xpath("im:name").text,
    author: entry.at_xpath("im:artist").text,
    category: entry.at_xpath("category").get("label"),
    price: entry.at_xpath("im:price").get("amount").to_i,
  }
}

data
# => [{:name=>"モンスターストライク", :category=>"ゲーム", :author=>"mixi, Inc", :price=>0},
# {:name=>"パズル&ドラゴンズ", :category=>"ゲーム", :author=>"GungHo Online Entertainment, Inc.", :price=>0},
# (省略)

データ取得結果

200件すべてを載せると長すぎるので上位20件のみを示す。ただし、カテゴリはLINE(ソーシャルネットワーキング)以外はすべてゲーム、価格はすべて0円だったので省略。

順位 アプリ名 販売元
1 モンスターストライクモンスターストライク mixi, Inc
2 パズル&ドラゴンズパズル&ドラゴンズ GungHo Online Entertainment, Inc.
3 白猫プロジェクト白猫プロジェクト COLOPL, Inc.
4 LINE:ディズニー ツムツムLINE:ディズニー ツムツム LINE Corporation
5 FINAL FANTASY BRAVE EXVIUSFINAL FANTASY BRAVE EXVIUS SQUARE ENIX INC
6 LINELINE LINE Corporation
7 ドラゴンクエストモンスターズ スーパーライトドラゴンクエストモンスターズ スーパーライト SQUARE ENIX INC
8 アイドルマスター シンデレラガールズ スターライトステージアイドルマスター シンデレラガールズ スターライトステージ BANDAI NAMCO Entertainment Inc.
9 LINE ポコポコLINE ポコポコ LINE Corporation
10 戦国炎舞 -KIZNA- 【人気の本格戦国RPG】戦国炎舞 -KIZNA- 【人気の本格戦国RPG】 Sumzap Inc.
11 Fate/Grand OrderFate/Grand Order Aniplex Inc.
12 ゲーム・オブ・ウォー「Game of War」ゲーム・オブ・ウォー「Game of War」 Machine Zone, Inc
13 星のドラゴンクエスト星のドラゴンクエスト SQUARE ENIX INC
14 ファントム オブ キル【一手で戦況が変わるシミュレーションRPG】ファントム オブ キル【一手で戦況が変わるシミュレーションRPG】 gumi Inc.
15 乖離性ミリオンアーサー乖離性ミリオンアーサー SQUARE ENIX INC
16 FINAL FANTASY Record KeeperFINAL FANTASY Record Keeper DeNA Co., Ltd.
17 あんさんぶるスターズ!あんさんぶるスターズ! Happy Elements K.K
18 クラッシュ・オブ・クラン (Clash of Clans)クラッシュ・オブ・クラン (Clash of Clans) Supercell
19 グランブルーファンタジーグランブルーファンタジー Cygames, Inc.
20 オルタンシア・サーガ -蒼の騎士団- 【戦記RPG】オルタンシア・サーガ -蒼の騎士団- 【戦記RPG】 SEGA CORPORATION

雑な分析

「分析」といってもほとんど数えるだけ。やる気が出たらもっとまともなのを追加する。

f:id:tatzyr:20151028010231p:plain:w147

アプリのカテゴリ

トップセールス上位のアプリのカテゴリ。

件数 カテゴリ
161 ゲーム
24 ソーシャルネットワーキング
5 ブック
5 エンターテインメント
2 ミュージック
1 フード/ドリンク
1 ユーティリティ
1 スポーツ

ゲーム(81%)、ソーシャルネットワーキング(12%)が強い。ソーシャルネットワーキングはほとんどLINEと出合い系。

categories = data.map {|entry| entry[:category] }
categories.uniq.group_by {|category| categories.count(category) }

アプリの販売価格

トップセールス上位のアプリの価格。

件数 価格
198 無料
1 840円
1 120円

198件(99%)のアプリが無料アプリ。840円のアプリはMinecraftで、120円のアプリはネオモンスターズ。両方ともアプリ内課金はある。

Minecraft: Pocket Edition

Minecraft: Pocket Edition

  • Mojang
  • ゲーム
  • ¥840

ネオモンスターズ

ネオモンスターズ

  • NTT Resonant Inc.
  • ゲーム
  • ¥120

Rubyコード

prices = data.map {|entry| entry[:price] }
prices.uniq.group_by {|price| prices.count(price) }

アプリが多くランクインしている販売元

ある販売元が何個のアプリをTop 200にランクインさせることができたか。2個以上の販売元のみ示す。

アプリ数 販売元
18 BANDAI NAMCO Entertainment Inc.
14 LINE Corporation
11 SQUARE ENIX INC
9 SEGA CORPORATION
7 Voltage inc.
6 KONAMI
6 COLOPL, Inc.
6 DeNA Co., Ltd.
4 King.com Limited
4 GungHo Online Entertainment, Inc.
4 ASOBIMO,Inc.
3 Electronic Arts
3 Ateam Inc.
3 Applibot Inc.
3 CYBIRD Co., Ltd.
2 Marvelous Inc.
2 KLab Inc.
2 Happy Elements K.K
2 SHUEISHA Inc.
2 D2C Inc.
2 Pokelabo, Inc.
2 Asobism
2 Cygames, Inc.
2 gumi Inc.
2 Supercell

Top 200に2個以上のアプリが入っているのは25社。残り79社は1つのアプリしかTop 200に入っていない。

Rubyコード

authors = data.map {|entry| entry[:author] }
authors.uniq.group_by {|author| authors.count(author) }

どの販売元の売上高が最も多いか

アプリ売上がべき乗則に従うと仮定したとき、どの販売元の累計売上高が最も多いか。

k位のアプリの売上が1/kと仮定したときの販売元ごとの累計売上高Top 20を示す。

順位 販売元 累計売上高
1 mixi, Inc 1.000
2 LINE Corporation 0.734
3 SQUARE ENIX INC 0.596
4 GungHo Online Entertainment, Inc. 0.541
5 COLOPL, Inc. 0.415
6 BANDAI NAMCO Entertainment Inc. 0.365
7 SEGA CORPORATION 0.150
8 KONAMI 0.128
9 DeNA Co., Ltd. 0.123
10 Sumzap Inc. 0.100
11 Aniplex Inc. 0.091
12 Machine Zone, Inc 0.083
13 gumi Inc. 0.078
14 Supercell 0.068
15 Happy Elements K.K 0.068
16 Cygames, Inc. 0.058
17 KLab Inc. 0.055
18 Applibot Inc. 0.050
19 King.com Limited 0.049
20 Voltage inc. 0.049

1位: mixi, Inc ← わかる
2位: LINE Corporation ← わかる
3位: SQUARE ENIX INC ← !?

ということで、これはあまりいいモデルではないかも。

Rubyコード

authors = data.map {|entry| entry[:author] }
sales = Hash.new(0)
authors.each.with_index(1) do |author, i|
  sales[author] += 1.0 / i
end

sales

Zenfone 2 Laserの操作をiPhoneに近づける設定7つ

前提

  • なるべく追加でアプリをインストールせず、デフォルトのアプリを使う
  • 「見た目」ではなく「操作」をiPhoneに近づける
    • iOS風のテーマを入れるようなことはしない

「ホーム画面の管理」画面はホーム画面を下から上にスワイプして出す。

f:id:tatzyr:20151023163318p:plain:w320

1. タッチ操作バイブの無効化

ボタン等に触れるとブッっと震えるフィードバックを無効化する。

ホーム画面の管理 > システム設定 > 音声と通知 > その他の音 > タッチ操作バイブ(OFF)

2. ドロワー画面の無効化

オリジナルAndroidの操作性とは変わってしまうが、ドロワー画面はいらないので無効化する。 iPhoneのようにホーム画面にすべてのアプリを置いてフォルダ分けする。

f:id:tatzyr:20151023171117p:plain:w320

ホーム画面の管理 > ユーザー設定 > レイアウト > 1層モード

3. ページめくりエフェクトの変更

ホーム画面ページ移動時のアニメーションを、左右へスライドするだけのものに変更する。

f:id:tatzyr:20151023171609p:plain:w320

ホーム画面の管理 > エフェクト > クラシック

4. Spotlight風アプリ検索

ホーム画面を下にスワイプするとSpotlightのようなアプリ検索を呼び出せるようにする。

f:id:tatzyr:20151023165516p:plain:w320

ホーム画面の管理 > ユーザー設定 > スワイプダウン時の動作 > アプリ検索を開く

5. Chromeのタブとアプリの統合を無効化

Chromeのタブ一覧がタスク切り替え画面に出てきて混乱するので無効化する。

Chromeアプリ > メニューアイコン > 設定 > タブとアプリの統合 > オフ

6. キーボードの変更1

ATOK for ASUS英語キーボードは中行(ASDF…の行)がiPhoneのキーボードより右にずれている(「Spotlight風アプリ検索」の画像を見よう)。 Googleキーボードならずれていないのでそれを使う。 ついでにGoogle日本語入力をインストールしておくと英語/日本語の切り替えが楽になる。

f:id:tatzyr:20151023171010p:plain:w320

1. PlayストアからGoogle日本語入力をインストール
2. ホーム画面の管理 > システム設定 > 言語と入力 > 現在のキーボード > キーボードの選択 >Googleキーボード/Google日本語入力をON

7. キーボードの変更2

ATOKを使い続けたい人用の設定。

f:id:tatzyr:20151023170102p:plain:w320

ATOKアプリ > QWERTYキーボード > 数字キー表示 > 縦画面/横画面(両方チェックを外す)

RSpecで「while line = gets」をテストする方法

RSpecwhile line = getsのような標準入力のループをテストしたいことがある。

Kernel.#getsARGFをレシーバとしたメソッドの省略形なので、Rspecのモック機能でARGF.getsを書き換えれば良い。

テストしたいコード:

def foo
  ary = []
  while line = gets
    ary << line.chomp
  end
  ary
end

テストコード:

require "rspec"
require "stringio"
require_relative "foo"

describe "foo" do
  it "標準入力を行ごとに配列にして返す" do
    inputs = StringIO.new("line1\nline2\n")
    allow(ARGF).to receive(:gets) { inputs.gets }
    expect(foo).to contain_exactly("line1", "line2")
  end
end

同様に、while line = Readline.readlineなどの場合にもこの方法は使える。

テストしたいコード:

require "readline"

def bar
  ary = []
  while line = Readline.readline
    ary << line.chomp
  end
  ary
end

テストコード:

require "rspec"
require "stringio"
require_relative "bar"

describe "bar" do
  it "標準入力を行ごとに配列にして返す" do
    inputs = StringIO.new("line1\nline2\n")
    allow(Readline).to receive(:readline) { inputs.gets }
    expect(bar).to contain_exactly("line1", "line2")
  end
end

rbenvのインストールにはrbenv-installerを使おう

rbenv-installerを使うとrbenvとそのプラグインを同時に導入できる。

rbenvを入れたあとにruby-buildを入れて…… rehashして……」「rbenv-gem-rehashプラグインも入れておくか……」みたいな面倒な手順が必要なくなる。

https://github.com/fesplugas/rbenv-installer

GitHub - fesplugas/rbenv-installer: Install rbenv and friends and update all of them when you want to ...

rbenv-installerのインストール

以下のコマンドでrbenv本体とruby-buildrbenv-default-gemsrbenv-gem-rehashrbenv-updateなどのプラグインが一発でインストールされる。

$ curl https://raw.githubusercontent.com/fesplugas/rbenv-installer/master/bin/rbenv-installer | bash

次に、以下のコードを~/.bash_profile~/.bashrcに追記してbashを再起動(これは通常のrbenvと同じ)。

# rbenv
export RBENV_ROOT="${HOME}/.rbenv"
if [ -d "${RBENV_ROOT}" ]; then
  epxport PATH="${RBENV_ROOT}/bin:${PATH}"
  eval "$(rbenv init -)"
fi

rbenv-installerで入る便利なプラグイン

rbenv-gem-rehashのおかげで、いちいちターミナルでrbenv rehashを叩かなくてもよくなる。

また、rbenv-updateのおかげで、ターミナルでrbenv updateを叩くと以下のようにrbenvとすべてのプラグインが一括で更新できるようになる。

$ rbenv update
updating rbenv
 |  Already up-to-date.

updating rbenv-bootstrap
 |  Already up-to-date.

updating rbenv-default-gems
 |  Already up-to-date.

updating rbenv-gem-rehash
 |  Already up-to-date.

updating rbenv-installer
 |  Already up-to-date.

updating rbenv-update
 |  Already up-to-date.

updating rbenv-use
 |  Already up-to-date.

updating rbenv-vars
 |  Already up-to-date.

updating rbenv-whatis
 |  Already up-to-date.

updating ruby-build
 |  Already up-to-date.

reloading rbenv
 |  done

この実行例を見てわかるが、他にも様々な便利なプラグインがインストールされる。

個々の紹介はしないけどrbenv-installerを使うとすべて使えるようになるので試してみてほしい。

Rubyの「if a = b」とSwiftの「if let a = b」は意味が異なる

SwiftRubyに似ていると言われることがある。(c.f. Swiftは○○に似ている)。 しかし、見た目は似ているけれど意味が異なる構文がある。 その一つに、代入を伴うif文がある。

Rubyif a = b

実行すると"false"が出力される。

b = false

if a = b
  puts("true")
else
  puts("false")
end

Swiftif let a = b

実行すると"true"が出力される。

let b:Bool?
b = false

if let a = b {
    println("true")
} else {
    println("false")
}

どうしてこうなった

Rubyのifの動作

Rubyif a = bは次の動作をする。

  1. 代入
  2. 分岐

それぞれを分けて書くとこうなる。

b = false

# 1. 代入
condition = (a = b) #=> false

# 2. 分岐
if condition
  puts("true")
else
  puts("false")
end

まず、a = bで代入が行われる。 この部分がfalseに評価され分岐条件になるので、"false"が出力される。

Swiftのif動作

一方、Swiftif let a = bは次の動作をする。

  1. Optionalの判別
  2. 分岐
  3. 代入

それぞれを分けて書くとこうなる。

let b:Bool?
b = false

// 1. Optionalの判別
let condition = (b != nil) // true

// 2. 分岐
if condition {
    // 3. 代入
    let a = b!
    println("true")
} else {
    println("false")
}

まず、b != nilでOptionalの判別が行われる。 分岐条件がtrueなので、let a = b!でforce unwrap&代入した後、"true"が出力される。

これは「optional binding」と呼ばれる機能で、Optional型の変数をunwrapする方法の一つ。