excelで住所を元に分類する

VBAも関数もあまり分からないので、以下でごり押し。

・B2に住所が書いてあるとする。
・countifでマッチングして、結果をifで条件分岐するだけ。。
・分類したい住所の数だけifが増えていく。。。

=IF(COUNTIF(B2, "埼玉*"), "埼玉", IF(COUNTIF(B2, "北海道*"), "北海道", IF(COUNTIF(B2, "東京*"), "東京", IF(COUNTIF(B2, "静岡*"), "静岡", "分類無し"))))

以下はメモ用。
sheet1がデータシートとする。

=IF(COUNTIF(Sheet1!C:C, C3), INDEX(Sheet1!A:A, MATCH(C3, Sheet1!C:C, 0)), "")

参考:http://otasuke.goo-net.com/qa5447508.html

prototype.jsのサンプル

ちょっとprototype.jsを使う機会があったので、忘れないように使い方を書いておこう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Sample</title>
<script type="text/javascript" src="/javascript/prototype.js"></script>
<script type="text/javascript">

var numCount = 0;
var isRequestSend = false;

function request() {
  if(!isRequestSend) {
    isRequestSend = true;
    var data = {q: 'パラメータ', a: numCount};
    var req = new Ajax.Request(
      'test.php',
      {
        method : 'get',
        parameters: $H(data).toQueryString(),
        onSuccess : onSuccessHandler,
        onFailure : onFailureHandler
      }
    );
  }
}

function onSuccessHandler(res) {
  var data = eval( "(" + res.responseText+ ")" );
  insertResult(data);
  numCount++;
  isRequestSend = false;
}


function onFailureHandler() {
  isRequestSend = false;
}

function insertResult(data){
  for(var i=0; i<data.length; i++) {
    var node = document.createElement("a");
    node.href = 'index.html';
    var d = data[i];
    node.innerHTML = "param1:"+ d['param1']+"param2:"+ d['param2'];
    var parent = $("result");
    parent.insertBefore(node, $("point"));
  }
}

</script>
</head>
<body>
<form>
<a onclick="request();">test</a>
</form>
<div id="result">
  <div>element1</div>
  <div id="point">element2</div>
</div>

</body>
</html>

「Rubyベストプラクティス」を読む4


4章はファイルやテキスト処理、正規表現をあつかっています。

目次:4章 テキスト処理とファイル管理

  • 4.1 状態トラッキングによる行指向のファイル処理
  • 4.2 正規表現
  • 4.3 ファイルを扱う
  • 4.4 tempfile標準ライブラリ
  • 4.5 テキスト処理戦略
  • 4.6 まとめ

4.1 状態トラッキングによる行指向のファイル処理

テキスト処理について、パーサーをつくる一つの方法が紹介されています。

# 本に載っているパーサーのコードを一部簡単にして書く
def parse(file_name)
  # 状態を保持する配列
  section = []
  
  File.foreach(file_name) do |line|
    # 状態を変更する処理
    # 状態はスタックで管理する
    case line
    when /^Start(\w+)/
      section.push $1
      next
    when /^End(\w+)/
      section.pop
      next
    end

    # 現在の状態で行う処理を決める
    case section
    when ["Font", "Metrics"]
      parse_char(line)
    when ["Kern"]
      parse_kern(line)
    when ["Metric"]
      next  # 処理しなくてもよいときは、nextで飛ばす
    else
      parse_generic(line) # その他一般の処理
    end
  end
end

4.2 正規表現

アンカーや量指定子の使い方が解説されている。

4.3 ファイルを扱う

テンポラリファイルを使う必要があるときは、自作するよりtempfileライブラリを使う。

require 'tempfile'
temp = Tempfile.new('foo.txt')
temp << some_data

# 読み込むときは、rewindで巻き戻す
temp.rewind
temp.each do |line|
  # 何かする
end

temp.close
# テンポラリファイルをすぐに削除したいときは、Tempfile#close! を使う。
# temp.close!

# テンポラリディレクトリを指定することもできる
a = Tempfile.new("foo.txt", "path/to/tmpdir")


File.foreachはEnumeratorを返すので、テキスト処理にEnumerableのメソッドが使える

sum = 0
File.foreach("data.txt"){|line| sum += line[/total: (\d+)/, 1].to_f}

# Enumeratorを使った処理
enum = File.foreach("data.txt")
sum = enum.inject(0){|s, r| s + r[/total: (\d+)/, 1].to_f}


