Hatena::ブログ(Diary)

Learning to be Me

2009-03-15

[]「日本語WordNetデータベースを探索するフロントエンドプログラム」を Ruby で書き直した 03:38

日本語WordNetのデータベースを探索するフロントエンドプログラム - yanbe.diff - subtech を読んで使ってみようとしたが、Python 2.6 が手元のマシンに入っておらず、apt で探すもみつからず、絶望的な気分になり、Ruby で書き直してみた。

基本的にはそのまま。usage とかもコピペ。嵌まった点としては、Python では空のリストが偽であるということ*1

#!/usr/bin/ruby -Ku
# -*- coding: utf-8 -*-

require 'rubygems'
require 'sqlite3'

class WNJpn

  Word = Struct.new("Word",:wordid, :lang, :lemma, :pron, :pos)
  Sense = Struct.new("Sense",:synset, :wordid, :lang, :rank, :lexid, :freq, :src)
  Synset = Struct.new("Synset",:synset, :pos, :name, :src)
  SynLink = Struct.new("SynLink",:synset1, :synset2, :link, :src)

  def initialize(dbfile)
    @conn = SQLite3::Database.new(dbfile)
  end

  def get_words(lemma)
    @conn.execute("select * from word where lemma=?",lemma).map{|row|Word.new(*row)}
  end

  def get_word(wordid)
    Word.new(*@conn.get_first_row("select * from word where wordid=?",wordid))
  end

  def get_senses(word)
    @conn.execute("select * from sense where wordid=?",(word.wordid)).map{|row|Sense.new(*row)}
  end

  def get_sense(synset, lang="jpn")
    row = @conn.get_first_row("select * from sense where synset=? and lang=?",synset,lang)
    row ? Sense.new(*row) : nil
  end

  def get_synset(synset)
    row = @conn.get_first_row("select * from synset where synset=?",synset)
    row ? Synset.new(*row) : nil
  end

  def get_syn_links(sense, link)
    @conn.execute("select * from synlink where synset1=? and link=?",sense.synset,link).map{|row|SynLink.new(*row)}
  end

  def get_sys_links_recursive(senses, link, lang="jpn", depth=0)
    senses.each do |sense|
      syn_links = get_syn_links(sense, link)
      puts "#{'  ' * depth}#{get_word(sense.wordid).lemma} #{get_synset(sense.synset).name}" unless syn_links.empty?
      _senses = syn_links.map{|syn_link|get_sense(syn_link.synset2,lang)}.compact
      get_sys_links_recursive(_senses, link, lang, depth + 1)
    end
  end

  def main(word, link, lang='jpn')
    if words = get_words(word)
      sense = get_senses(words.first)
      get_sys_links_recursive(sense, link, lang)
    else
      STDERR.puts "(nothing found)"
    end
  end

  def self.print_usage
    puts <<-EOS
usage: wn.rb word link [lang]
    word      word to investigate

    link      syns - Synonyms
      hype - Hypernyms
      inst - Instances
      hypo - Hyponym
      hasi - Has Instances      mero - Meronyms
      mmem - Meronyms --- Member
      msub - Meronyms --- Substance
      mprt - Meronyms --- Part
      holo - Holonyms
      hmem - Holonyms --- Member      hsub - Holonyms --- Substance      hprt - Holonyms -- Part      attr - Attributes
      sim - Similar to      entag - Entails
      causg - Causes
      dmncg - Domain --- Category
      dmnug - Domain --- usage      dmnrg - Domain --- Region
      dmtcg - In Domain --- Category      dmtug - In Domain --- usage      dmtrg - In Domain --- Region      antsg - Antonyms

    lang (default: jpn)
      jpn - Japanese
      eng - English
    EOS
  end

  def close_db
    @conn.close
  end

end


if __FILE__ == $0
  if ARGV.length >= 2
    dbfile = "wnjpn-0.9.db"
    wnj = WNJpn.new(dbfile)
    wnj.main(*ARGV)
    wnj.close_db
  else
    WNJpn.print_usage
  end
end

*1:Ruby では偽ではない。

2008-11-20

[][]Netbeans 6.5 で Fast Debugger を使う 02:33

