読んでおきたい記事メモ

http://android-developers.blogspot.jp/2011/06/new-editing-features-in-eclipse-plug-in.html
http://android-developers.blogspot.jp/2011/05/introducing-viewpropertyanimator.html
http://android-developers.blogspot.jp/2011/03/identifying-app-installations.html
http://android-developers.blogspot.jp/2011/02/android-30-fragments-api.html
http://android-developers.blogspot.jp/2011/03/renderscript.html
http://android-developers.blogspot.jp/2011/07/custom-class-loading-in-dalvik.html
http://android-developers.blogspot.jp/2011/07/debugging-android-jni-with-checkjni.html
http://android-developers.blogspot.jp/2011/06/things-that-cannot-change.html
http://android-developers.blogspot.jp/2011/01/processing-ordered-broadcasts.html
http://android-developers.blogspot.jp/2010/12/its-not-rooting-its-openness.html
http://android-developers.blogspot.jp/2010/12/analytics-for-android-apps.html
http://android-developers.blogspot.jp/2010/12/new-gingerbread-api-strictmode.html
http://android-developers.blogspot.jp/2010/12/saving-data-safely.html
http://android-developers.blogspot.jp/2010/10/improving-app-quality.html
http://android-developers.blogspot.jp/2010/10/traceview-war-story.html
http://android-developers.blogspot.jp/2010/09/proguard-android-and-licensing-server.html
http://android-developers.blogspot.jp/2010/07/multithreading-for-performance.html
http://android-developers.blogspot.jp/2010/07/apps-on-sd-card-details.html
http://android-developers.blogspot.jp/2010/06/game-development-for-android-quick.html
http://android-developers.blogspot.jp/2010/05/dalvik-jit.html
http://android-developers.blogspot.jp/2010/05/be-careful-with-content-providers.html
http://android-developers.blogspot.jp/2010/04/multitasking-android-way.html
http://android-developers.blogspot.jp/2009/11/optimize-your-layouts.html
http://android-developers.blogspot.jp/2009/11/integrating-application-with-intents.html
http://android-developers.blogspot.jp/2009/05/drawable-mutations.html
http://android-developers.blogspot.jp/2009/02/android-layout-tricks-1.html
http://android-developers.blogspot.jp/2009/02/android-layout-tricks-2-reusing-layouts.html
http://android-developers.blogspot.jp/2009/03/android-layout-tricks-3-optimize-by.html
http://android-developers.blogspot.jp/2009/02/faster-screen-orientation-change.html
http://android-developers.blogspot.jp/2009/01/why-is-my-list-black-android.html

まとめ系ニュースを、さらにまとめて眺める系アプリ作った

https://play.google.com/store/apps/details?id=com.komamitsu.newstream

ちょっとした暇なときに、カジュアルなニュースを流し読みできるアプリです。

発言小町、知恵袋、はてなブックマーク、NAVERまとめの最新情報をまとめて読めます。

元々、AppStoreのNewsStormっぽいものを探していたのですがAndroidアプリで良さげなものがなかったので、
自分好みのものを作りました。機能を絞ってシンプルにしてあります。

各ニュースをクリックすると、普段使い慣れているWebブラウザで詳細を閲覧できるので、URLの共有等はそちらから行ってください。

また、電波が届かない場所でも、前回閲覧したニュースを楽しめるようにしてあります。

もともと個人用でやっつけで作ってあったのだけど、折角なので少し手直ししてリリース。

テトリスっぽいけどテトリスじゃないアプリを作った

https://play.google.com/store/apps/details?id=com.komamitsu.pentaris

以前作ってみたテトリスアプリは

This is a notification that your application, Ketris (Tetris clone), 
with package ID com.komamitsu.ketris, has been removed from 
the Google Play Store.

REASON FOR REMOVAL: Alleged copyright infringement
 (according to the terms of the Digital Millenium Copyright Act).

All violations are tracked. Serious or repeated violations of any nature will 
result in the termination of your developer account, and investigation and 
possible termination of related Google accounts. Please review the 
Developer Distribution Agreement and Content Policy to ensure that 
your applications are compliant with our policies.

