/ 最近 .rdf 追記 設定 本棚

脳log[2009-08-06~]



2009年08月06日 (木)

[tDiary] tdiary.rbと plugin/navi_user.rbにパッチあて(plugin/recent_list.rbの分は使用してないのでスルー) + セクションごとの最終更新日時に一票

 > Re: [tDiary-devel] all_filtersとかload_pluginsが呼ばれ過ぎで遅い件

速くなると聞いては捨ておけぬ。

category_anchorでの nil.yearエラーは、起こったのがカテゴリモードだったら既知だけど最新N日表示だから違うし、TDiaryBase@dateは読み出し専用プロパティだから(navi_user.rbのような荒技を使ったりしない)プラグインには変更できないし……。(結局わかりません)

 セクションごとの更新日時

plugin/makerss.rbが直前に変更のあったセクションの他に、過去にちょっとした修正のあったセクションを *.rdfのエントリに加えてしまうのを防げるので賛成。一度考えた回避策はちょっとした修正が *.rdfに反映されてしまうので影響が大きくて断念したし。

 「編集」や「追記」のときに、どうやったら変更のあったセクションを見つけられるだろう。(編集機能は一日単位だから)
  1. *.td2から読み込んだ AnyStyleDiaryと POSTされたソースに基づく AnyStyleDiaryを両方に並べて
  2. each_sectionしながらセクションを全文比較
  3. 一致すれば td2から読んだ古い方の最終更新日時を POSTされた新しい方の AnyStyleDiaryにもセット。
  4. POSTされたソースに基づく方を *.td2に書き込む

という手順を考えた。

セクションインデックスがずれるような変更だった場合は影響範囲が無駄に大きくなるけど、セクションインデックスはセクションIDを兼ねているのでやむなし(URLだって変わっちゃってますから)。

新規作成や修正されたセクションに適用される最終更新日時を、一回の「編集」や「追記」につき一つに限らないと、update_procの中で、変更のあったセクションを見つけるときにアバウトな処理をするはめになりそう。(心配しなくてもそうはならんでしょうが)


2009年08月05日 (水) [Vista] XPと Vistaを LANケーブルでつないで、XPで共有設定しておいた My Documentsフォルダの中身を Vistaの Documentsフォルダにコピーした。「My Picturesは Picturesへコピーされます。」「My Musicは Musicへコピーされます。」「My Videosは Videosへコピーされます。」 わざわざコピー元フォルダの desktop.iniを先読みしてるんかい!(まあ、フォルダアイコンをカスタム表示したりするために読んでるんでしょうね) 害はないので MSが用意したシナリオにただ感心した。


2009年08月03日 (月) [tDiary] だれもがブログやプロフや MySpaceなど自分用の Webスペースを持ってる(らしい)昨今、ツッコミを入れた人を数文字の名前とオプションのメールアドレスでだけしか特定できないのってどうだろうか。OpenID(や類似のサービス)が本人確認の代行だけでなく、自己紹介、アイコン、メアド、ホームページURLなど公開設定された情報を引っぱってくる窓口になってくれると嬉しい。「提供する主なサービスとして、認証(Authentication)、属性交換(Attribute Exchange)などがある。」(Wikipedia:OpenIDから)。属性交換何するものぞ? 「レスポンスは『http』または『https』から始まるOpenID(URI形式の場合)のほか、仕様上はニックネーム、フルネーム、メールアドレスが含まれるが、内容は認証サーバによって異なる。例えばYahoo!では60文字程度の機械的なOpenIDしか返さない」(Wikipedia:OpenIDから)。がっかりだよ Yahoo! となれば、ユーザーの提示する URLが再利用しやすい形式のユーザー情報を指していたらいいんだ。その内容と URLを提示したユーザーとを結びつけるのが OpenID認証サーバー<link rel="openid.server" href="..."/>。コメントトラッキングも支援できたほうが良いが、ユーザー認証の延長でできるのだろうか。(仕組みがまったく、どちら側(あるいは両方)の対応が必要なのかもわかってないけど)。ひょっとしてこういう流れで OAuthが出てきて、コメントを受け付けたサイトが、コメント主の利用するコメント集積所(Twitterに読み替えてもいいよ)へ、コメント主の代わりにコメントを CCするのを可能にしてくれたりするんだろうか。(なんか、いま、ちょっとだけ時代に追いついてこれた気がしてる。一年半前には @ITの記事になってるんだけどね)


