Hatena::ブログ(Diary)

Lev/Kanafソフトウェア開発室

2012-03-01

放置、復帰、そして……新学期

今週のお題「卒業」


あれから1年放置してしまいました…… 何やってるんでしょうね。

まぁ実際、更新してる暇なんてなかったわけです。書くことが無かったんじゃなくて、ありすぎて整理する暇が無かったんです、というありがちな言い訳をしておきます。


とりあえず色々ありすぎた中からザックリと抜粋してみます。


前の記事のタイトル「新学期」は既に前々学期だった件について。

本当にすいませんでした!


とりあえず、こんな感じです。

  • 卒研のOS制作はポシャりました。時間的に無理でした。
  • 計算可能性理論は勉強したけど全然分かった気がしないのでテスト受けませんでした。(滝汗)
  • 他はまぁ、大体そんな感じです。

そして言及さえされなかった前学期ですが、

  • 卒研でQualcommさんと提携してMPEG2システムの多重化ストリームを色々操作する開発をしました。実はユダヤ企業なQualcommさんと仲良くなれました。わーい。
  • なんか今までうまくいってなかった信号処理や電子回路関係の試験がようやく通りました。わーい。
  • 他はまぁ、大体そんな感じです。

そして来週から授業開始する新学期ですが、


そして、地味に進めていたスクリプトのプロジェクトですが、

  • wxWidgetsLuaで開発していたLevanaですが、スマートフォンでの動作が重要視され始めたので一時凍結しています
  • 代わりにSDL-1.3とBoostLuaをベースにした軽量高速なメディアエンジンLevの開発を始めました。
  • Levの開発開始に伴い、それをベースとしたタグベースのスクリプトエンジンKanafの開発を始めました。

ヘブライ語でLevは心(心臓)、Kanafは(単数形で)翼の意です。Google Codeで鋭意開発中です。

公式ページは、

です。

今後はLevやKanafの開発の様子や解説なんかを掲載できたらと思っていますが、何かと時間に追われているので再び放置になっても怒らないで下さい。ソフトウェア開発の方は着々と進んでいます。

2011-02-28

新学期

新学期始まりました。全然更新時間無いけど生存報告までに。


卒業研究で独自OSフルスクラッチで作る事になりました。

他に自然言語処理やら人工知能やら電気実験実習やらがあって、

間違いなく最もハードなセメスターで死にそうですが、

やっと本当に願った学びができそうです。卒業に向かって踏ん張ります。


あと、フランス語も勉強します。

あと、体育が必修科目で、卓球やりたかったけど何故か未経験のテニスをすることに。(←一番の不安要素)


更に数値解析やら計算可能性理論も一応受講申請してますが、

前の学期で幾つかの通らなかった単位の再履修とかで、この辺は未確定。

ていうか物理的に無理な気しかしない。


因みに、こっそりSF.netを抜けだしてGoogle Codeに乗り換え計画してて、

http://code.google.com/p/levana/

にプロジェクトページ作りましたが、上記の理由でOSとか作らなきゃいけないので、

こっちは長期停滞するかもです。

2011-01-21

Luaでusingディレクティブを実現する2

id:Akiva:20110120#1295539117[Luaでusingディレクティブを実現する1]の続きです。


昨日のエントリーで大体前置きは書いたので、コードを貼っておきます。

これもLevライブラリに取り込む予定なので本来MITライセンスですが、

掲載分のコードはパブリックドメイン扱いで結構なので適当にコピーして使って下さい。


最新版は、

http://levana.svn.sourceforge.net/viewvc/levana/trunk/lev/util.lua

からダウンロード可能です。(こっちは一応MITライセンス)

今回はリビジョン25に日本語でコメント入れておきます。


-----------------------------------------------------------------------------
-- Name:        util.lua
-- Purpose:     便利関数の定義
-----------------------------------------------------------------------------

-- 使用方法:
--   * using() : ルックアップ設定のクリア
--   * using(...) : ルックアップ設定の追加、但し ... はテーブルのリストとする

-- 依存関係
local _G = _G

-- モジュール登録
module 'util'


-- テーブル t から value を検索し、見つけたら削除
local find_and_remove = function(t, value)
  for i,j in _G.ipairs(t) do
    if (j == value) then
      _G.table.remove(t, i)
      return
    end
  end
end


-- テーブル(配列) t を逆順にしたものを返す
local reverse = function(t, ...)
  local r = {}
  for i,val in _G.ipairs(t) do
    _G.table.insert(r, 1, val)
  end
  return r
end


-- 変数名 varname で変数値のルックアップ
local lookup = function(env, varname)
  meta = _G.getmetatable(env)
  -- 最初はグローバル変数から探す
  if meta.__owner[varname] ~= nil then
    return meta.__owner[varname]
  end
  -- 無ければリストに登録されたテーブルから順番に探す
  for i,t in _G.ipairs(meta.__lookup) do
    if t[varname] ~= nil then
      return t[varname]
    end
  end
  -- それでも無ければ一段外のスコープの環境から探す
  if (meta.__parent == meta.__owner) then
    -- 一段外のスコープとグローバル環境が同じなら探す意味が無い
    -- non sense to look up twice for the same table
    return nil
  end
  return meta.__parent[varname]