ということでremoveされてしまったので、テトリステトリスたる四つのブロックを使わないように五つのブロック中心のゲームに変更して作り直してみた。

ゲームバランスは微妙... テトリスよりも悩みどころが多いのでやりごたえはあるけど、テトリス好きにそのままお勧めできるかどうか....

まぁ、ちょっと様子みる感じで。

[ocaml] OCamlでLispのサブセットっぽいのを作ってみた

https://github.com/komamitsu/OCaml-minilisp

先日、仕事が一区切りついてボーッとしていたところ、「そういえばLisp系の言語に疎いなぁ自分」とふと思ったので、簡単なLispのサブセットを作ってみた。いま見ると文法的にはSchemeぽい。


こんな感じでREPLしたり、

$ ./minilisp
minilisp> (define
(fib n x1 x2)
(if (<= n 0)
x1
(fib (- n 1) x2 (+ x1 x2))))
Func(fib, [n, x1, x2], Cond(Apply(<=, Var(n), Num(0)), Var(x1), Apply(fib, Apply(-, Var(n), Num(1)), Var(x2), Apply(+, Var(x1), Var(x2)))))

minilisp> (fib 100 0 1)
Num(3736710778780434371)


ソースファイルを読んで評価したりできる。

$ cat sample.minilisp 
(define
  (fib n x1 x2)
  (if (<= n 0)
      x1
      (fib (- n 1) x2 (+ x1 x2))))

(print (fib 100 0 1))


$ ./minilisp sample.minilisp 
Num(3736710778780434371)


フィボナッチ数列でも計算できれば良いか、くらいの感じだったので、気がついたらcarもcdrも実装してなかった(シンボルもない)という何だかやる気のない感じが... 気が向いたらlambdaとかprognとかも一緒に実装しようかなぁ。気が向いたら。

OPAMでインストールしたパッケージがocamlfindで見つからない件

OPAMいいですね。

http://d.hatena.ne.jp/wistery_k/20120909/1347164530 の記事を見て使い始めましたが、道具としてのOCamlが何かの壁を乗り越えそうな感じがしてます。

で、使っていて、たまにocamlfind -listの中にopamでインストールしたパッケージが出てこないことに気がつきました。

eval `opam config -env` でパス周りの設定を通しているので、こいつは .xxshrc辺りに書いておいてあげる必要がありそうです。


あと本題と関係ないけど、shell/opam_completion.sh とかを読み込んでおくと auto-completeが効くのでさらに便利。


追記(2012-10-08)

https://github.com/OCamlPro/opam に普通に載ってたわ...

sendBroadcast()のコスト

極端な話、普通のメソッド呼び出しとどの程度違うのか、と気になったので試してみた。

package com.komamitsu.bench;

import java.util.concurrent.Executors;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class BenchActivity extends Activity {
  private static final String ACTION_HOGE = "hogehoge";
  private static final int LOOP_COUNT = 10000;

  private volatile int countForIntent;

  private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      // Log.d("#####", "intent onReceive");
      countForIntent++;
    }
  };

  private volatile int countForThread;

  void onRecieveFromThread(String label) {
    countForThread++;
  }

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    findViewById(R.id.intent).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
          @Override
          public void run() {
            IntentFilter intentFilter = new IntentFilter(ACTION_HOGE);
            registerReceiver(receiver, intentFilter);
            Intent intent = new Intent(ACTION_HOGE);
            intent.putExtra("hoge", "hogehoge");

            long start = System.currentTimeMillis();
            Log.d("#####", "intent start: count=" + countForIntent);
            for (int i = 0; i < LOOP_COUNT; i++) {
              sendBroadcast(intent);
            }

            long end = System.currentTimeMillis();
            Log.d("#####", "intent end: count=" + countForIntent + ", time=" + (end - start));
            unregisterReceiver(receiver);
          }
        });
      }
    });

    findViewById(R.id.methodcall).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Executors.newSingleThreadExecutor().execute(new Runnable() {
          @Override
          public void run() {
            long start = System.currentTimeMillis();
            Log.d("#####", "methodcall start: count=" + countForThread);
            for (int i = 0; i < LOOP_COUNT; i++) {
              onRecieveFromThread("hogehoge");
            }

            long end = System.currentTimeMillis();
            Log.d("#####", "methodcall end: count=" + countForThread + ", time=" + (end - start));
          }
        });
      }
    });
  }
}