2009年07月31日 (金)

クイズ。> 本当はむずかしいハッシュテーブル - sumiiの日記

ネタバレ注意!

member()があっさり 0 を返しすぎるのはすぐわかった。コメント欄の、ハッシュ表があふれることを否定しない表現「『要素の個数はハッシュ表のサイズBより小さいとする』という前提なので、『全部埋まっていたら』の意味にもよりますが」を読んでいて、前提条件に関わらず、同じ要素が複数回 insert()されるとハッシュ表があふれるのもわかった。さて 3つめは? そして修正版は?


(member()相当の機能が欲しいだけなら)ソート済みの int配列にすれば…… < ハッシュテーブルじゃない!

意外にできる子とはいえ insert()と delete()でのメモリブロックの移動コストが気になる。一定サイズの配列(Chunk)をリストで管理したら……とかどうでもいい。


(元記事コメントから)> 削除済みマークを付ける

delete()で EMPTY(0)にせず DELETED(-1とか)にするのね。clear()するまで不良資産(DELETEDな要素)は貯まる一方、というわけでもなく、insert()するときに再利用できそうだ。

これ(削除済みマークを付ける)って FAじゃないのかなあ。

とりあえず、気付いた 2つの問題(delete()がからむと member()が不正確。重複要素であふれる)に対処した insert()はこう。EMPTYな要素より DELETEDな要素を優先して使用したくてフラグっぽい変数が増えてしまった。

/* 要素xをハッシュ表hに追加 */
void insert(int x) {
  int i = hash(x);
  int j = -1; /* i より優先して xを代入すべき位置 */
  while (h[i] != EMPTY) {
    if (h[i] == x) {
      /* return; */
      j = i;
      break;
    } else if (h[i] == DELETED) {
      j = i;
    }
    i = (i + 1) % B;
  }
  h[j != -1 ? j : i] = x;
}

delete()の方も、EMPTYの代わりに DELETEDを代入するように変更する。


重複要素でハッシュ表があふれることがなくなったとはいっても、insert()と delete()を繰り返すうちに DELETEDな要素が増えていって EMPTY要素の数が 0になる可能性はないだろうか。EMPTY要素がなくなると、存在しない要素xを引数にした member()が無限ループに陥る。直観的にそういう可能性はない気がするけど……。


完全ハッシュ関数の状態からスタートしよう。ハッシュテーブルには最低でも一か所の穴(どの要素xも配置されない)がある。この位置を一時的にでもある要素で埋められればハッシュテーブルの全ての要素を DELETEDにできる。直接この穴に配置される要素xは存在しないからハッシュ値を衝突させて insert()で玉突き処理を行わせなければいけない。完全ハッシュ関数を二つの要素のハッシュ値だけが衝突するように変更してみよう。玉突きで穴を一つ塞ぐことができるが、完全ハッシュ関数ではなくなることで穴は二つ(以上)になっていた。以下略。

拍子抜け。要素を重複して格納せず、DELETEDな要素を再利用している限り、ハッシュ表が DELETEDであふれることはない。あっけなさすぎて自作自演臭がしてきた。


蛇足も蛇足の別バージョン。全然かわりばえしない。

/* 要素xをハッシュ表hに追加 */
void insert(int x) {
  int i = hash(x);
  int d = -1; /* hash(x)に一番近い DELETEDの位置 */
  while (h[i] != EMPTY && h[i] != x) {
    if (d == -1 && h[i] == DELETED) {
      d = i;
      h[d] = x;
    }
    i = (i + 1) % B;
  }
  if (d == -1) {
    h[i] = x;
  } else if (h[i] != EMPTY) {
    h[i] = DELETED;
  }
}

0 ≦ hash(x) < B は暗黙の前提にしてるけど、3つ目のミスだったりして。

コメントも読めばわかるけど、ミスが 1。修正過程で陥りやすい落とし穴が 2つくらい、なんだってば。(でもわかりにくい)


2009年07月29日 (水) ドラゴンクエスト。新しく追加されたギコギコもいいかな、と一瞬思うも、やっぱりゲレゲレにした。おおよそ 1.5倍速(90fps)でプレイできている。


2009年07月24日 (金) [790FX-GD70] BIOS v1.4 DL可


2009年07月23日 (木)

最終更新: 2010-01-15T21:38+0900

[Ruby][C++] 釣られますた