rails 書くときには、Netbeans をつかっていて、6.5が出たらしいので、入れた。

が、Ruby を選択した場合では Fast Debugger が動かなかったので*1、それを動かすまでのメモ。

現状

# gem list | grep ruby-debug-ide

ruby-debug-ide (0.4.1, 0.4.0, 0.1.10)

という状態では、

f:id:bubbles:20081121023303p:image

とか

f:id:bubbles:20081121022141p:image

という感じで、使えない*2

対処

そのため、

# sudo gem install ruby-debug-ide -v 0.3.1

とやってあげて、0.3.1 の ruby-debug-ide を入れてあげると、

f:id:bubbles:20081121023302p:image

という感じで、Netbeans 側から認識されて、無事使えるようになる。

*1jruby だと、うごくっぽい。

*2インストールをクリックしても、gem をインストールするにはルート権限が必要なため、失敗する

2008-10-29

[][][] java で memcached に入れたものを ruby で取り出す (その逆も) 23:51

タイトル通り。

memcached つかうほどのものをいじっていないのだけれども面白そうなのでメモ。

以下、java はno title

ruby は memcached-client を前提とする*1

普通につかう。

普通にサンプル通り書くと、java なら

String[] servers = { "localhost:11211", };

SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.initialize();

MemCachedClient mcc = new MemCachedClient();
mcc.set("one", 1);
mcc.set("two", 2);
mcc.get("one");

こんな感じ*2

ruby だと

require 'rubygems'
require 'memcache'

mem = MemCache.new("localhost:11211")

mem["three"] = 3
mem["four"] = 4
mem["three"]

こんな感じ。

普通に使う分にはこれで十分っぽい*3

java で入れたものを ruby から取り出す

でも、ここで java で入れたやつを ruby から取ろうとして、

p mem["one"]

とかやっても、

/opt/local/lib/ruby/gems/1.8/gems/memcache-client-1.5.0/lib/memcache.rb:214:in `load': marshal data too short (ArgumentError)

from /opt/local/lib/ruby/gems/1.8/gems/memcache-client-1.5.0/lib/memcache.rb:214:in `[]'

from mem.rb:8

とかそんな感じで、怒られる。

これは、ruby の memcache-client が値がマーシャルであることを期待しているため*4

該当個所はここらへん。


  ##
  # Retrieves +key+ from memcache.  If +raw+ is false, the value will be
  # unmarshalled.

  def get(key, raw = false)
    server, cache_key = request_setup key

    value = if @multithread then
              threadsafe_cache_get server, cache_key
            else
              cache_get server, cache_key
            end

    return nil if value.nil?

    value = Marshal.load value unless raw # ここでエラー!!!

    return value
  rescue TypeError, SocketError, SystemCallError, IOError => err
    handle_error server, err
  end

ちなみに話がずれるけど、get なんて使ってないのにここでエラーになっているのは、以下のようにエイリアスが定義されているから。

alias [] get

まあということで、

p mem.get["one",true]

とやってあげればOK、とおもいきや、java で入れた値の場合、

"\000\000\000\001"

という値が返ってきてしまう。

これに関しては、java の memcached ライブラリのサイトの http://www.whalin.com/memcached/HOWTO.txt:title にコメントがあって、

Multi-client Example:

=====================

If you need to support multiple clients (i.e. Java, PHP, Perl, etc.)

you need to make a few changes when you are setting things up:

// use a compatible hashing algorithm

pool.setHashingAlg( SockIOPool.NEW_COMPAT_HASH );

// store primitives as strings

// the java client serializes primitives

//

// note: this will not help you when it comes to

// storing non primitives

mcc.setPrimitiveAsString( true );

// don't url encode keys

// by default the java client url encodes keys

// to sanitize them so they will always work on the server

// however, other clients do not do this

mcc.setSanitizeKeys( false );

ということらしい。ということで、java 側を

String[] servers = { "localhost:11211", };

SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setHashingAlg( SockIOPool.NEW_COMPAT_HASH ); // ここと
pool.initialize();

MemCachedClient mcc = new MemCachedClient();
mcc.setPrimitiveAsString( true ); // ここと
mcc.setSanitizeKeys( false ); // ここ
mcc.set("one", 1);
mcc.set("two", 2);
mcc.get("one");