ファイル処理をしていて、現在の行数を知りたいときは、File#linenoを使う

keys = []
values = []
File.open("foo.txt") do |file|
  file.each do |line|
    (file.lineno.odd? ? keys : values) << line.chomp
  end
end

Hash[*keys.zip(values).flatten]


ファイルに修正を施して保存する場合、"r+"でファイルを開いて処理するよりも、テンポラリファイルに処理結果を書いておいて、最後にリネームするやり方のほうが簡単。

require "tempfile"
require "fileutils"

file_name = "data.txt"
temp = Tempfile.new("working")
File.foreach(file_name) do |line|
  # 何かする
  temp << result
end

temp.close
# 安全のため、ファイルをバックアップしておく
FileUtils.cp(file_name, "#{file_name}.bak")
FileUtils.mv(temp.path, file_name)

「Rubyベストプラクティス」を読む3


3章の話は、2章と似たような優れたインターフェイスの作り方やRubyのリフレクションが紹介されています。

3章目次

  • BlankSlate:ステロイドで強化されたBasicObject
  • 柔軟なインターフェイスを作る
    • instance_eval()をオプション化する
    • method_missing()とsend()を使ってメッセージを扱う
    • 2つの目的を兼ねたアクセサ
  • オブジェクトごとの振る舞いを実装する
  • 既存のコードを拡張、変更する
    • 新しい機能を追加する
    • エイリアス経由で変更する
    • オブジェクトごとの変更
  • クラスとモジュールをプログラムで作る
  • フックとコールバックを登録する
    • 新しく追加された機能を検出する
    • 継承を補足する
    • Mix-inを補足する

BlankSlate:ステロイドで強化されたBasicObject

BlankSlateというライブラリの紹介。メソッドを公開、非公開する仕組みをもったクラス。
仕組みは、メソッドを非公開にするときは、Module#instance_methodで、UnboundMethodを取得しておいてから、undef_methodでメソッドを削除する。
メソッドを公開するときは、取得しておいたUnboundMethodをModule#define_methodに与えて、メソッドを定義する。

柔軟なインターフェイスを作る

instance_eval()をオプション化する

2章で紹介した、instance_evalを使ってselfを変えるAPIはカッコイイけど、selfが変わることが都合が悪い時がある。

class Document
  def self.generate(file, &block)
    d = Document.new
    d.instance_eval(&block)
    d.write(file)
  end

  def text(arg)
    # 何かする
  end
end

class Foo
  def bar
    "bar"
  end

  def use_document
    Document.generate("test.txt") do
      # selfがDocumentオブジェクトなので、下のFoo#bar呼び出しは使えない
      text "my document #{bar}"
    end
  end
end

こういうときは、ブロックに与えられた引数の個数を調べて、instance_evalを使うか、自身のオブジェクトを引数として与えるか
選択するようなコードを書ける。

class Document
  def self.generate(file, &block)
    d = Document.new
    block.arity < 1 ? d.instance_eval(&block) : block.call(d)
    d.write(file)
  end
end

class Foo
  def use_document
    Document.generate("test.txt") do |d|
      d.text "my document #{bar}"
    end
  end
end
method_missing()とsend()を使ってメッセージを扱う

method_missingでメソッド名を解析して、解析した文字列を、sendでメソッド呼び出しする例。
例えば、fill_and_strokeというメソッド呼び出しをmethod_missingでフックして、fillとstrokeのメソッドを呼び出すとか。
ActiveRecordのfind_by_*** とかと同じような仕組みの話だとおもう。

2つの目的を兼ねたアクセサ

メソッドに引数があれば、セッターとして動作して、引数がなければゲッターとして動作するメソッドの話。
これが必要になるのは、instance_evalを使ったAPIを提供しようとするとき、=の付くセッターでは、ローカル変数とみなされてしまうから。

Document.generate do
  # selfを指定しなければいけない
  self.font_size = 10
  text "the font size is #{font_size}"
end

# セッター、ゲッターとして動作するメソッドを作る
class Document
  def font_size(size=nil)
    return @font_size unless size
    @font_size = size
  end
end

Document.generate do
  # selfを指定しなくても書ける
  font_size(10)
  text "the font size is #{font_size}"
end

オブジェクトごとの振る舞いを実装する

特異クラスを使って、オブジェクト固有のメソッドを作る話

