発端は、2ch で見かけた、この発言 JavaScript.
まずは、こちらをご覧ください。
(注: 例示の JavaScript は SpiderMonkey や Rhino のインタプリターで実行するか、print を alert に読みかえてブラウザーで実行してください。)
// __GooglePrettify__
function func1(a) {
a = {hoge: 2};
}
var b = {hoge: 1};
func1(b);
print(b.hoge); // => 1
実行すると、1 と表示されましたよね。「参照渡し(call by reference)なら、ここで 2 と表示されなければおかしい。これは、値渡し(call by value)じゃないの?」というのがリンク先で投げかけられていた疑問です。
ここで、次の関数を考えてみます。
// __GooglePrettify__
function func2(a) {
a.hoge = 2;
}
var b = {hoge: 1};
func2(b);
print(b.hoge); // => 2
今度は 2 と表示されたと思います。この挙動を指して、参照渡しだと説明された場合は納得できると思います。
ここで、突然ですが、視点を変えて C++ で同様の関数をつくってみましょう。
// __GooglePrettify__
#include <iostream>
using namespace std;
class Hoge
{
public:
int hoge;
Hoge(int a) : hoge(a) {}
};
void func_v1(Hoge a)
{
a = Hoge(2);
}
void func_v2(Hoge a)
{
a.hoge = 2;
}
void func_r1(Hoge &a)
{
a = Hoge(2);
}
void func_r2(Hoge &a)
{
a.hoge = 2;
}
int main(void)
{
{
Hoge b(1);
func_v1(b);
cout << b.hoge << endl; // => 1
}
{
Hoge b(1);
func_v2(b);
cout << b.hoge << endl; // => 1
// まぁ、値渡しですから。
}
{
Hoge b(1);
func_r1(b);
cout << b.hoge << endl; // => 2
// おっ!?
}
{
Hoge b(1);
func_r2(b);
cout << b.hoge << endl; // => 2
// うんうん。これこそ、参照渡しです。
}
return 0;
}
同じような事をするのに、2 倍のパターンが出てきました。さてさて、JavaScript はどちらのパターンでしょう。 結論から言うと後者の _r 付きのパターンにあたります。これが参照渡し(call by refference)です。 _v 付きは、もちろん値渡し(call by value)です。
ここで、よ〜く見ると、func_r1 と func1 の挙動が異なりますね。 ここで更に C++ について学んで行きましょう。
// __GooglePrettify__
#include <iostream>
using namespace std;
class Hoge
{
public:
int hoge;
Hoge(int a) : hoge(a) {}
};
void func_p1(Hoge *a)
{
a = new Hoge(2);
}
void func_p1_(Hoge *a)
{
*a = Hoge(2);
}
void func_p2(Hoge *a)
{
a->hoge = 2;
}
int main(void)
{
{
Hoge b(1);
func_p1(&b);
cout << b.hoge << endl; // => 1
// あらあら (このままだとメモリーリークします)
}
{
Hoge b(1);
func_p1_(&b);
cout << b.hoge << endl; // => 2
// おぁ!?
}
{
Hoge b(1);
func_p2(&b);
cout << b.hoge << endl; // => 2
// うんうん
}
return 0;
}
またしても分裂してしまいました。ここで、func1 と同じものは func_p1 です。func_r1 と同じものは func_p1_ です。(本当は、参照は書き変え不可な所が違いますが。)
func_p1 と func_p1_ の違いのポイントは、代入時の左辺値が a か *a か、という点です。
これによって、処理の対象が "ポインタ" か "ポインタが指し示している先のオブジェクト" か、という違いが生じます。
C++ では参照を用いることで、"*" を使わずに "参照先のオブジェクト" に対して処理を実行することができます。
ところで、先程の C++ の関数はこうも書けます。
// __GooglePrettify__
#include <iostream>
using namespace std;
class Hoge
{
public:
int hoge;
Hoge(int a) : hoge(a) {}
};
void func_r1_(Hoge &a)
{
a.operator=(Hoge(2));
}
int main(void)
{
{
Hoge b(1);
func_r1_(b);
cout << b.hoge << endl; // => 2
// おおっ!?
}
return 0;
}
この書き方のほうが、参照先のオブジェクトの代入演算子を実行している...、参照先のオブジェクトのメンバ関数 operator= を実行しているということがわかりやすいと思います。
ポインタによる参照渡しの例 func_p1 に戻ると、こっちは参照先自体(ポインタ)を入れ替える処理が実行されます。そしてこれは、JavaScript での参照渡しと同様の動作です。
func_p1 と func_r1 の差はここです。
C++ での参照は、一貫して参照先のオブジェクトに○○を適用という感じです。 JavaScript でも、C++ のそれとだいたい同じなのですが、代入の時だけは参照を貼り替えるという動作になります。
なんとなくイメージはつかんでいただけたかなと思うのですが、いかがでしょう?
最後に、C++ では参照先のオブジェクトに対して代入演算子が呼ばれると言いましたが、この動作と同じようなことを JavaScript でやってみましょう。
// __GooglePrettify__
function assign(that){
if (this === that) return this;
for (var i in that) if (that.hasOwnProperty(i)) {
this[i] = that[i];
}
return this;
}
function func1_(a) {
a.assign = assign; // Assign method を生やして...
a.assign({hoge: 2});
}
var b = {hoge: 1};
func1_(b);
print(b.hoge); // => 2
この様に、ちゃんと値が書き変わりました。
おわり。(続編は有りません。)
参考文献-
Effective C++ 原著第3版
私はこれで C++ を学びました ;) 持ってるのは改訂第2版だけど。 -
新訂版 More Effective C++
私はこれ(ry こっちも、新訂版は持ってない! -
Effective STL—STLを効果的に使いこなす50の鉄則
3 冊セットということで、ついでに。 -
JavaScript 第5版
JavaScript といえば、この本。 -
JavaScript: The Good Parts —「良いパーツ」によるベストプラクティス
JavaScript での Effective C++ 的な本。(だと個人的に思う。)
組み込むのは簡単だった。
steps to phantasien インスパイアで、__GooglePrettify__ を埋め込んだ <pre>
対象にしてみた。こうしておくと、FeedReader で読んでる人への目印(?)になる。
これら CSS と JavaScript をロードしておいて、
<!-- __GooglePrettify__ --> <link rel="stylesheet" type="text/css" href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css" /> <script type="text/JavaScript" src="http://www.google.com/jsapi"></script> <script type="text/JavaScript" src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script>こんなのを実行するようにしておく。
// __GooglePrettify__
(function(){
// jQuery をロードして…
google.load("jquery", "1");
// コールバックは google.setOnLoadCallback で指定
google.setOnLoadCallback(function(){
// __GooglePrettify__ を含む <pre> の class に prettyprint を指定して、__GooglePrettify__ の行を取り除く
$("pre:contains('__GooglePrettify__')").addClass("prettyprint").each(function(i, elm){
elm.innerHTML = elm.innerHTML.replace(/^.*?__GooglePrettify__.*?\n/, "");
$(this).css("background-color", "white");
});
// prettyPrint!!
prettyPrint();
});
})();
/* __GooglePrettify__ */
#include <stdio.h>
int main(void) {
printf("Hello world!\n");
return 0;
}
ZFS で、際限なく延々と snapshot をとるのではなく、古い snapshot は自動で消したいよね、というモチベーション。
そこで、こんな script を書いてみた。(自己の責任にてご利用ください。)
以下のように crontab に書いておくと…
@daily /root/bin/zfs_snapshot.sh tank days_later 7 recursive @weekly /root/bin/zfs_snapshot.sh tank weeks_later 5 recursive @monthly /root/bin/zfs_snapshot.sh tank months_later 12 recursive @yearly /root/bin/zfs_snapshot.sh tank years_later 10 recursive
このような感じで、今日 0days_later だった snapshot が明日には 1days_later ... というふうにローテーションしていきます。
tank/usr 5.23G 137G 2.64G /usr tank/usr@0weeks_later 0 - 2.57G - tank/usr@0months_later 0 - 2.57G - tank/usr@3days_later 29.5K - 2.57G - tank/usr@2days_later 6.22M - 2.59G - tank/usr@1days_later 6.16M - 2.59G - tank/usr@0days_later 6.16M - 2.59G -
既知の問題
- snapshot が規定数に達していない場合は、zfs destroy, zfs rename がエラーになる
- コメントの英語が変
ということで、つけてみた。↓の方参照。
この前は mktemp を書いてましたが、今度は seq を書いてみました。 seq は、shell script でループを書く時によく使ってます。
勉強ということで、Test::Base を使ってみたり、コードは gist に貼ってみたり...。 しかし、これだと feed reader 上で直接コードが読めないところが難点かな。
(ref: Happy Testing Perl:第 2 回 Test::Base の紹介|gihyo.jp ... 技術評論社)
gist: 5103 — GitHub最近、シェルスクリプトをいじってることが多いんだけど、シェルスクリプト中で使って便利なのが mktemp.
それで、乱用ぎみに使っていたのですが、とある環境で作業をしていて、mktemp を使おうと思ったら無かったんで perl で実装してみたり。
utils: perl/mktemp@09177d8ec349にあげておいた。 ライセンスはBoost Software License 1.0 (BSL1.0).
もっときれいに書けないかな〜。
#!/usr/bin/env perl use strict; use warnings; use Fcntl; # use Data::Dumper; my $try_count = 10; my %options = ( t => 'using_tempdir', u => 'unsafe', d => 'dir', ); my %mode = ( using_tempdir => 0, unsafe => 0, dir => 0, ); my $template; foreach my $arg (@ARGV) { if ($arg =~ /^-(.*)$/) { foreach (split //, $1) { die "unknown option: $_" unless exists $options{$_}; $mode{$options{$_}} = 1; } } else { $template ||= $arg; } } unless (defined $template) { $mode{using_tempdir} ||= 1; $template = 'tmp.XXXXXXXXXX'; } # warn Dumper \%mode; # warn $template; my $temp_name; while ($try_count) { $temp_name = $template; $temp_name =~ s/(X)/&rand_char($1)/eg; if ($mode{using_tempdir}) { $temp_name = ($ENV{TMPDIR} || '/tmp') . "/$temp_name"; } # warn $temp_name; if ($mode{dir}) { last if mkdir $temp_name; } else { if (sysopen my $fh, $temp_name, O_WRONLY | O_CREAT | O_EXCL) { close $fh; last; } } $try_count--; } die "make temp failed: $temp_name" if $try_count == 0; if ($mode{unsafe}) { if ($mode{dir}) { rmdir $temp_name or die $!; } else { unlink $temp_name or die $!; } } print "$temp_name\n"; sub rand_char() { my @chars = ('a'..'z', 'A'..'Z', 0..9); return $chars[rand(scalar @chars)]; }
Test Anything Protocol - TAP ですが、なんとなく C++ で実装してみました。
tap.hpp
#ifndef TAP_HPP_526c07e9e0a11dc15e7838c85f7e46780782392d #define TAP_HPP_526c07e9e0a11dc15e7838c85f7e46780782392d #include <string> #include <iostream> namespace tap { size_t tests__ = 0; size_t numof_tested__ = 0; void tests(size_t n) { tests__ = n; std::cout << "1.." << n << std::endl; } void ok_notok__(bool is_ok, const std::string& name) { std::cout << (is_ok ? "ok " : "not ok ") << ++numof_tested__ << " - " << name << std::endl; } template <typename T, typename U> void is__(const std::string& file, size_t line, T got, U expected, const std::string& name = "") { if (got != expected) { ok_notok__(false, name); std::cerr << "# Failed test '" << name << "'\n" << "# at " << file << " line " << line << ".\n" << "# got: '" << got << "'\n" << "# expected: '" << expected << "'" << std::endl; } else { ok_notok__(true, name); } } template <typename T, typename U> void isnt__(const std::string& file, size_t line, T got, U expected, const std::string& name = "") { if (got == expected) { ok_notok__(false, name); std::cerr << "# Failed test '" << name << "'\n" << "# at " << file << " line " << line << ".\n" << "# '" << got << "'\n" << "# ne\n" << "# '" << expected << "'" << std::endl; } else { ok_notok__(true, name); } } void fail__(const std::string& file, size_t line, const std::string& name) { ok_notok__(false, name); std::cerr << "# Failed test '" << name << "'\n" << "# at " << file << " line " << line << std::endl; } void diag__(const std::string& d) { std::cerr << "# " << d << std::endl; } } #define is(g, e, n) tap::is__(__FILE__, __LINE__, (g), (e), (n)) #define isnt(g, e, n) tap::isnt__(__FILE__, __LINE__, (g), (e), (n)) #define fail(n) tap::fail__(__FILE__, __LINE__, (n)) #define diag(d) tap::diag__((d)) #endif
こんな風に使います。
#include "tap.hpp" #include <iostream> #include <string> using namespace std; int main(void) { tap::tests(6); is(1, 3, "hoge"); string hoge = "hoge"; string geho = "hoge"; isnt(hoge, geho, "hoge"); isnt(0, 0, "hoge"); fail("asdf"); is(0, 0, "asf"); diag("asdf"); return 0; }実行すると...。
% g++ -Wall -Wextra test.cpp && ./a.out 1..6 not ok 1 - hoge # Failed test 'hoge' # at test.cpp line 10. # got: '1' # expected: '3' not ok 2 - hoge # Failed test 'hoge' # at test.cpp line 13. # 'hoge' # ne # 'hoge' not ok 3 - hoge # Failed test 'hoge' # at test.cpp line 14. # '0' # ne # '0' not ok 4 - asdf # Failed test 'asdf' # at test.cpp line 15 ok 5 - asf # asdf
a.t を用意して...。
#!/bin/sh
./a.out
% prove -v ./a.t ./a...... 1..6 not ok 1 - hoge not ok 2 - hoge not ok 3 - hoge not ok 4 - asdf ok 5 - asf # Failed test 'hoge' # at test.cpp line 10. # got: '1' # expected: '3' # Failed test 'hoge' # at test.cpp line 13. # 'hoge' # ne # 'hoge' # Failed test 'hoge' # at test.cpp line 14. # '0' # ne # '0' # Failed test 'asdf' # at test.cpp line 15 # asdf Failed 5/6 subtests Test Summary Report ------------------- ./a.t (Wstat: 0 Tests: 5 Failed: 4) Failed tests: 1-4 Parse errors: Bad plan. You planned 6 tests but ran 5. Files=1, Tests=5, 0 wallclock secs ( 0.03 usr + 0.01 sys = 0.04 CPU) Result: FAIL [1] 3716 exit 1 prove -v ./a.t
こうなる。
freeHG にあげときました。BSL1.0 です。utils: Manifest
テストに失敗したときの戻り値を...には、まだ対応していません。
EM7 用 /etc/X11/xorg.conf 設定メモ
KENSINGTON 64325 EXPERT MOUSE OPTICAL
Section "InputDevice" Identifier "em7" Driver "mouse" Option "CorePointer" Option "Protocol" "auto" Option "Device" "/dev/input/mice" Option "Buttons" "9" Option "Emulate3Buttons" "true" Option "EmulateWheelButton" "8" Option "EmulateWheel" "true" Option "YAxisMapping" "4 5" Option "XAxisMapping" "6 7" # Option "DragLockButtons" "8" EndSection
これで、右奥のボタンを押しながらボールを転がすことでスクロール可能に。
加えて Firefox の設定を以下にする(about:config で)と、横スクロールもできるようになる。Firerox 3 だと標準でこの設定になってるっぽい。
- mousewheel.horizscroll.withnokey.action = 0
- mousewheel.horizscroll.withnokey.numlines = 1
- mousewheel.horizscroll.withnokey.sysnumlines = true

OpenPGP key (0xF0F5FDCE) fingerprint: 