end


-- 変数名 key で 値 value をグローバル変数として代入
local substitute = function(env, key, value)
  meta = _G.getmetatable(env)
  meta.__owner[key] = value
end


-- 代替usingディレクティブ
function using(...)
  -- 最初に呼び出し元の環境と、環境のメタテーブルを取得
  local env  = _G.getfenv(2)
  local meta = _G.getmetatable(env) or {}
  -- 呼び出し元(関数)のアドレスも取得しておく
  local f = _G.setfenv(2, env)
  if (meta.__caller == f) then
    -- 既に環境のセットアップ済みなので、ルックアップ設定のみ変更する
    if #{...} == 0 then
      -- もしルックアップ指定が無い(usingの空呼び出し)なら、設定を削除する
      meta.__lookup = {}
      return env
    end
    for i,val in _G.ipairs({...}) do
      find_and_remove(meta.__lookup, val)
      _G.table.insert(meta.__lookup, 1, val)
    end
    return env
  end

  -- 環境未設定なので新しい環境テーブルとメタテーブルを用意する
  local newenv  = {}
  local newmeta = {}
  -- メタテーブルに必要な項目を設定する
  newmeta.__caller = f
  newmeta.__index = lookup
  newmeta.__lookup = reverse({...})
  newmeta.__newindex = substitute
  newmeta.__owner = meta.__owner or env
  newmeta.__parent = env
  -- 新環境とメタテーブルを登録
  _G.setmetatable(newenv, newmeta)
  _G.setfenv(2, newenv)
  -- 新環境を返す
  return newenv
end


-- usingは最初からプレフィックス無しで呼べた方が嬉しい
_G.using = using

使い方はこんな感じで、

require 'util'

t1 = {x = 'x1', y = 'y1'}
t2 = {y = 'Y2', z = 'Z2'}

function test()
  using() -- 安全のためルックアップ設定のクリア
  -- 初期化しておく
  x, y, z = nil
  print(x, y, z) -- nil nil nil

  using(t1)
  print(x, y, z) -- x1 y1 nil

  using(t2) -- using(t1, t2)としても同じ
  print(x, y, z) -- x1 Y2 Z2

  using(t1) -- using(t2, t1)としても同じ
  print(x, y, z) -- x1 y1 Z2

  x = 'XXX'
  print(x, y, z) -- XXX y1 Z2
end

test()
test()
print(x, y, z) -- XXX nil nil

実行結果から分かるのは、



ただし、環境の設定が適用される「スコープ」というのが他の言語とちょっと違うので語弊があるのですが、do 〜 end ブロック内でusingした場合は、(恐らく予想に反して)ブロック外からでもアクセスできちゃいます。ただしファイル単位ではちゃんとスコープとして環境設定できるので、外部コードをrequireとかdofileした時のusingはちゃんと内部で完結されます。


また、上記の例では関数の最初にusing()という空呼び出しをしていますが、

これをしない場合は関数の再呼び出し時にもルックアップの設定が引き継がれます。

多くの場合は必要無いのですが、例えば

function f()
  newt = {x = 'hogehoge', y = 'fugafuga', z = 'piyopiyo'}
  using(newt)
  〜 -- newtへアクセスするコード
end

とした場合、一見普通に動いてようで、関数呼び出しの度に新しいテーブルを作ってルックアップ対象に登録するので、

メモリ使用量が一方的に増加してGCがうまく機能できなくなります。

なので、

  • ルックアップ設定が関数終了後も残るのでは困る場合
  • 新しく生成されたテーブルをルックアップ設定したい場合

は、どこか(普通は冒頭)でusing()すべきで、そうでない場合もやはりusing()した方が安全です。

基本的に何度もusingされる事を前提にコーディングしているので、呼び出しコストはそんなに大きくないと思います。

解説がくどすぎる感じですが、だいたいそんな感じです。

2011-01-20

Luaでusingディレクティブを実現する1

タイトルの通り、近代の高級言語ではだいたい備わっているであろうusingディレクティブを、Luaで実現しようって話ですが、はてな日記エントリーの id:gintenlabo:20100709#1278697770 で既にやってみた方がいらっしゃるのはLuaで実際に開発に携わってる方とかはご存知だと思います。知っての通り、Luaにはディレクティブなんて概念は無く、全てがチャンクと呼ばれるステートメントやブロックの処理単位で動作するので、特殊な機能は関数とテーブルをトリッキーに使って実現して下さいっていう感じですよね。


とにかくLuaは余計な言語仕様を排除して、ミニマリズムな仕様(構文や変数)とシンタックスシュガーを武器に、他のスクリプト言語の追随を許さない軽量さを誇っています。そこに痺れる憧れ…る?