こんな感じで、10000回、メソッド呼び出しをしたり、ブロードキャストインテントを投げたりして時間を計ってみた。

結果、

09-19 01:22:08.264 D/#####   (16201): intent start: count=0
09-19 01:22:22.984 D/#####   (16201): intent end: count=10000, time=14728
09-19 01:22:25.325 D/#####   (16201): methodcall start: count=0
09-19 01:22:25.335 D/#####   (16201): methodcall end: count=10000, time=3

メソッド呼び出しはまぁ置いといて、ブロードキャストインテントを投げると一回辺り1.5msec近くかかる。

vmstat見てたら、freeが減ってanonが増えてた. swap outが必要な領域が増えたぽい。

リクルートコミュニケーションズの採用情報のところにあった問題

http://rikunabi-next.yahoo.co.jp/company/cmi0089954001/nx1_rq0008861224/

「相手の思考を推理する」過程をプログラムで表現する問題です。
開発言語は問いませんので、慣れ親しんだ開発環境をご用意ください。

A, B, C の3人が 1〜5の5枚のカードを使ってインディアンポーカーをします。
3人は、ランダムに1枚のカードを引いて額にかざします。
相手のカードは見えますが、自分のカードは見えません。

この状態で、A->B->Cの順番に、
■自分が1番大きい(MAX)
■自分は2番目に大きい(MID)
■自分が1番小さい(MIN)
■わからない(?)
を答えます。

一人でも答えがわかった場合、そこで終了となります。
「わからない」と答えた場合、回答権が次の人に移ります。
Cもわからない場合、再度Aに回答権が移ります。
3人ともウソを言ったり、適当に答えてはいけません。

例1「A=1 B=4 C=5」だった場合、
「A => MIN」で終了します。

例2「A=1 B=2 C=4」だった場合、
「A => ?, B => MID」で終了します。

Bは「Aがわからないなら、自分は5ではない」と考えるからです。

以上を踏まえて、
引数で「A=1 B=4 C=5」で実行すると「A => MIN」を出力
引数で「A=1 B=2 C=4」で実行すると「A => ?, B => MID」を出力
するようなコマンドラインのプログラムを作成してください。

なお、人数やカードの枚数がパラメーター化されていて、
さまざまなケースがシミュレーションできるコードが望ましいです。

※ヒント: 今回のケースでは、
「全員がわからない(無限ループ)」という結果にはなりません。 

三連休の最終日、ちょっと面白そうだったので暇つぶしに挑戦してみた。ちなみに書いたコードは公開してOKと許可頂き済み。

