カレーの恩返し

おいしいのでオススメ。

ActiveRecordっぽいO/Rマッパーを作ってみた

github.com

ActiveRecordっぽいものを一度実装してみて本物と実装方法の違いを眺めるのが勉強になりそうだと思ったのでとりあえず作ってみました。

ついでにSQLite3のC言語APIRubyで実行できるようにする拡張ライブラリsqlite3_coreも作りました。

※記事中では読みやすさのために例外処理は省いています。


実装したのは以下のメソッドになります。

  • set_database(db_path, type)
    どの種類(type)のどこに保存されている(db_path)DBを利用するのか設定する。
  • TableClass.new(attribute)
    レコードオブジェクトを生成する。
  • TableClass#{column名}
    レコードオブジェクトのカラムデータを参照する。
  • TableClass#{column名}=
    レコードオブジェクトのカラムデータに書き込む。
  • TableClass.where(condition)
    conditionに合致するTableのレコードオブジェクトを全件取得する。
  • TableClass.all
    Tableのレコードオブジェクトを全件取得する。
  • TableClass.find(id)
    同一のidをもつTableのレコードオブジェクトを1件取得する。
  • TableClass#save
    レコードオブジェクトの情報をDBに保存する。
  • TableClass#update(attribute)
    レコードオブジェクトの更新をDBに反映させる。
  • TableClass#destroy
    レコードオブジェクトをDBから削除する。
  • TableClass.belongs_to(table)
    Tableがtableに所属しているリレーションをTableClassに反映させる。
  • TableClass.has_many(tables)
    Tableは複数のtableを所持しているリレーションをTableClassに反映させる。

 


それではひとつずつ見ていきます。

set_database(db_path, type)

どの種類(type)のどこに保存されている(db_path)DBを利用するのか設定する。

def set_database(db_path, type)
  $db = Object.const_get(type.to_s.capitalize).new(db_path)
end
  • DBの情報をグローバル変数として持たせることにしました。
  • type: :sqlite3のときはSqlite3.new(db_path)を実行するという単純な仕組みにしました。
irb(main):003:0> set_database('./db_test.sqlite3', :sqlite3)
=> #<Sqlite3:0x00007f86858fdc08 @db=#<Sqlite3Core:0x00007f86858fdbe0>>

 

TableClass.new(attribute)

レコードオブジェクトを生成する。

def initialize(args = {})
      @table_schema = $db.table_schema(self.class.table_name)

      define_column_name
      
      store_record_to(args)

      define_accessor_belongs_to
      define_accessor_has_many
  end

# レコードオブジェクトにnewの引数の値を格納
def store_record_to(info)
  @table_schema.keys.each do |column|
    if info.has_key?(column)
      instance_eval "@#{column} = #{info[column].inspect}"
    else
      instance_eval "@#{column} = nil"
    end
  end
end
  • instance_evalでレコードオブジェクトに初期値を与えるようにしました。
  • 引数infoでeachを回すのではなくテーブルのカラム名でeachを回しているので存在しない引数を受け取ると無視してくれます。
  • define_column_name, define_accessor_belongs_to, define_accessor_has_manyは後ほど説明します。
irb(main):007:0> User.new
=> #<User:0x00007f8a2316bdb8 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=nil, @name=nil, @age=nil>

irb(main):008:0> User.new(id:1, name: 'name01', age:20)
=> #<User:0x00007f8a24872ea8 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="name01", @age=20>

irb(main):021:0> User.new(id: 5, name: 'name05', height: 174, weight: 56)
=> #<User:0x00007f868582c428 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=5, @name="name05", @age=nil>

 

TableClass#{column名} / TableClass#{column名}=

レコードオブジェクトのカラムデータを参照/書き込みをする。

def define_column_name
  @table_schema.keys.each do |column|
    self.class.class_eval "attr_accessor :#{column}"
  end
end
  • attr_accessorで定義しました。
irb(main):009:0> user = User.new
=> #<User:0x00007f8a23124418 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=nil, @name=nil, @age=nil>

irb(main):010:0> user.id = 1
=> 1

irb(main):011:0> user.id
=> 1

irb(main):012:0> user.name = 'name01'
=> "name01"

irb(main):013:0> user.name
=> "name01"