てな感じでかえてあげると、ruby からも値が取り出せるようになる。

ruby で入れたものをjava からとりだす

ruby の memcache-client の場合、値をマーシャルにして保存しようとするので、そうさせないようにする。

該当部分のコードは以下。

  def add(key, value, expiry = 0, raw = false)
    raise MemCacheError, "Update of readonly cache" if @readonly
    server, cache_key = request_setup key
    socket = server.socket

    value = Marshal.dump value unless raw # ここでマーシャルにしてる!!
    command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"

    begin
      @mutex.lock if @multithread
      socket.write command
      socket.gets
    rescue SocketError, SystemCallError, IOError => err
      server.close
      raise MemCacheError, err.message
    ensure
      @mutex.unlock if @multithread
    end
  end

つまり、ruby で

 mem.add("one","1",0,true)

として値を入れてあげれば java から

mcc.get("one");

という形でとりだせる。

ここで、注意しなきゃいけないのは、add の定義のなかで、

 command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"

となっている点。ruby の場合、

 1.size #=> 4
 "1".size #=> 1

なので、値は Fixnum とかじゃまずい*5

 mem.set("one",1,0,true) # 第二引数が String じゃない!

とかやるとエラーになる*6

サイズがあうものを入れると入れられちゃう分、気がつきにくそうで怖い。

つまり、以下はOK。

 mem.set("hoge",1234,0,true) # 第二引数が String じゃないけど、通る!

…。

まとめ

ruby の memcache-client は デフォルトだと値をマーシャルにして入れているということ。

マーシャルじゃなくて保存したいときには、add と get の引数に true を指定してあげればよい。

ruby,php,perlでmemcacheの中身を使い回す | Parse Errorとかをみる限りだと、perl とか php はそのまま入れてるっぽい。

*1gem install memcached-client で入るやつ

*2:import とかは省略

*3:多分

*4:ちなみに add するときもデフォルトでは、マーシャルにして入れている

*5:だからマーシャルにしてるのかなーとか思ったり

*6:というか反応が返ってこなくなる

2008-09-25

[][] will_paginate について 21:20

最近railsはじめた。

で、will_paginate*1

# sudo gem install will_paginate

で、入れたのだが、これだと古いものが入ってしまうらしい。

これは、Home ? mislav/will_paginate Wiki ? GitHub

The name of the will_paginate gem is now “mislav-will_paginate” because it’s being built by GitHub. If you have a gem named “will_paginate”, it is old and you are advised to remove it from your system.

という文章があったため判明した。

そこで、http://github.com/mislav/will_paginate/wikis/installationをみながら、

# sudo gem uninstall will_paginate
# sudo gem sources -a http://gems.github.com
# sudo gem install mislav-will_paginate

と、やってあげて、ねんがんのwill_paginateをてにいれた。

*1:railsでページ送りをするためのやつで、ググったところ一番メジャーっぽかったやつ

2008-08-15

[][][]ニコニコの右上でFortuneのデータつくった 16:16

まず、右上のコメント - ニコニコチャートから右上の発言集をとってくる。

require 'open-uri'

open("http://www.nicochart.jp/static/comment.html") do |f|
  f.each do |line|
    line.scan(/<dd>(.*?)<\/dd>/){|a|puts a.first.gsub(/<.*?>/,""); puts "%"}
  end
end

上記のスクリプト(ここではmigiue.rbとする)だと、標準出力にでてくるので適当なファイルにリダイレクト。

ruby migiue.rb > nico

で、nicoの中身は以下のような感じ。

ニコニコしている時のあなたが一番輝いていて、………好き。
%
泣くな!ニコニコしろ!!
%
たまに空見上げてみるのもいいもんだよ
%
みんな〜、ニコニコしてるかな?
%
右上の夏休みは一日もない。
%

これに対して、strfileする。

strfile nico nico.dat

これで完成。

で、このファイルを指定してfortuneを実行すれば、

bubbles% fortune nico
朝、目を覚まし、横を見るとなぜかニコニコが同じふとんに
bubbles%

という感じに。

あとは、ログイン時に表示されるように適当に.zshrcとか.bashrcをいじる。