class Resolver
  def initialize(num_of_members, num_of_cards)
    @num_of_cards = num_of_cards
    @members = 'A'.upto('Z').to_a[0, num_of_members]
    @all_card_nums = 1.upto(num_of_cards).to_a
  end

  def resolve(cards, members_hist = [])
    @members.each do |member|
      if member_and_result = trackback_members_hist(cards, members_hist + [member])
        return members_hist.map{|m| {m => nil}} + [member_and_result]
      end
      members_hist << member
    end

    if members_hist.size > @members.size * 3
      nil
    else
      resolve(cards, members_hist)
    end
  end

  def get_visible_cards(cards, member)   
    visible_cards = cards.clone
    visible_cards.delete(member)
    visible_cards
  end

  def trackback_members_hist(cards, members_hist)
    # puts "#{'-' * 40} cards:#{cards}, members:#{members_hist} #{'-' * 40}"

    members_hist = members_hist.clone
    member = members_hist.pop
    visible_cards = get_visible_cards(cards, member)
    result = answer(visible_cards.values)
    return {member => result} if result  

    return nil if members_hist.empty?

    undecidable_nums = []
    visible_nums = @all_card_nums - visible_cards.values
    visible_nums.each do |visible_num|   
      assumed_cards = visible_cards.clone
      assumed_cards[member] = visible_num

      member_and_result = trackback_members_hist(assumed_cards, members_hist)
      undecidable_nums << visible_num unless member_and_result
    end

    visible_cards_min = visible_cards.values.min
    visible_cards_max = visible_cards.values.max

    result =
      case
      when undecidable_nums.all?{|x| x < visible_cards_min}
        :min
      when undecidable_nums.all?{|x| x > visible_cards_max}
        :max
      when undecidable_nums.all?{|x| visible_cards_min < x && x < visible_cards_max}
        :mid
      else
        return
      end

    return {member => result}
  end

  def answer(other_nums)
    min_start = nil
    min_end = nil
    mid_start = nil
    mid_end = nil
    max_start = nil
    max_end = nil

    1.upto(@num_of_cards).each do |i|
      if max_start
        max_end = i - 1 unless other_nums.include? i
      elsif mid_start
        if other_nums.include? i
          mid_end = i - 1
          max_start = i
        end
      elsif min_start
        unless other_nums.include? i
          min_end = i - 1
          mid_start = i
        end
      else
        min_start = i if other_nums.include? i
      end
    end

    min_end = @num_of_cards if min_start && min_end.nil?
    mid_end = @num_of_cards if mid_start && mid_end.nil?
    max_end = @num_of_cards if max_start && max_end.nil?

    case
    when min_start == 1 && max_end == @num_of_cards
      :mid
    when min_start == 1 && mid_end == @num_of_cards
      :max
    when min_end == @num_of_cards
      :min
    else
      nil
    end
  end
end

if __FILE__ == $0
  resolver = Resolver.new(4, 6)
  raise unless resolver.answer([1, 2, 3]) == :max
  raise unless resolver.answer([4, 5, 6]) == :min
  raise unless resolver.answer([1, 2, 6]) == :mid
  raise unless resolver.answer([1, 5, 6]) == :mid
  raise unless resolver.answer([3, 4, 5]).nil?
  raise unless resolver.answer([1, 3, 6]).nil?
  raise unless resolver.answer([1, 4, 6]).nil?
  raise unless resolver.answer([1, 4, 5]).nil?
  raise unless resolver.answer([2, 3, 6]).nil?
  raise unless resolver.resolve('A' => 1, 'B' => 2, 'C' => 5, 'D' => 6) == [{'A' => nil}, {'B' => :mid}]
  # p resolver.resolve('A' => 3, 'B' => 4, 'C' => 5, 'D' => 2)

  resolver = Resolver.new(3, 5)
  raise unless resolver.resolve('A' => 1, 'B' => 4, 'C' => 5) == [{'A' => :min}]
  raise unless resolver.resolve('A' => 1, 'B' => 2, 'C' => 4) == [{'A' => nil}, {'B' => :mid}]
  raise unless resolver.resolve('A' => 2, 'B' => 3, 'C' => 4) == [{'A' => nil}, {'B' => nil}, {'C' => :max}]

  exit if ARGV.size < 2

  print_result = proc do |x|
    case x
    when :min then 'MIN'
    when :max then 'MAX'
    when :mid then 'MID'
    when nil then '?'
    end
  end

  num_of_members = Integer(ARGV.shift)
  num_of_cards = Integer(ARGV.shift)
  cards = ARGV.inject({}){|a, kv| kvs = kv.split('='); a[kvs[0]] = Integer(kvs[1]); a}

  resolver = Resolver.new(num_of_members, num_of_cards)
  answer = resolver.resolve(cards)
  puts answer.map{|kv| k = kv.keys.first; "#{k} => #{print_result.call(kv[k])}"}.join(', ')
end

ちなみに今現在、会社の飲み会から帰宅して酔っ払ってるのでコピペミスがあるかも。