C/C++ではもちろん0==false はtrueなわけですが・・・ もしかして、boolean型を整数型とキャスト比較しない仕様なんでしょうか?

この辺はC/C++も難しくて 本質的には 2==trueも本当は真なんですが偽になったりもします。

Rubyでビットフラグをチェックするときは 0との比較を忘れてはいけない。

if(flag & mask)
  # (意図に反して)必ず実行される
end
if(0 != flag & mask)
  # 期待通り
end

というのはさておき、2==true に関して、「本質的には」という言葉が胡散臭い。その「本質」を表すコードは「(2!=false) == (true!=false)」であって、2==true は関係ないのでは? ==は真偽値を返す演算子ではあっても、真偽値のみをオペランドにとる演算子ではないから、2==true も 1==true も -1==true までもが暗黙にそのように解釈されて真になったりしたら、むしろ気持ち悪い。それは PHPの世界だ。(偏見)

Cと逆だな

Rubyでは非0が偽だと言いたげですね。


 @2009-11-06 まさしく偏見でした。

Safe Bool (ja.wikibooks.org)という C++のイディオムがあって、つまり裏を返すと、危険な Boolean変換が C++には存在するということだ。やーめーてー。

C++09(予定)では explicitなコンストラクタみたいに、明示的なキャストを必要とする変換を定義できるようになるらしい。


2009年07月21日 (火) ハルヒのエンドレスエイト。いつまで続くんだろう。F1もテニスも野球もサッカーも似たようなことの繰り返しなんだし、野球を 2時間見るより(<見たことないけど)断然面白く見られるけどね。

[Amazon][tDiary][Ruby] 徒労。劣化コピー。amazon.rb差し替え。SecretKey埋め込みなのでパーミッションは rw-------。

先人 > Amazon Product Advertising APIの認証の件 - zorioの日記

Ruby-1.8.7と Ruby-1.8.6では String#force_encoding("ASCII-8BIT")ができず、String#ordもない(ないのはエンコーディングの概念がないからと、String#[]で代替できるからだと思われる)。それらを使い分けるために 2種類のメソッドを用意するくらいなら、unpackで配列経由でいいです。

require 'digest/sha2'
def hmac_sha256(key, message)
	hash = Digest::SHA256
	hash_block_size = 64 # bytes (= hash.new.block_length)
	key = hash.digest( key ) if hash_block_size < (key.bytesize rescue key.size)
	ikey = Array.new( hash_block_size, 0x36 )
	okey = Array.new( hash_block_size, 0x5c )
	key.unpack("C*").each_with_index{|key_byte, i|
		ikey[i] ^= key_byte
		okey[i] ^= key_byte
	}
	inner_hash = hash.new.update( ikey.pack("C*") )
	outer_hash = hash.new.update( okey.pack("C*") )
	digest = outer_hash.update( inner_hash.update( message ).digest ).digest
	return digest
end

短い秘密鍵は 0を補うって書いてあった。その処理が見あたらないのになぜうまくいくのかと考えたら、0を相手に排他的論理和をとったって何も変わらないのねん。


class Digest::Base

update(str)
self << str
文字列を追加する。self を返す。複数回updateを呼ぶことは文字列を連結してupdateを呼ぶことと等しい。すなわち m.update(a); m.update(b) は m.update(a + b) と、 m << a << b は m << a + b とそれぞれ等価である。

Ruby-1.9で文字列の連結は怖いので m.update(a + b)m << a + bDigest::SHA256.digest(ipad + message) は避けたい。


302 Foundはわかる。リバースプロキシは何するもの?


 追記@2009-08-04: 他の部分も

require 'uri'
require 'base64'
def amazon_authenticated_query_string( host, params )
	re_rfc3986_unreserved = /[^A-Za-z0-9\-_.~]/
	query_string = params.to_a.sort_by{|x| x.first }.map{|key, value|
		URI.encode(key, re_rfc3986_unreserved) +'='+ URI.encode(value, re_rfc3986_unreserved)
	}.join("&")
	string_to_sign = <<-"STRING_TO_SIGN".gsub(/^\t\t/, '').chomp
		GET
		#{host.downcase}
		/onca/xml
		#{query_string}
	STRING_TO_SIGN
	amazon_secret_access_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
	signature = Base64.encode64( hmac_sha256( amazon_secret_access_key, string_to_sign ) ).chomp
	return "#{query_string}&Signature=#{URI.encode(signature, re_rfc3986_unreserved)}"