user = User.new
# 特異クラスの取得
singleton = class << user; self; end
# メソッド定義
singleton.__send__(:define_method, :logged_in?){ true }

既存のコードを拡張、変更する

新しい機能を追加する

オープンクラスなので、メソッドを追加で定義できるという話。

エイリアス経由で変更する

メソッドに機能を追加したいとき、Module#alias_methodを使ってメソッドの名前を変えて、追加の機能を実装した同名メソッドを作れる。

オブジェクトごとの変更

オブジェクトをextendして追加の機能を持たせることができる。クラス単位の変更となるModule#alias_methodと違って、こちらの方法はオブジェクトごとの変更になる。

クラスとモジュールをプログラムで作る

Class#newで匿名クラスを作れる。Class#newのブロック内でdefすれば、匿名クラスのインスタンスメソッドを作成できる。

def Mystery(secret)
  if secret == "chunky"
    Class.new do
      def message
        "You"
      end
    end
  else
    Class.new do
      def message
        "Don't"
      end
    end
  end
end

class Win < Mystery("chunky")
  def who_am_i
    "I am Win"
  end
end

class EpicFail < Mystery("smooth ham")
  def who_am_i
    "I am teh fail"
  end
end

フックとコールバックを登録する

新しく追加された機能を検出する

メソッド定義をmethod_addedでフックできる。

継承を補足する

継承はinheritedでフック出来る。

Mix-inを補足する

includeはincludedでフック出来る。本でも紹介されているけど、以下のようなincludedの使い方を結構見かける気がする。

module MyModule
  module ClassMethods
    def class_method
      # クラスオブジェクト用のメソッド
    end
  end

 def foo
    # インスタンス用のメソッド
  end

  def self.included(base)
    base.extend(ClassMethods)
  end
end

class Foo
  include MyModule
  # includedのおかげで、下のようにextendを書く必要はない
  # extend MyModule::ClassMethods
end

まとめ

章の最後では、この章で紹介されたテクニックを使ったNaiveCampingRoutesという面白いコードが書いてある。

「Rubyベストプラクティス」を読む2


2章はメソッドの作り方に関する章。
良いメソッドを書くための、引数やブロックの使い方が書かれています。

色々なメソッドの引数処理

# 標準的な引数
def distance(x1, y1, x2, y2)
  Math.hypot(x2-x1, y2-y1)
end

# オプションパラメータのある引数
def load_file(name, mode="rb")
  File.open(name, mode)
end

# 疑似キーワード引数
# 下のように呼べる
# story(animal: "Tiger", person: "Yankee Doodle")
def story(options)
  # デフォルト値を持たせて、mergeするやり方もある。
  options = {person: "person", animal: "animal"}.merge(options)
  "#{options[:person]} went to town, riding on a #{option[:animal]}}"
end

# 引数を配列として使う
def distance2(*points)
  distance(*points.flatten)
end

ブロックを使う

イテレートとか、前処理や後処理があるメソッドはブロックを使うようにすると綺麗になる。
イテレートのメソッドを定義するときは、Enumerableが使えるか検討する。
コールバックもブロックを使ってできる。&blockでブロックを受け取り、あとで呼び出す。
また、以下のようにinstance_evalを使って、APIをきれいにできる。

# 以下のようなコードで使用するオブジェクトがあるとする
# server = Server.new
# server.handle(/hello/){"hello"}
# server.handle(/bye/){"bye"}
# server.run
#
# instance_evalを使って、以下のように呼び出せるようにする
# Server.run do
#   handle(/hello/){"hello"}
#   handle(/bye/){"bye"}
# end

class Server
  def self.run(port=3333, &block)
    server = Server.new(port)
    server.instance_eval(&block)
    server.run
  end
end

「Rubyベストプラクティス」を読む1


数か月前に、一度「Rubyベストプラクティス」を読んでいたけれど、良い内容だったので
復習のために、もう一度読みなおしてみる。大事そうなところを日記に書いておこう。

まずは1章から、テストの話。

メソッドを分割する

本に載っている例。以下のようなコードをテストするとする。

class Questioner
  # questionの文字列を出力して、標準入力を待つ。
  # 入力の内容によって、以下のような動きをする
  # y Y Yes YeS YES yes など => trueを返す。
  # n N No nO など => falseを返す。
  # それ以外 => もう一度 askを呼ぶ。
  def ask(question)
    puts question
    response = gets.chomp
    case(response)
    when /^y(es)?$/i
      true
    when /^no?$/i
      false
    else
      puts "I don't understand."
      ask question
    end
  end