Luaではメタテーブルという仕様で、テーブルやユーザ定義型の振る舞いを制御して、いわゆる演算子オーバーロードプロパティに相当する機能を実現でき、環境テーブルという仕様で帯域変数へのアクセス方法などを制御して、いわゆるレキシカルスコープとか名前空間といった識別子の構造化が実現できます。


ただし、(当然ながら)局所変数の方が環境テーブルよりも名前解決の優先順位が高いので、実際に行われる名前解決は、

現在のスコープの局所変数から探す→より外側のスコープの局所変数から探す→…
(ローカル変数が見つからない場合は→)
環境変数(厳密には関連付けされた環境テーブル、デフォルトでは_G)から探す
(環境テーブルのメタテーブルに、テーブルアクセス用の設定がされている場合は→)
インデックス設定(テーブルないし関数)を通じて変数の値を得る

といった手順で行われると思っていいです。こう書くと変数アクセスの為に物凄いオーバーヘッドがかかってるように見えますが、あくまで構文上の説明であって、実際はもっとうまく名前解決されてるし、ローカル変数はチャンクの分析時点でレジスタスタックに適切にマッピングされるので積極的にローカル変数を使いましょうって話。


usingディレクティブを実現したいってのは要するに、

function f(t)
  table.insert(t, "new value")
  
  -- ...

  table.sort(t)
end

とかいう時に、テーブル名ドットのプレフィックスを省略して、(但し関数スコープ内のみで有効な)

function f(t)
  using(table)
  insert(t, "new value")

  -- ...

  sort(t)
end

なんて出来たら記述量が大幅に減って、文脈によっては見た目も凄くスッキリします。こんな機能が是非欲しい。環境テーブルとメタテーブルをうまく連携させれば実現できそうで、要するに帯域変数参照時の名前解決部分が、

関連付けられた環境テーブルから変数名を探そうとするがメタテーブルで定義されたアクセス関数で制御される
→通常通り環境テーブルから探す→ルックアップに指定されたテーブルから順番に探す

という感じで介入される感じですね。

『通常の環境テーブルから探す』の優先順位も任意にできるんですが、この順番が一番都合が良さそうです。(そうでないとreturn文以外でスコープ外へ変数を渡したい時に面倒になる)

変数代入時は普通に環境テーブルに代入してやれば良さそうです。


gintenlaboさんもこういった方法でルックアップをうまいこと実現されてて関心したんですが、どうにも関数内でこのusing関数を呼んだ時に幾つか問題が起こるようで、一つはスコープ解決が出来てない為に、関数のスコープ外(一番外側のチャンクとか)でも名前干渉が起こってしまう。もう一つは、関数内で動的生成されたテーブルをusingで設定して、この関数が何度も呼ばれると、内部のルックアップリストが増え続けて著しい速度で解放されないメモリ量も増加します。(ガベージコレクタをフル起動させても使用中のメモリは解放しようがない)


別にバグというバグではなく、あくまで使い方の問題なので、スコープに関係無くルックアップしたい場合とか、モジュールテーブルしかルックアップしない場合は全然問題無いです。ただ僕はむしろスコープ内アクセスとか任意のテーブルをルックアップとかいう使い方をしたいので、これを解決する事を念頭に実装してみました。


100行程度のコードなんですが、前置きが無駄に長くなったので後日コード掲載と説明します。

2011-01-13

聖地のロシア餃子

相変わらずプログラミングには関係の無い記事です。

各方面で「びゃー。ロシア餃子うまいー!」っていうコメントを書きこむと、

「何それ、うpしろ」とかいう反応が(主に姉とかから)聞こえてきたので記事にしてみます。


電気コンロが汚いのは仕様です。新しい学生寮に来た時にはこの状態で、洗っても駄目でした。

見所はそこじゃないから。


f:id:Akiva:20101125203727j:image:left

f:id:Akiva:20101125204118j:image


勝手にロシア餃子って呼んでるけど、日本語読めるロシア人(あるいはGoogle翻訳してくるロシア人)が見たら激怒しそうなので、ちゃんと捕捉すると正式名称はペリメニ(пельмени)という、由緒正しきスラブ諸国の伝統料理でございます。ウクライナではヴァレーヌィクィ(Вареники)とも呼ばれるみたいです。左写真は大学の売店で買ってきたウクライナペリメニのチルド食品の調理図です。


「茹でもの」という意味のヴァレーヌィクの通称どおり、本来はスープの具として食するのが一般的ですが、中身が七面鳥の挽肉や玉ねぎなので、ぶっちゃけ蒸し焼きにしちゃえば焼き餃子として食えるんじゃね?と思ったのが事の発端で勝手に焼き餃子風調理にしてみました。もっとカリカリにすれば良かったかも。まぁロシア人もペリメニ焼いて食べたりするらしいので怒られないはず。ダンプリング料理は世界中にあるけど、ロシアのそれが餃子っぽいのは、伊達に中国の隣国やってねぇって話ですかね。


右写真が盛り付け図。醤油や米酢はイスラエルでも普通に買えるので、自家製ラー油を加えれば完璧です。旨かった。皮が分厚くて独特の食感だけど大丈夫だ、問題ない。