end

2009年07月19日 (日) はてブの配色を Evangelionに。初号機っぽい。


2009年07月15日 (水)

最終更新: 2010-01-15T21:38+0900

[] 自炊というらしい

ここは自炊技術のためのウィキです。
「自炊」とは2ちゃんねるが由来の隠語で、
「自分で漫画や雑誌、書物などを電子化する」という意味です。
2ちゃんねるの関連スレッドを読みたい方はダウンロード板へどうぞ。
初めての方は、左のメニューバー「事前準備」から下へ読んでください。
一流の職人になれるよう頑張りましょう。

日本全国でどれだけの人手が浪費されているのか。あまりにあほらしいので絶対に自炊はしません。「本」としての価値がある本と、電子データだけを買いたい。


2009年07月08日 (水) [Vista] ショートカットファイルをごみ箱から削除するときに、ターゲットの情報を表示して確認するのってどうよ? (びっくりしてごみ箱の中を確認してしまう)


2009年07月07日 (火) [Firefox] Firefoxを 3.5にアップデートできない。今のやり方はアドオン作者に継続的な負担を強いているよね。動かないアドオンがあればアップデートされないのは Windowsに似てる。標準ユーザーは事前に互換性の確認もできないんだから話になりません。


2009年07月05日 (日) FEDERERでした。それまでブレイクしてたのは RODDICKだったのにファイナルセットでできなかった。

[tDiary] makerss.rb: 更新したつもりのないエントリまでが上がってくる理由。

ある日のエントリの一つを更新すると、同じ日の他のエントリまでが更新されたとして上がってくる理由。たぶんそれまでに他のエントリで「ちょっとした修正」を行っていたのだろう。makerss.rbを見てみたら

add_update_proc do
	makerss_update unless @cgi.params['makerss_update'][0] == 'false'
end

ちょっとした修正では日記の更新時に何も行っていない。これを、index.rdfの更新はしないけど cache/makerss.cacheの更新はする、というふうに変えられないだろうか。


目的は、以前に加えたちょっとした修正と、今回の普通の更新で加えた変更とを区別するため。


編集対象のセクションを特定できるなら、cache/makerss.cacheを常に更新することで、以前のちょっとした修正を検出しなくなるようにする必要はないし、むしろ LDRのようにフィードの内容の一字一字を比較するリーダー?(フィードアグリゲータ?)まで騙すことを考えると、cache/makerss.cacheの内容はちょっとした修正以前の古いままにしておく方がいいみたい。


じゃあ、なぜ既にセクション単位で編集を行ってるこの日記で、直前の編集に関係していないセクションまでが上がってくるのか? makerss.rbの変更まで手を回す前にやる気が尽きたから。

update_procに引数の追加が必要なんだけど、改めて具体的に考えてみると単純にセクションナンバー1つを渡せばよいというものでもなさそうだ。セクション2を編集した結果、セクション2がなくなってセクション3が 2に繰り上がってる可能性がある。セクション2が分裂してセクション2と 3になってる可能性がある。

LDRといい update_procの引数といい、万全を期すと二進も三進もいかんなあ。(LDRは使ってないから外せない問題ではないし、update_procは {before=>2, after=>2..3} を渡すだけで済んでしまう気もするけど)


ああ、afterの範囲を確定するのが難しいんだ。変更前に、編集対象のセクションの前にいくつ、後にいくつのセクションがあるかを数えておく手があるけど、場合によったら直前のセクションと結合してしまうことがあるからなあ(セクション2を編集していたはずが、セクション2を削除してセクション1に追記したことになってる可能性がある)。この場合セクション1に変更があったことは無視してセクション2が削除されたことだけを考えていいなら、やっぱり前後のセクション数を数えておく方法でよさそうだ。


makerss.cacheは二つの目的を持ってるんじゃないか。変更のあったセクションを検出する目的と *.rdfのソースにする目的。

そもそも、*.rdfのソースは *.rdf自身で十分じゃないか?(深くわかってるわけじゃないけど) これに変更のあったセクションを特定できる引数が update_procに加わったら makerss.cacheを用済みにできないか。


できない。一日単位の編集機能をなくせるわけじゃないから今と同等の変更検出機能は必要。そうすると *.rdfのソースを makerss.cacheから *.rdfにスイッチする必要もなさそうに思えるけど、……ないかも。(うっかりフィードに紛れ込んだテストコメントは index.rdfからアイテムを削除するのでなく、コメントを非表示にすれば、あとあと甦ってくることもなかったんだ)


 整理。