end

まずはテストしやすいように、コードを分割する。この場合、getsをほかの部分と分離させる。

class Questioner
  def ask(question)
    puts question
    response = yes_or_no(gets.chomp)
    response.nil? ? ask(question) : response
  end

  def yes_or_no(response)
    case(response)
    when /^y(es)?$/i
      true
    when /^no?$/i
      false
    end
  end
end

これで、yes_or_noメソッドは簡単にテストできる。例えば、minitestを使えば

def setup
  @questioner = Questioner.new
end

def test_yes
  %w(y Y Yes YES yes).each do |yes|
    assert @questioner.yes_or_no(yes), "#{yes.inspect} expected to parse as true"
  end
end

とできる。askメソッド以外はテスト出来るようになる。

スタブを使う

次に、askメソッドを呼び出しているようなメソッドをテストする場合。

class Questioner
  def inquire_about_happiness
    ask("Are you happy?") ? "Good" : "That's Too Bad"
  end
end

askは内部でgetsを使っているので、テストしにくい。このようなときは、特異メソッド定義を使って、askをスタブで置き換える。

def setup
  @questioner = Questioner.new
end

def test_yes
  def @questioner.ask(question); true; end
  assert_equal "Good", @questioner.inquire_about_happiness
end

def test_no
  def @questioner.ask(question); false; end
  assert_equal "That's Too Bad", @questioner.inquire_about_happiness
end

モックを使う

putsやgetsを使っているaskメソッドをテストするには、IOオブジェクトのように振舞うオブジェクトを使えばいい。
まずはputsやgetsを、IOオブジェクトへのメソッド呼び出しに書き直す。

class Questioner
  def initialize(in=STDIN, out=STDOUT)
    @input, @output = in, out
  end

  def ask(question)
    @output.puts question
    response = yes_or_no(@input.gets.chomp)
    response.nil? ? ask(question) : response
  end
end

テストではIOオブジェクトをStringIOなどにすれば、askメソッドもテストできる。

def setup
  @in = StringIO.new
  @out = StringIO.new
  @questioner = Questioner.new(@in, @out)
  @question = "Are you happy?"
end

def test_ask
  @in << "yes"
  @in.rewind
  assert @questioner.ask(@question), "Expected yes to by true";
  assert_equal "#{@question}\n", @output.string
end

ActionScriptとJavaScriptの連携(ExternalInterface)

ActionScriptからJavaScriptを呼んだり、またはその逆をやりたいときはExternalInterfaceを使えばいいんだけど、今までやったことなくて、少しハマったのでメモしておく。

ActionScriptからJavaScriptの関数を呼ぶ。

ExternalInterface.callを使う。

Security.allowDomain(ExternalInterface.call("function() { return location.hostname }"));
// javascriptのalertを呼ぶ。
ExternalInterface.call("alert('hello')");

JavaScriptからActionScriptの関数を呼ぶ。

ExternalInterface.addCallbackで、JavaScriptから呼び出したい関数を登録しておく。
JavaScript側で、埋め込まれたSWFObjectを取得して、その取得したオブジェクトに対して、addCallbackで登録しておいたメソッドを呼び出す。

// ActionScript側
//  第1引数が、JavaScript側で呼び出すときの関数名になる。
//  第2引数が、登録する関数。 第1引数と第2引数の関数名は同じでなくてもよい。
ExternalInterface.addCallback("doSomething", doSomething);
public function doSomething():void {
  // 何かする。
}
// JavaScript側。swfobject.jsを使って埋め込んだとする。
var flashvars = {};
var params = {
                menu: "false",
		scale: "noScale",
		allowFullscreen: "true",
		allowScriptAccess: "always",
		bgcolor: "#FFFFFF"
	      };
var attributes = {
	        id:"AppTest",
                name:"AppTest"
};
swfobject.embedSWF("AppTest.swf", "altContent", "100%", "100%", "9.0.0", "expressInstall.swf", flashvars, params, attributes);

// この関数を呼べば、上で登録したActionScriptの関数が呼べる。
function callActionScript() {
    var swf = document.getElementById("AppTest");  // attributesのidを指定する。
    swf.doSomething();
}