irb(main):014:0> user.age = 20
=> 20

irb(main):015:0> user.age
=> 20

irb(main):016:0> user
=> #<User:0x00007f8a23124418 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="name01", @age=20>

 

TableClass.where(condition)

conditionに合致するTableのレコードオブジェクトを全件取得する。

def where(condition)
    objects = []
    
    if condition == :all
      condition = nil
    end

    records = $db.select(table_name, condition)
    records.each do |record|
      objects << self.new(record)
    end
    objects
end
  • selectで取得した内容を引数にTableClass.newして配列に格納しました。
rb(main):019:0> User.where(age: 20)
=> [#<User:0x00007f8a23160828 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>, 
#<User:0x00007f8a24888398 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=2, @name="bbb", @age=20>, 
#<User:0x00007f8a23139390 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=3, @name="ccc", @age=20>]

irb(main):020:0> User.where(age: 20, name: ['aaa', 'bbb'])
=> [#<User:0x00007f8a2310d740 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>, 
#<User:0x00007f8a230f50a0 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=2, @name="bbb", @age=20>]

 

TableClass.all

Tableのレコードオブジェクトを全件取得する。

def all
  where(:all)
end
  • TableClass.where(condition)を使いました。
irb(main):017:0> User.all
=> [#<User:0x00007f8a2484a048 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>, 
#<User:0x00007f8a24822c00 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=2, @name="bbb", @age=20>, 
#<User:0x00007f8a2301fea0 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=3, @name="ccc", @age=20>, 
#<User:0x00007f8a23818ff8 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=4, @name="ddd", @age=25>]

 

TableClass.find(id)

同一のidをもつTableのレコードオブジェクトを1件取得する。

def find(id)
  hash = $db.select(table_name, { id: id })
  return nil if hash.empty?

  self.new(hash[0])
end
  • そのままです。
irb(main):018:0> User.find(1)
=> #<User:0x00007f8a230446b0 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>

 

TableClass#save

レコードオブジェクトの情報をDBに保存する。

def save
  $db.insert(self.class.table_name, self.to_attr)
end
  • これもそのままです。
irb(main):021:0> User.find(5)
=> nil

irb(main):022:0> user = User.new(id: 5, name: 'name05', age: 100)
=> #<User:0x00007f8a230bf180 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=5, @name="name05", @age=100>

irb(main):023:0> user.save
=> true

irb(main):024:0> User.find(5)
=> #<User:0x00007f8a238320e8 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=5, @name="name05", @age=100>

 

TableClass#update(attribute)

レコードオブジェクトの更新をDBに反映させる。

def update(attribute)
    if $db.update(self.class.table_name, attribute, self.id)
      @table_schema.keys.each do |column|
        if attribute.has_key?(column)
          instance_eval "@#{column} = #{attribute[column].inspect}"
        end
      end

      return true
    end

    false
  end
  • $db.updateでDBを更新した後instance_evalでレコードオブジェクト自身も更新しています。
irb(main):028:0> user = User.find(1)
=> #<User:0x00007f8a2483aad0 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>

irb(main):029:0> user.update(age: 500000000)
=> true

irb(main):030:0> User.find(1)
=> #<User:0x00007f8a230f4178 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=500000000>

 

TableClass#destroy

レコードオブジェクトをDBから削除する。

def destroy
  $db.delete(self.class.table_name, self.id)
end
  • そのままです
irb(main):009:0> user = User.find(3)
=> #<User:0x00007f868598cc00 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=3, @name="ccc", @age=20>

irb(main):010:0> user.destroy
=> true

irb(main):011:0> User.find(3)
=> nil

 

TableClass.belongs_to(table)

Tableがtableに所属しているリレーションをTableClassに反映させる。

def belongs_to(table)
  if $db.table_schema(table_name).keys.include?("#{table}_id".to_sym)
    @@belongs_to_tables << table
  end
end

def define_accessor_belongs_to
  @@belongs_to_tables.each do |belongs_to_table|
    instance_eval <<~EOS
      def #{belongs_to_table}
        if @#{belongs_to_table}_id.nil?
          nil
        else
          Object.const_get('#{belongs_to_table}'.capitalize).find @#{belongs_to_table}_id
        end
      end
    EOS
  end
end
  • define_accessor_belongs_toTableClass.new内で実行されます。
  • クラス変数@@belongs_to_tablesにbelongs_toしたいテーブルをためておいてnew時にinstance_evalでメソッドを定義してます。
# post.rb
class Post < NonActiveRecord
  belongs_to :user
end

irb(main):019:0> post = Post.find(1)
=> #<Post:0x00007f86859363a0 @table_schema={:id=>:integer, :title=>:text, :content=>:text, :user_id=>:integer}, @id=1, @title="title1", @content="content1", @user_id=1>

irb(main):020:0> post.user
=> #<User:0x00007f86858fc0d8 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>

 

TableClass.has_many(tables)

Tableは複数のtableを所持しているリレーションをTableClassに反映させる。

def has_many(tables)
  if $db.table_schema(tables.to_s).keys.include?("#{self.to_s.downcase}_id".to_sym)
    @@has_many_tables << tables
  end
end

def define_accessor_has_many
  @@has_many_tables.each do |has_many_table| 
    instance_eval <<~EOS
      def #{has_many_table}
        Object.const_get(self.class.singularize('#{has_many_table}').capitalize).where(#{self.class.to_s.downcase}_id: self.id)
      end
    EOS
  end
end
  • だいたいbelongs_toと同じで定義されるメソッドの内容が違うだけです。
# user.rb
class User < NonActiveRecord
  has_many :posts
end

irb(main):015:0> user = User.find(1)
=> #<User:0x00007f868580eec8 @table_schema={:id=>:integer, :name=>:text, :age=>:integer}, @id=1, @name="aaa", @age=20>

irb(main):016:0> user.posts
=> [#<Post:0x00007f86859c6ce8 @table_schema={:id=>:integer, :title=>:text, :content=>:text, :user_id=>:integer}, @id=1, @title="title1", @content="content1", @user_id=1>, 
#<Post:0x00007f86859aef58 @table_schema={:id=>:integer, :title=>:text, :content=>:text, :user_id=>:integer}, @id=2, @title="title2", @content="content2", @user_id=1>]

 


 

感想

  • 今回実装した処理と本家の処理を見比べてどこが違うのかを調べていきたいと思った。
  • 本家のwhere句はメソッドチェインすると適切なSQL文が発行されるが、どう考えても謎なのでソースを漁ろうと思う。

【大学編入】Not情報系から情報系に編入が決まって不安な人がやっておくといいこと

Not情報系から情報系に編入が決まった方、おめでとうございます。
高専では情報系の科目が全然なかったけど本当にやっていけるんだろうか…と不安に思っている人はちょこちょこいると思います。

自分もそうだったので編入する前にこんなことをやっておくと(授業レベルでは)全然不安に思うことはないよってのを挙げてみたいと思います。 この分野が足りてないってのがあればコメントをもらえると助かります。

 

ブログを持つ

初めの記事は編入体験記とかでいいのでとりあえず技術系記事を書く媒体を持っておく。
以下の内容をやってみて「ここ面白いな」とか「ここもうちょっと深掘りしてみたいな」と思った内容を記事にまとめてみるのが良いと思います。

 

コンピュータシステムの理論と実装をやってみる

コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方

コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方

NAND素子からテトリスのようなアプリケーションが動く環境を作るをコンセプトに書かれている本です。
全てシミュレータ上で行えるのとTDDで作っていけるので独学しやすいと思います。

以下が気になる人におすすめです。

  • 論理回路は勉強したけど、論理回路とコンピュータが頭の中で繋がっていない
  • なんでアセンブラで書いた処理を正しくコンピュータが行ってくれるんだろう
  • 結局コンパイルってどんな処理が行われているんだろう

 

この解答を一通りまとめてみる

www.wantedly.com

webに関する情報科学の基礎知識がまとまっています。

何かのアプリケーションは作ったことあるけど何がどうなって動いてるのかよくわかってないって人におすすめです。

 

関数型言語に触れてみる

だいたいの大学で関数型言語を扱う授業はあるように思います。(プログラム言語論とか)
あらかじめ触れて概念や構文を理解しておくと授業を楽に理解できるようになると思います。
ちなみに筑波大学情報科学類はOCamlです。

 

(おまけ) 好きな言語をもっておく

編入してから確実に一度は好きな言語を聞かれるので好きな言語がない人は色々触って見つけておくといいかも。
その言語のパーフェクトシリーズやオライリーなどを1冊やっていると好きな言語が同じ人に会ったときに盛り上がれて良いと思います。

【大学編入】編入試験の成績開示方法・結果(筑波大学 情報科学類)

必要なもの

受験票 or 学生証

  

方法

5/1~6/30に筑波大学 教育推進部入試課に電話する

電話した日から2営業日以降で都合のつく平日(9~16時)に予約をする

筑波大学 本部棟2Fへ行き編入試験結果を見せてもらう

 

電話1本で予約ができるのは便利なんだけど、筑波大生以外はわざわざつくばまで来ないといけないのが欠点。

 

結果

学力検査(外国語) 90点
学力検査(専門科目) 149点

総合点 239点

 

志望動機や調査書の結果が入ってなかったので本当に関係ないのかも。

refineは一体どこで真価を発揮するのか

結論

ナンセンスな標準メソッドの実装にパッチを当てたくなったときにrefineは真価を発揮する。

 

以下、結論に至るまでに道のりです。


 

refineとは

Rubyにはrefineというどんなクラスのメソッドでもローカルで再定義できる機能がある。

[1] pry(main)> module StringExtensions
[1] pry(main)*   refine String do
[1] pry(main)*     def reverse
[1] pry(main)*       "esrever"
[1] pry(main)*     end
[1] pry(main)*   end
[1] pry(main)* end
=> #<refinement:String@StringExtensions>
[2] pry(main)>
[3] pry(main)> module StringStuff
[3] pry(main)*   using StringExtensions
[3] pry(main)*   "my_string".reverse
[3] pry(main)* end
=> "esrever"
[4] pry(main)> "my_string".reverse
=> "gnirts_ym"

refineを含んでいるStringExtensionsをusingで呼び出した場所からStringStuffの終わりまで再定義が有効になる。
refineの構文についての細かい説明は省略するので上記のコードからふわっと理解してほしい。

 

Railsに潜る

refineを知った最初は「めっちゃ便利じゃん!」と思ったけど、よく考えてみると大抵のことはサブクラス作って親クラスのメソッドオーバーライドを行えば解決しそうだし、refineを使わないと解決できない問題が思いつかなかった。

なのでrefineの真価を発揮する場面をRailsソースコードから調べてみた。
すると1ヶ所だけヒットした。

% find . -type f -name "*.rb" | xargs grep 'refine' -n
./activesupport/lib/active_support/core_ext/enumerable.rb:142:    refine Array do

 

# https://github.com/rails/rails/blob/32431b37704c0aaec06ae1a23e0d6091d6542fd2/activesupport/lib/active_support/core_ext/enumerable.rb 一部抜粋


# Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
#
# We tried shimming it to attempt the fast native method, rescue TypeError,
# and fall back to the compatible implementation, but that's much slower than
# just calling the compat method in the first place.
if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)
  # Using Refinements here in order not to expose our internal method
  using Module.new {
    refine Array do
      alias :orig_sum :sum
    end
  }

  class Array
    def sum(init = nil, &block) #:nodoc:
      if init.is_a?(Numeric) || first.is_a?(Numeric)
        init ||= 0
        orig_sum(init, &block)
      else
        super
      end
    end
  end
end

Ruby 2.4でArray#sumが追加されたけど要素にNumeric以外を入れるとTypeErrorになっちゃうからその辺イイ感じにしちゃうよー って感じのコメントが書いてあるような気がする。

読んでいく。

if Array.instance_methods(false).include?(:sum) && !(%w[a].sum rescue false)

Array#sumが存在するかつ[‘a’].sumが例外を吐くときTrue

using Module.new {
    refine Array do
      alias :orig_sum :sum
    end
  }

if文中はArray#sumArray#orig_sumという別名を定義

class Array
    def sum(init = nil, &block) #:nodoc:
      if init.is_a?(Numeric) || first.is_a?(Numeric)
        init ||= 0
        orig_sum(init, &block)
      else
        super
      end
    end
  end

要素がNumericのときはArray#orig_sum(再定義前のArray#sum)を使い、それ以外のときはEnumerable#sumでイイ感じにする (今回の目的はArray#sumの解読ではないのでこれ以上の追跡は省略する)

再定義前のArray#sumを使ってnativeに任せられるところは任せている。 さらにif文を抜けるとArray#orig_sumは使えなくなるため無駄に汚染することもない。

 

 

でもrefineなんか使わなくても以下のように直接Arrayクラスでprivateなorig_sumを宣言してしまえばいいじゃないかと思う人がいるかもしれない。

class Array
    alias :orig_sum :sum
    private :orig_sum

    def sum(init = nil, &block)
      # ...
    end
end

refineを使ったときと使わないときの差を考えてみる。

 

refineを使用したとき

irb(main):001:0> module ArrayExtension
irb(main):002:1>     refine Array do
irb(main):003:2*         alias :sumsum :sum
irb(main):004:2>     end
irb(main):005:1> end
=> #<refinement:Array@ArrayExtension>
irb(main):006:0> module Hoge
irb(main):007:1>     using ArrayExtension
irb(main):008:1>     [1,2,3].sumsum
irb(main):010:1> end
=> 6
irb(main):011:0> [1,2,3].sumsum
NoMethodError: undefined method 'sumsum' for [1, 2, 3]:Array
irb(main):012:0> [1,2,3].send(:sumsum)
NoMethodError: undefined method 'sumsum' for [1, 2, 3]:Array

usingで呼び出したmodule中のみでArray#sumsumが使えるがmodule外ではsendメソッドを使っても呼び出せない。

 

refineを使用してないとき

irb(main):001:0> class Array
irb(main):002:1>   alias :sumsum :sum
irb(main):003:1>   private :sumsum
irb(main):004:1> end
=> Array
irb(main):005:0> [1,2,3].sumsum
NoMethodError: private method 'sumsum' called for [1, 2, 3]:Array
irb(main):006:0> [1,2,3].send(:sumsum)
=> 6

Array#sumsumはprivateメソッドなので[1,2,3].sumsumはNoMethodErrorを吐くが、sendメソッドを使った場合Array#sumsumが呼び出せてしまう。

今回の目的は標準メソッドにパッチを当てることなのでパッチを当てる前のメソッドが呼び出せてしまうのは意に反している。

 

だから標準メソッドにパッチを当てたいときはrefineを使った方が良いよねっていうお話でした。

 

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

これを読みながら勉強中です。

【大学編入】単位認定・院試の推薦基準について(筑波大学 情報科学類)

単位認定申請書の提出が終わり一息ついたので忘れないうちに書いておこうと思います。
ここに書いてあるのは筑波大学 情報科学類の話なので、他大学/他学類だと異なるということが大いにあり得ることに注意してください。

単位認定について

情報科学類は高専1~5年の単位を最大80単位まで大学での単位として単位認定してくれます。
80単位というのは内部生とだいたい同じくらいで、 3年次は1日平均4コマ(1コマ75分)程度、4年次は卒業研究などの4年次でしか履修できない科目計9単位ほどを履修すれば卒業できます(できる見通しです)。

単位認定の流れは以下のようになっています。
編入生オリエンテーションで去年編入した3編生にアドバイスをもらいながら単位認定申請書を作成する(PCを持っていくことを推奨)

単位認定申請書が完成したら担任にアポを取り、個別面談で申請書の内容が妥当なのかチェックする

問題なければそのまま提出

単位認定の結果が通達される(夏頃)

自分は情報科学類に編入した高専の先輩がほぼいない&高専時の学科が電気情報工学科のダブルコンボできちんと単位認定されるのか不安だったのですが、 卒業研究と実験の単位を乱用することでなんとか80単位近くを申請することができました。
同じ学科から情報科学類に編入した先輩がいる人は 《魔法の言葉》「先輩これで申請通ってました」が使えるので単位認定が瞬間で終わるみたいです。羨ましい。

 

ここで気をつけないといけないのは高専でどんな成績をとっていても認定された単位の成績は「A」, 「B」, 「C」ではなく 「認」 になることです。 なので高専在学中の成績が全て「可」でも問題ないです。 これは去年?からの変更された制度らしく先生達も完全に把握しているわけではありませんでした。

編入試験でも成績はほぼ見ずにペーパーテストの出来が直接合否に繋がるっぽかったので高専の成績評価制度をよっぽど信用してないんだなーと思いました。

===== 追記 2018/02/13 =====
やっと単位認定の結果が返ってきました。申請した79.5単位全て認定されました。

 

院試の推薦基準について

筑波大学情報科学類からの院試の推薦条件は「成績AもしくはA+が6割を占めていること」です。

以前までは認定された単位の成績が「A」,「B」,「C」に変換されていたので高専時の成績がほぼ「優」である編入生は内部生よりも圧倒的に有利でした。

ですが、前項の単位認定後の成績が「認」になった影響で単位認定により得られた単位は推薦基準の分母から外れました。 よって3年次の成績のみで判定されるためAを取りまくらないといけないようです。

【PRML】演習問題 2.38 解答

二次形式を平方完成することで, (2.141)と(2.142)の結果を導出せよ.

この問題で使う数式

{  \mathcal N
\displaystyle
( x | \mu , \sigma ^ 2 ) = \frac{ 1 }{ ( 2 \pi \sigma ^ 2 ) ^ \frac{ 1 }{ 2 } } \exp \{ - \frac{ 1 }{ 2 \sigma ^ 2} ( x - \mu ) ^ 2 \} \tag{2.38}
}

{ \displaystyle
p ( \mathbf{ x } | \mu ) = \prod_{ n = 1 }^N p ( x_n | \mu )  =
\frac{ 1 }{ ( 2 \pi \sigma ^ 2 ) ^ \frac{ N }{ 2 } } \exp \{ - \frac{ 1 }{ 2 \sigma ^ 2} \sum_{ n = 1 } ^ N ( x_n - \mu ) ^ 2 \}  \tag{2.137}
}

{ \displaystyle
p ( \mu ) = \mathcal N ( \mu | \mu_0 , \sigma_0 ^ 2 ) \tag{2.138}
}

{ \displaystyle
p ( \mu | \mathbf{ x } ) \propto p ( \mathbf{ x } | \mu )  p ( \mu )  \tag{2.139}
}

{ \displaystyle
p ( \mu | \mathbf{ x } ) = \mathcal N ( \mu | \mu_N , \sigma_N ^ 2 ) \tag{2.140}
}

{ \displaystyle
\mu_N = \frac{ \sigma ^ 2}{ N \sigma_0 ^ 2 + \sigma ^ 2} \mu_0 + \frac{N \sigma_0 ^ 2}{ N \sigma_0 ^ 2 + \sigma ^ 2} \mu_{ML}   \tag{2.141}
}

{ \displaystyle
\frac{ 1 }{ \sigma_N ^ 2 } = \frac{ 1 }{ \sigma_0 ^ 2} + \frac{ N }{ \sigma ^ 2 }  \tag{2.142}
}

{ \displaystyle
\mu_{ML} = \frac{ 1 }{ N } \sum_{ n = 1 } ^ N x_n  \tag{2.143}
}


{ \displaystyle p ( \mu | \mathbf{ x } ) } を式変形し指数部を平方完成させて(2.141), (2.142) を導出する。

(2.139), (2.137), (2.138)より

{
\displaystyle
p ( \mu | \mathbf{ x } ) \propto p ( \mathbf{ x } | \mu )  p ( \mu ) = \prod_{ n = 1 }^N p ( x_n | \mu ) \mathcal N ( \mu | \mu_0 , \sigma_0 ^ 2 )
} {
\displaystyle
\qquad \quad = \frac{ 1 }{ ( 2 \pi \sigma ^ 2 ) ^ \frac{ N }{ 2 } } \exp \{ - \frac{ 1 }{ 2 \sigma ^ 2} \sum_{ n = 1 } ^ N ( x_n - \mu ) ^ 2 \} * \frac{ 1 }{ ( 2 \pi \sigma_0 ^ 2 ) ^ \frac{ 1 }{ 2 } } \exp \{ - \frac{ 1 }{ 2 \sigma_0 ^ 2} ( \mu - \mu_0 ) ^ 2 \}
} {
\displaystyle
\qquad \quad = \frac{ 1 }{ ( 2 \pi \sigma ^ 2 ) ^ \frac{ N }{ 2 } ( 2 \pi \sigma_0 ^ 2 ) ^ \frac{ 1 }{ 2 } } \exp \{ - \frac{ 1 }{ 2 \sigma ^ 2} \sum_{ n = 1 } ^ N ( x_n - \mu ) ^ 2 - \frac{ 1 }{ 2 \sigma_0 ^ 2} ( \mu - \mu_0 ) ^ 2 \}
}

指数部を { ( \mu - \mu_N ) ^ 2 } となるように平方完成させる。
また、{ p ( \mu | \mathbf{ x } ) }{ p ( \mathbf{ x } | \mu )  p ( \mu ) } は比例関係であるため{ \mu }と無関係な指数部は定数として無視できる。

指数部を取り出し展開させていくと

{
\displaystyle
\qquad \quad - \frac{ 1 }{ 2 \sigma ^ 2} \sum_{ n = 1 } ^ N ( x_n - \mu ) ^ 2 - \frac{ 1 }{ 2 \sigma_0 ^ 2} ( x - \mu_0 ) ^ 2
} {
\displaystyle
\qquad \quad = - \frac{ 1 }{ 2 \sigma ^ 2} \sum_{ n = 1 } ^ N ( x_n ^ 2 - 2 x_n \mu + \mu ^ 2 ) - \frac{ 1 }{ 2 \sigma_0 ^ 2} ( \mu ^ 2 - 2 \mu_0 \mu + \mu_0 ^ 2 )
} {
\displaystyle
\qquad \quad = - ( \frac{ N }{ 2 \sigma ^ 2} + \frac{1}{2 \sigma_0 ^ 2 } ) \mu ^ 2 + ( \frac{1}{\sigma ^ 2} \sum_{n=1}^N x_n + \frac{\mu_0}{\sigma_0 ^2} ) \mu +  \underbrace{( - \frac{1}{2 \sigma ^2} \sum_{n=1}^N x_n ^ 2 - \frac{1}{2 \sigma_0 ^2} \mu_0 ^2 )}_{const.}
} {
\displaystyle
\qquad \quad = - ( \frac{ N }{ 2 \sigma ^ 2} + \frac{1}{2 \sigma_0 ^ 2 } ) \{ \mu ^ 2 - \frac{1}{\frac{ N }{ 2 \sigma ^ 2} + \frac{1}{x \sigma_0 ^ 2 }} ( \frac{1}{\sigma ^ 2} \sum_{n=1}^N x_n + \frac{\mu_0}{\sigma_0 ^2} ) \mu + const. \}
} {
\displaystyle
\qquad \quad = - ( \frac{ N }{ 2 \sigma ^ 2} + \frac{1}{2 \sigma_0 ^ 2 } ) \{ \mu ^ 2 - 2 * \frac{\sigma_0 ^2 \sum_{n=1}^N x_n + \sigma ^2 \mu_0 }{ N \sigma_0 ^ 2 + \sigma ^2}  \mu + const. \}
} {
\displaystyle
\qquad \quad = - ( \frac{ N }{ 2 \sigma ^ 2} + \frac{1}{2 \sigma_0 ^ 2 } ) \{ \mu - \frac{\sigma_0 ^2 \sum_{n=1}^N x_n + \sigma ^2 \mu_0 }{ N \sigma_0 ^ 2 + \sigma ^2} \} ^ 2
} {
\displaystyle
\qquad \quad = - ( \frac{ N }{ 2 \sigma ^ 2} + \frac{1}{2 \sigma_0 ^ 2 } ) \{ \mu - \frac{\sigma ^2}{ N \sigma_0 ^ 2 + \sigma ^2} \mu_0 + \frac{\sigma_0 ^2 \sum_{n=1}^N x_n }{N \sigma_0 ^ 2 + \sigma ^2 } \} ^ 2
} {
\displaystyle
\qquad \quad = - ( \frac{ N }{ 2 \sigma ^ 2} + \frac{1}{2 \sigma_0 ^ 2 } ) \{ \mu - \{ \frac{\sigma ^2}{ N \sigma_0 ^ 2 + \sigma ^2} \mu_0 + \frac{ N \sigma_0 ^2  }{N \sigma_0 ^ 2 + \sigma ^2 } \frac{1}{N} \sum_{n=1}^N x_n \} \} ^ 2
}

(2.143)より

{
\displaystyle
\qquad \quad = - \frac{1}{2} ( \frac{ N }{ \sigma ^ 2} + \frac{1}{ \sigma_0 ^ 2 } ) \{ \mu - \{ \frac{\sigma ^2}{ N \sigma_0 ^ 2 + \sigma ^2} \mu_0 + \frac{ N \sigma_0 ^2  }{N \sigma_0 ^ 2 + \sigma ^2 } \mu_{ML} \} \} ^ 2
}

{
\displaystyle
\frac{1}{\sigma_N ^ 2} = - ( \frac{ N }{ \sigma ^ 2} + \frac{1}{ \sigma_0 ^ 2 } ) , \mu_N = \frac{\sigma ^2}{ N \sigma_0 ^ 2 + \sigma ^2} \mu_0 + \frac{ N \sigma_0 ^2  }{N \sigma_0 ^ 2 + \sigma ^2 } \mu_{ML}
} とおくと

{
\displaystyle
\qquad \quad = - \frac{1}{2 \sigma_N ^ 2} \{ \mu - \mu_N \} ^ 2 = \mathcal N ( \mu | \mu_N , \sigma_N ^ 2 )
}

となり(2.140)と等しくなるので(2.141), (2.142)を導出できた。

RDFをSPARQLを使ってRailsでDBっぽく扱ってみた

RDFをSPARQLで操作するハッカソンがあり、Activerecordを使ってのDB操作と同じようにRDFを扱いたかったのでactiverecordのメソッドっぽくラップしてみた。

まず以下をGemfileに追記する。

# Gemfile
...
gem 'active_attr'
gem 'sparql'

active_attr はテーブルを持たないモデルを少ない記述で作成できるようになるgem。
README読めば大体わかる。

sparql はSPARQLをrubyで叩くためのgem。


 

次にモデルを記述していく。
具体例として尾道市の飲食店のモデルを示す。

class Restaurant
  require "sparql/client"
  include ActiveAttr::TypecastedAttributes
  include ActiveAttr::BlockInitialization

  attribute :name, type: String
  attribute :address, type: String
  attribute :lat, type: Float
  attribute :lng, type: Float
  attribute :image, type: String

  def self.all
    restaurants = []
    results = self.all_query

    results.each do |result|
      restaurant = Restaurant.new do |r|
        r.name = result[:name]
        r.address = result[:address_name]
        r.lat = result[:lat].to_s.gsub('+', '')
        r.lng = result[:lng].to_s.gsub('+', '')
        r.image = result[:img]
      end
      restaurants << restaurant
    end

    restaurants
  end

  private

  def self.all_query
    client = SPARQL::Client.new("https://sparql.odp.jig.jp/api/v1/sparql")
    results = client.query('
      select ?id ?address_name ?img ?name ?lat ?lng {
      ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://purl.org/jrrk#CivicPOI>;
       <http://purl.org/jrrk#address> ?address.
      filter(regex(?address, "広島県尾道市"))
      ?s <http://odp.jig.jp/odp/1.0#genre> <http://odp.jig.jp/res/category/%E9%A3%9F%E3%81%B9%E3%82%8B>.
      ?s <http://purl.org/dc/terms/identifier> ?id.
      ?s ?p ?id.
      ?s <http://imi.ipa.go.jp/ns/core/rdf#住所> ?addresses.
      ?addresses <http://imi.ipa.go.jp/ns/core/rdf#表記> ?address_name.
      ?s <http://schema.org/image> ?img.
      ?s <http://www.w3.org/2000/01/rdf-schema#label> ?name.
      ?s <http://imi.ipa.go.jp/ns/core/rdf#地理座標> ?locate.
      ?locate <http://imi.ipa.go.jp/ns/core/rdf#緯度> ?lat.
      ?locate <http://imi.ipa.go.jp/ns/core/rdf#経度> ?lng.
      filter(lang(?name) = "ja")
    }
    ')

    results
  end
end

実装の流れとしては

  1. 尾道市の飲食店データを得るためのクエリをSPARQLで書く。(ここがメイン)
  2. クエリから結果を返すself.all_queryを書く。
  3. self.all_queryの返り値をマッピングしてモデルのレコードインスタンスを生成するself.allを書く。

これでRestaurant.allを実行すれば尾道市の飲食店が全件取得できるようになる。

SPARQL難しい。