makerss.cacheは二つの役割を持っている。

  • 変更のあったセクションを検出する。
  • .rdfのソースにする。

変更のあったセクションの検出能力に難があったので改善を試みる。一日単位の編集機能が存在し続け、rdfのエントリの単位がセクションである限り、変更のあったセクションの検出能力の向上は無駄にならない。

makerss.cacheの日記内容をちょっとした修正でも更新する

これでちょっとした修正のあったセクションの誤検出はなくなる。だがこの makerss.cacheをもとに *.rdfを作成するとエントリの内容がちょっとした修正後のものになり、LDRなど RSSリーダーによる過剰検知の原因となる。

*.rdfのソースを *.rdfにする

右から左に流すだけでは過剰検知の起こりようがない。フィードを更新するときにどんな問題が発生するだろう……。手順を考える。

  1. makerss.cacheとの比較を基に、追加、変更のあったセクションを特定する。
  2. .rdf内のエントリから削除された(現在のセクション数から考えられるより大きいセクションナンバーを持つ)セクションを取り除く。
  3. 追加、変更されたセクションを .rdfのエントリに変換する。
  4. .rdf内に対応するエントリがあれば上書き、なければ追加し、.rdfの先頭に移動する。
  5. エントリ数の上限まで書き込み。

問題は、フィードに含まれる日記やコメントを隠したりセクションを削除したりするとエントリ数が上限より少なくなる、だけだろうか。最初は無理そうに思えたけど、なんだったんだろう。


makerss.cacheから読み込んだものと *.rdfから読み込んだものとは Rubyオブジェクトか文字列かという違いがあるんだなあ。でもそれが意味を持つのは MakeRssNoComments#itemで呼ばれる rdfsec.section.respond_to?( :body_to_html )だけみたいだから、ここを rdfsec.id.index("p") に置き換えれば *.rdfから読み込んだ xml文字列だけで makerss.cacheの代わりができそう。


 整理(Plan B)

セクション単位の編集機能があり、makerss.rbが update_procの引数から変更のあったセクションを知ることができれば、今以上の、変更のあったセクション検出能力は特段必要ではない。

makerss.cacheはこれまで通りちょっとした修正では更新されず、LDRをちょっとした修正以前の古い内容でだまし続けることが可能。

日記の書き手はフィードの更新を伴うデリケートな編集作業ではセクション単位の編集を行うことで、余計なセクションが上ってくることを避けられる。

こっちが断然楽そうだ。でも自分の環境で閉じてしまうから代替案なんだよね。


タイトル(脳log)にふさわしい垂れ流しっぷりじゃあないですか?


 をを、バグってる

はるか上の方でこう書いた。

場合によったら直前のセクションと結合してしまうことがあるからなあ(セクション2を編集していたはずが、セクション2を削除してセクション1に追記したことになってる可能性がある)。

何を見て書いていたかというと……

	unless body1.empty?
		current_section = @sections.pop
		if current_section then
			body1 = "#{current_section.body.sub( /\n+\Z/, '' )}\n\n#{body1}"
		end
		@sections << WikiSection::new( body1, author )
	end

解説解説

appendされた Wikiソースがサブタイトルを持っていないもの( body1 )だったら、
最後のセクションを取り除き

最後のセクションの本文( current_section.body, サブタイトルを含まない )と body1を連結し、

新しいセクション( サブタイトルなし )として追加する。

WikiSection#bodyの代わりに WikiSection#to_srcを使わないとサブタイトルが消えてしまう。

			body1 = "#{current_section.to_src.sub( /\n+\Z/, '' )}\n\n#{body1}"

サブタイトルのあるセクションに、サブタイトルのない本文を追加したときに、サブタイトルのないセクションができあがるのを防ぎます > fix_and_test_append_without_subtitle.diff


 Plan B 仮実行中

セクション単位の編集機能が追加されていることが前提。そのせいでこの日記でしか使えないから気乗りしなかったんだけど、第一案が手詰まりなのと一つの実験場として可能性を示すために。

update_procに updated_sectionというパラメータを追加し、makerss.rbが過去のちょっとした修正を誤検出するのを防ぎます > add_param_updated_section_to_update_proc.diff