今日はコレクションをやりました.良く使う割にはあんまり知らないことがあったなぁ
Comparable は interface で int compareTo(T obj) というメソッドを持っている.1,2,3とかa,b,cとか自然な順序を比較することができるというinterface.compareToで自分自身と比べる.Comparator も interface だけど,int compare(T obj1, T obj2) という風に,2つのオブジェクトを比較するためのインターフェース.Arrays.sort(someList, new SomeComparator()); という風に使う.
Object にある public method.変える時は適切にオーバーライドする.
意味として等しいか?を示す.
たとえば,50円が2つ入っているリストを持つオブジェクトと,100円が1つ入っているリストを持つオブジェクトは等しいか?
そのオブジェクトをどういう風に使いたいかによって,「等しさ」は異なる.使う人間が定義してやらないといけない.
オマケ 同値(等しさ)の定理は以下の3つ
public int hashCode()
つまり,無限に存在するオブジェクトを,int数だけの有限空間に無理矢理突っこむという関数.異なるオブジェクトが同じhashCodeを返してもいい.やらないし,やらないほうがいいけど
public int hashCode(){
return 0;
}
とか定数も禁止されてはいない.↑でやったMapやSetに値を入れる時のキーなどに使う.
1.5から導入された.1.4以前だと
List list = new ArrayList();
list.add("A");
list.add(new Integer(100));
String s = (String) list.get(0);
みたいに書かないといけない.
という理由から,「あるリストに決められたオブジェクトしか入れられない」と決めて,取るときのキャストを不要にした書き方を用意した.それがジェネリクス.こんなの
List<String> list = new ArrayList<String>();
コンパイルの時.
コンパイルした後のバイトコードにはジェネリクスの情報は入っていない(イレイジャー erasure).後方互換性(1.4でコンパイルできるものが1.5でコンパイルしてもちゃんと動く)のためにそうなっている.
List<Number> list = new ArrayList<Number>(); list.add(new Integer(100)); // できない
はコンパイルエラーになる.なぜか?「IntegerはNumberではない」から.ジェネリクスでは普通にやるとオブジェクトの継承関係を見てはくれない.
不便すぎるので,ワイルドカードを用意した
List<?> list = new ArrayList<Object>();
で何でも入れられるリストになる.
でもそれだと1.4と同じ状態になってしまうので,条件つきのワイルドカードを用意した
List<? extends Number> list = new ArrayList<Number>(); list.add(new Integer(100)); // できる!!
これで「Number型を持つもの」をリストに入れられる.同様に <? super T> でTの親ならを何でも入れられるリストを作れる.でも〜の親ってあんまり宣言する機会がない.
今,僕の行っている会社ではJavaの研修をやってくれるのです.素晴しいですね.せっかくなので覚えた部分をまとめてみました.今日はオートボクシングと列挙型です.
オブジェクト型とプリミティブ型を相互に変換する
軽くて速いよ!
コンパイル時にやる
できない.例えば秒数末尾が偶数ならnull,奇数ならオブジェクトを生成とか,コンパイル時に調べられない.
書ける.オートボクシング導入(1.5)より前ではこれは普通のことだった.method(int型); だと前者,method(Integer型); だと後者が呼ばれる.
twitterで,あるユーザーが作成したリストに含まれているメンバーを一括followするrubyスクリプトです.ruby1.9で動作確認しました.
# -*- coding: utf-8 -*-
require 'logger'
require 'rubytter'
LOG = Logger.new('follow_user_on_the_list.log')
USER = 'niku_name' # your twitter account
PASS = '' # your twitter password
# ex) follow list "http://twitter.com/hdhsakym/robocup"
TARGET_USER = 'hdhsakym'
TARGET_LIST = 'robocup'
# oauth = Rubytter::OAuth.new(CONSUMER_KEY, CONSUMER_SECRET)
# access_token = OAuth::AccessToken.new(oauth.create_consumer, ACCESS_TOKEN, ACCESS_SECRET)
# @client = OAuthRubytter.new(access_token) # 2010/05/16現在 OAuth だと list 操作が 404 Not Found になる
@client = Rubytter.new(USER, PASS)
def all_users
ary = []
next_cursor = -1
while next_cursor != 0
begin
res = @client.list_members(TARGET_USER, TARGET_LIST, {cursor:next_cursor})
rescue => ex
LOG.warn ex
sleep 10
retry
end
ary.concat res.users
next_cursor = res.next_cursor
LOG.info "next_cursor is #{next_cursor}"
sleep 60
end
ary
end
all_users.each do |user|
LOG.info "#{user[:screen_name]}処理中..."
begin
@client.follow user[:screen_name]
sleep 60
rescue => ex
LOG.warn ex
sleep 10
retry
end
end
6152円はN円札がM枚…とかいうアレです."6152.bill"で紙幣の単位と枚数のハッシュを返すようにしてみました.
class Integer
def bill
raise if self < 0
unit = [10000, 5000, 2000, 1000, 500, 100, 50, 10, 5, 1]
unit.inject({}) do |result,item|
remain = self - result.inject(0){ |r, i| r += i.inject(:*) }
result[item] = remain/item
result
end
end
end
0.bill # => {10000=>0, 5000=>0, 2000=>0, 1000=>0, 500=>0, 100=>0, 50=>0, 10=>0, 5=>0, 1=>0}
3.bill # => {10000=>0, 5000=>0, 2000=>0, 1000=>0, 500=>0, 100=>0, 50=>0, 10=>0, 5=>0, 1=>3}
6152.bill # => {10000=>0, 5000=>1, 2000=>0, 1000=>1, 500=>0, 100=>1, 50=>1, 10=>0, 5=>0, 1=>2}
18947.bill # => {10000=>1, 5000=>1, 2000=>1, 1000=>1, 500=>1, 100=>4, 50=>0, 10=>4, 5=>1, 1=>2}
begin
-1.bill # =>
rescue => ex
ex # => RuntimeError
end
twitter for iPhoneでは投稿に画像を添付することができ,設定で投稿先を選べます.通常はtwitpicとかyfrogになるのですが,google apps engineを利用して,自分のtumblrへ投稿することができました.
まずgithubからソースを取得します
git clone git@github.com:niku/twitter_for_iphone_gateway.git
取得したディレクトリのconfig.yamlに自分のtwitterアカウント情報とtumblrアカウント情報を記述します.
google application engineのアプリケーション名を決めて,config.ruに記述します.今twifigになっていますが,たぶん同じ名前は使えないんじゃないかな.記述したら,その名前でgaeにアプリケーションを作成しておきます.
ファイルをgaeにアップロードします.
appcfg.rb . update
twitter for iPhoneを起動したら
が出るので,印でくくった部分をタップ.以下も同じです.
ここは先ほどgaeへアップロードしたアプリの名前に置き換えてください.
https://"アプリケーション名".appspot.com/image
です.
twitter for iPhone から画像つきで投稿すると,twitterは http://twitter.com/niku_name/status/16783623668 のようになり,tumblrは http://tumblr.niku.name/post/725826838 のようになります.
http://developer.atebits.com/tweetie-iphone/custom-image/ にtwitter for iPhoneの画像投稿時に送る情報が載っていました.
http://www.tumblr.com/docs/en/api にtumblrのAPI情報が載っていました
簡単でした.そうrubytterならね.
# -*- coding: utf-8 -*-
require 'logger'
require 'time'
require 'uri'
require 'net/http'
require 'rubytter'
LOG = Logger.new(STDOUT)
USER = '' # your twitter account
PASS = '' # your twitter password
LIST_NAME = '' # create twitter list name ex) last-post-was-quarter-year-ago
PAST_TIME = Time.now - 60 * 60 * 24 * 90 # quarter year
#oauth = Rubytter::OAuth.new(CONSUMER_KEY, CONSUMER_SECRET)
#access_token = OAuth::AccessToken.new(oauth.create_consumer, ACCESS_TOKEN, ACCESS_SECRET)
#@client = OAuthRubytter.new(access_token) # 2010/05/16現在 OAuth だと list 操作が 404 Not Found になる
@client = Rubytter.new(USER, PASS)
begin
@client.list(USER, LIST_NAME)
rescue Rubytter::APIError
@client.create_list(LIST_NAME) # リストが無ければ作成する
end
def all_friends
ary = []
next_cursor = -1
while next_cursor != 0
begin
res = @client.friends(USER, {cursor:next_cursor})
rescue => ex
LOG.warn ex
sleep 10
retry
end
ary.concat res.users
next_cursor = res.next_cursor
LOG.info "next_cursor is #{next_cursor}"
sleep 60
end
ary
end
all_friends.select { |friend|
if friend.statuses_count == 0
true
else
Time.parse(friend.status.created_at) < PAST_TIME
end
}.tap{ |e| LOG.info "対象は#{e.size}人くらいいます" }.each{ |friend|
name = friend.screen_name
LOG.info "#{name}処理中..."
begin
@client.add_member_to_list(LIST_NAME, name) if @client.user(name).statuses_count != 0 # 0ポストの人はリスト登録に失敗する
@client.leave(name)
sleep 60
rescue => ex
LOG.warn ex
sleep 10
retry
end
}
通常のTwitterAPIではcursorというのを指定して値を全取得します.rubytterでcursorを使うにはfriendsの2番目の引数にハッシュで指定してやればOKでした.
# -*- coding: utf-8 -*-
require 'rubytter'
require 'oauth'
USER = ''
CONSUMER_KEY = ''
CONSUMER_SECRET = ''
ACCESS_TOKEN = ''
ACCESS_TOKEN_SECRET = ''
oauth = Rubytter::OAuth.new(CONSUMER_KEY, CONSUMER_SECRET)
access_token = OAuth::AccessToken.new(oauth.create_consumer, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
@client = OAuthRubytter.new(access_token)
def all_friends
ary = []
next_cursor = -1
while next_cursor != 0
res = @client.friends(USER, {cursor:next_cursor})
ary.concat res.users
next_cursor = res.next_cursor
p next_cursor
end
ary
end
p all_friends
twitterの自己紹介欄に「consadole」や「コンサドーレ」と書いてある人をリストに加えるスクリプトです. http://niku.name/20100516.html#p01 の改良版です.http://twpro.jp/ で検索した人も加えるようにしました.twproはajaxで値を返してくるので解析がちょっと面倒でした.API用意してくれないかな.コードは汚いけど使い捨てだからいいかなと思っています.
# -*- coding: utf-8 -*-
require 'uri'
require 'net/http'
require 'nokogiri'
require 'oauth'
require 'rubytter'
USER = 'niku_name'
PASS = ''
#CONSUMER_KEY = ''
#CONSUMER_SECRET = ''
#ACCESS_TOKEN = ''
#ACCESS_SECRET = ''
#PIN = ''
LIMIT = 500 # 500 は list 登録人数の上限値
SEARCH_WORDS = ['コンサドーレ', 'consadole']
LIST_NAME = 'consadole'
def get_members_from_lefthandle search_word
(0..(1/0.0)).inject([]) do |base, num|
uri = URI.parse("http://tps.lefthandle.net/search/#{num}/?s=#{URI.encode search_word}&sort=follower")
doc = Nokogiri::HTML(Net::HTTP::get(uri))
result = doc.search('div.tweetuser').map{|e| e['id']}
if result.empty?
break base
else
base.concat result
end
end
end
def get_members_from_twpro search_word
(1..(1/0.0)).inject([]) do |base, num|
result = Net::HTTP::post_form(URI.parse("http://twpro.jp/search/search/#{num}"), word:search_word).body.scan /<span class='name'><a href='http:\/\/twitter\.com\/(\w+)'>/
if result.empty?
break base
else
base.concat result
end
end
end
members = SEARCH_WORDS.map{ |search_word|
lefthandle = get_members_from_lefthandle(search_word)
twpro = get_members_from_twpro(search_word)
lefthandle.concat(twpro)
}.flatten.uniq
oauth = Rubytter::OAuth.new(CONSUMER_KEY, CONSUMER_SECRET)
access_token = OAuth::AccessToken.new(oauth.create_consumer, ACCESS_TOKEN, ACCESS_SECRET)
#client = OAuthRubytter.new(access_token) # 2010/05/16現在 OAuth だと list 操作が 404 Not Found になる
client = Rubytter.new(USER, PASS)
begin
client.list(USER, LIST_NAME)
rescue Rubytter::APIError
client.create_list(LIST_NAME) # リストが無ければ作成する
end
# リストに登録
members.each do |member|
p member
begin
client.add_member_to_list(LIST_NAME, member)
rescue => ex
p ex
next if client.user(member).statuses_count == 0 # 0ポストの人は登録に失敗する
sleep 10
retry
end
end
@bookscan_jpにスキャンを依頼したのですが,そのうち「Rubyベストプラクティス」のスキャン結果にページ抜けがあることに気づきました.
応援していますし,安い値段でやっている以上仕方ないのかなとは思いますが,ほぼ新刊の状態の本でこれだけ抜けられると,今後お願いするのには躊躇してしまいますね…
題にかもしれないと記述したのは,本の落丁の可能性は否定できないためです.あるいは元々こういうページ構成だったとか.この本をお持ちの方,まえがきの辺りの正しいページ構成はどのようになっているでしょうか?
連続したスクリーンショットは以下のような感じです
今日の朝の通勤電車です.1回目の電車は暖房が入っていて,2回目の電車は冷房が入っていました.なんだか不思議ですね.あれどうやって決めてるんだろう…運転手さんの裁量なのかな.