帰ってきた人生戦闘詳報


madtape.com

2012-01-23

[] 「成立」の解釈 09:09

昨晩twitterであるツイートをしたところ、えらい勢いでRTされてしまい、Mentionでちょくちょく疑問や質問をいただいたので、自分の力のおよぶ限りの解説をさせていただいているのですが、毎回140文字以内に収めるのが大変なのと、あまりに多くの方に興味を持っていただいたため、対応が大変になってきましたので、こちらに簡単な解説でも書いておくことにしました。


とはいっても、自分は日本史研究者とか専門家ではないので、あまり詳しいところまでは説明できません。また、個人的な解釈が混ざりこんでいる可能性があることもあらかじめおことわりしておきます。


して、件のツイートはこちら。

Twitter

【若い方へ】 わりと歳のいった人が稀に「いい国作ろう〜」みたいなネタを呟きますが、一定の年代まで学校では鎌倉幕府の成立年を1192年とし「いい国作ろう鎌倉幕府」という語呂あわせとともに教えていました。現代の解釈では皆さんの習った1185年で合ってますので心配御無用です。


私を含む一定の世代まで、社会科での「鎌倉幕府成立」の年号は、「いい国(1192)作ろう鎌倉幕府」の語呂合わせと共に、1192年と教わってきました。これは源頼朝朝廷から征夷大将軍に任ぜられた年であり、この時点を鎌倉幕府の成立と見なす観点からの解釈によるものです。


しかし現在では、それ以前と以後で大きく社会の有り様を変えたのは頼朝壇ノ浦平氏を滅ぼし、守護や地頭を各地に置き実効的な支配体制を築き上げた1185年である、という解釈が主流であるようです。1192年の征夷大将軍任命はその実効支配に対する事後追認的なものである、といったところでしょうか。

【参考】鎌倉幕府 - Wikipedia


これを踏まえ、現在の中学校歴史教科書の記述も従来とは変更されています。

2008年の段階で既に教科書にはその記述があったようです。

【参考】asahi.com:中学校の歴史 1192は違うの?鎌倉幕府成立 - 教科SHOW - 小中学校 - 教育

この教科書の変更があって以降の教育を受けた世代は、鎌倉幕府の「成立」を1185年と習うことになります。特に何か新事実が出てきたりしたわけではありませんが、

何をもって「幕府の成立」とするか、という解釈が変わった

ということです。我々の世代が受けた教育が大きく内容的に誤っていた、というわけではないのでご安心ください。ただ、征夷大将軍任命という「形式」よりも「実効支配の開始」を重視するようになった、ということらしいです。


もっとも、教科書の改訂があって以降も、教える先生によっては1192年の征夷大将軍任命のほうを重視したり、あるいは先生の側の認識をアップデートできていなかったりで、1192年と習った若い方もいるようです。何をもって「成立」と見なすかが違うだけで、1185年としても1192年としても必ずしも誤りではありません。


重要なのは、歴史における出来事の経緯を理解すること、だと思っています。

トラックバック - http://d.hatena.ne.jp/big-bros/20120123

2011-12-20

[] #プログラム大喜利 文字列カエサル暗号化する関数またはプログラム 08:46

Twitter

Twitter

朝食を終えてから書いたので「朝飯前」とは言えないが、内容はそのレベル。

妙に真面目に書いてしまって、前提に無い条件にも対応はしている。


/*
caesar.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void caesar(char *buf, char * str)
{
	int i;
	int len;
	
	for(i = 0; str[i]; i++) {
		// 設問の前提から外れるけど、チェックを入れておく。
		if(!isalpha(str[i])) {
			buf[i] = str[i];
			continue;
		}
		buf[i] = (tolower(str[i]) > 'w') ? str[i] - 23 : str[i] + 3;
	}
	buf[i] = '\0';
}

int main(int argc, char **argv)
{
	int i;
	char * buf;
	
	for(i = 1; i < argc; i++) {
		buf = malloc(strlen(argv[i]) + 1);
		if(NULL == buf) return EXIT_FAILURE;
		caesar(buf, argv[i]);
		printf("%s ", buf);
		free(buf);
	}
	printf("\n");		// 勿体ないから putchar() とかでもいい。
	return EXIT_SUCCESS;
}

[] #プログラム大喜利 内容不定Job実行系 00:05

Twitter

実はオブジェクト指向問題だったりする。

JavaC++, Ruby などのオブジェクト指向言語であれば非常にすっきりと実装できたりする。

実はCでもできん事は無いのだけれども、スコープをファイル単位で分けねばならなかったりで結構しちめんどくさいw

下記回答は当初C++のくせにまとめて書いていたが、やっぱり読みにくいというか、ヘッダはヘッダとしてあったほうがクラスの全容が把握しやすいと思われるため、分けることにした。

内容は分割前と同じ。

CJobMgr.h

#ifndef CJobMgr_h
#define CJobMgr_h

#include <iostream>
#include <list>

class CJobMgr;

//! Job基底クラス
/*!
各Jobは、このクラスを派生させて実装する。
*/
class CJob
{
  protected:
	CJob();

  public:
	virtual ~CJob();

	virtual bool watch() = 0;	// 監視メソッド: 監視条件の判定を記述する
	virtual bool exec() = 0;	// 実行メソッド: 実行する処理を記述する
	virtual void destroy() = 0;	// 破棄メソッド: 破棄処理を記述する
	
  protected:
	//! Job登録メソッド
	bool regist();
	
	//! 破棄予約メソッド
	bool remove();
};

class CJobMgr
{
	friend class CJob;
  private:
	CJobMgr();
	virtual ~CJobMgr();
  public:
	//! インスタンス取得
	static CJobMgr& getInstance();
	
	//! 登録 Job 実行
	void exec_job();
	
  private:
	//! 指定Jobの登録
	bool regist(CJob * pJob);
	
	//! 指定Jobの削除予約
	bool remove(CJob * pJob);
private:
	std::list<CJob *>		m_lstJob;		//!< Jobリスト
	std::list<CJob *>		m_lstRemove;	//!< 削除予約リスト
};

#endif // CJobMgr_h

CJobMgr.cpp


#include "CJobMgr.h"


CJob::CJob() {}
CJob::~CJob() {}

/*!
\return Job登録が正常終了すればtrue, 異常終了すれば false

Jobの初期化条件がJobごとに異なることが考えられるので、初期化メソッドは実装しないが、
初期化の最後にこのメソッドを呼び、自身をマネージャに登録するという仕様。
*/
bool
CJob::regist()
{
	return CJobMgr::getInstance().regist(this);
}
	
/*!
\return 破棄予約に成功すれば true, 失敗すれば false

Jobを破棄予約する。破棄予約されたJobはメインループの最後にまとめて破棄される。
各Jobインスタンスに対し destroy() を呼んだ後、delete される。
*/
bool
CJob::remove()
{
	return CJobMgr::getInstance().remove(this);
}




CJobMgr::CJobMgr() {}
CJobMgr::~CJobMgr() {}

/*!
\return インスタンスへの参照

ジョブマネージャは複数存在してはいけないので、singleton として実装する。
*/
CJobMgr&
CJobMgr::getInstance()
{
	static CJobMgr instance;
	return instance;
}

/*!
メインループの中で呼び出すべきメソッド。
	for(;;) {
	    CJobMgr::getInstance().exec_job();
	}
のように呼べば良い。

登録されている全てのJobに対し watch() を呼び、
true を返したものについては exec() を呼び出す。
その過程で自身を削除予約した者については、最後にまとめて破棄処理を行う。
*/
void
CJobMgr::exec_job()
{
	std::list<CJob *>::iterator it;

	// 削除予約リストをクリアする。
	m_lstRemove.clear();

	// 登録されているJobを処理する
	for(it = m_lstJob.begin(); it != m_lstJob.end(); it++) {
		CJob * pJob = *it;
		if(pJob->watch()) pJob->exec();
	}
		
	// 削除予約されたものを削除する
	for(it = m_lstRemove.begin(); it != m_lstRemove.end(); it++) {
		CJob * pJob = *it;
		pJob->destroy();
		m_lstJob.remove(pJob);
		delete pJob;	// インスタンスそのものを破棄
	}
}

/*!
\param pJob  登録対象となるJobインスタンスのポインタ
\return 登録が正常終了すればtrue, 異常終了の場合はfalse

指定されたポインタのJobをマネージャに登録する。CJob::regist()内部から呼ばれる。
privateメソッドだが、friend class である CJob からは呼べるため、
Jobの実装者はマネージャの存在を意識する必要がなくなる。

このようにする理由は、singleton の public メソッドはプログラム中の
何処からでも呼べてしまうため。
不正な呼び出しを防ぐ目的で「CJob以外から呼ぶことができない」状態を作り出している。
*/
bool
CJobMgr::regist(CJob * pJob)
{
	try {
		m_lstJob.push_back(pJob);
		return true;
	} catch(std::bad_alloc& ex) {
		return false;
	}
}

/*!
\param pJob  削除予約対象となるJobインスタンスのポインタ
\return 予約が正常終了すればtrue, 異常終了の場合はfalse

指定されたポインタのJobを、マネージャに対し削除予約する。CJob::remove()内部から呼ばれる。
スコープに関しては regist(CJob*) と同様。
*/
bool
CJobMgr::remove(CJob * pJob)
{
	try {
		m_lstRemove.push_back(pJob);
		return true;
	} catch(std::bad_alloc& ex) {
		return false;
	}
}

[] #プログラム大喜利 内容不定Job実行系(わざわざCで書いてみる編) 01:53

Twitter

上の回答例で「実はCでも出来んことはない」と書いた以上Cでも書いてみる。

jobmgr.h


#ifndef jobmgr_h
#define jobmgr_h

typedef struct JOBHDR {
	struct JOBHDR * prev;
	struct JOBHDR * next;
	struct JOBHDR * rmv_next;
	int	(*func_watch)(void *);
	int (*func_exec)(void *);
	void (*func_destroy)(void *);
} JOBHDR;


void job_init();

void * job_regist(size_t szWork,
			int (*func_watch)(void *),
			int (*func_exec)(void *),
			void (*func_destroy)(void *));

int job_remove(void * pWork);

void job_exec(void);

#endif /* jobmgr_h */

jobmgr.c


#include <stdio.h>
#include <stdlib.h>

#include "jobmgr.h"

typedef struct {
	JOBHDR * begin;
	JOBHDR * end;
	JOBHDR * remove;
} JOBLIST;

static JOBLIST jobList;

void * job_regist(size_t szWork,
			int (*func_watch)(void *),
			int (*func_exec)(void *),
			void (*func_destroy)(void *))
{
	JOBHDR * pWork;
	
	if(szWork < sizeof(JOBHDR)) return NULL;
	if(NULL == (pWork = malloc(szWork))) return NULL;
	
	pWork->func_watch = func_watch;
	pWork->func_exec = func_exec;
	pWork->func_destroy = func_destroy;
	
	pWork->next = NULL;
	if(NULL != (pWork->prev = jobList.end)) {
		jobList.end->next = pWork;
	} else {
		jobList.begin = pWork;
	}
	jobList.end = pWork;
	pWork->rmv_next = NULL;

	return pWork;
}

int job_remove(void * pWork)
{
	JOBHDR * jWork = (JOBHDR *)pWork;
	
	jWork->rmv_next = jobList.remove;
	jobList.remove = jWork;

	return 1;
}

void job_exec(void)
{
	JOBHDR * pJob;
	JOBHDR * pNxt;

	jobList.remove = NULL;
	for(pJob = jobList.begin; pJob; pJob = pJob->next) {
		if(pJob->func_watch(pJob)) pJob->func_exec(pJob);
	}
	/* 破棄予約分の破棄 */
	pJob = jobList.remove;
	while(pJob) {
		pNxt = pJob->rmv_next;
		if(pJob->prev) {
			pJob->prev->next = pJob->next;
		} else {
			jobList.begin = pJob->next;
		}
		if(pJob->next) {
			pJob->next->prev = pJob->prev;
		} else {
			jobList.end = pJob->prev;
		}
		pJob->func_destroy(pJob);
		free(pJob);
		pJob = pNxt;
	}
}

void job_init(void)
{
	jobList.begin = jobList.end = jobList.remove = NULL;
}

count_job.c (上記を使ったJobの作例)


#include <stdio.h>
#include <stdlib.h>

#include "jobmgr.h"

typedef struct {
	JOBHDR		header;
	
	int			count;
} WORK;

static int my_watch(void * vpWork) {
	WORK * pWork = vpWork;

	pWork->count++;
	if(pWork->count > 100) return 1;
	return 0;
}

static int my_exec(void * vpWork)
{
	WORK * pWork = vpWork;
	pWork->count = 0;
	printf("count 100\n");
	return 1;
}

static void my_destroy(void * vpWork)
{
	WORK * pWork = vpWork;
	/* 特にやることないな */
}

void count_init(void)
{
	WORK * pWork;
	
	pWork = job_regist(sizeof(WORK), my_watch, my_exec, my_destroy);
	pWork->count = 0;
}
トラックバック - http://d.hatena.ne.jp/big-bros/20111220

2011-12-18

[] #プログラム大喜利 出題その(2) 11:39

Twitter

正攻法で書くとこんな感じか。

/*
	my_cbrt.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define CONV_REPEAT  30

double my_cbrt(int N)
{
	double x = N;
	int i;

	for(i = 0; i < CONV_REPEAT; i++) {
		x = sqrt(sqrt((double)N * x));
	}
	
	return x;
}

int main(int argc, char **argv)
{
	int n = atoi(argv[1]);	/* コマンドライン引数から任意の数を得る流れは手抜き */

	double x = my_cbrt(n);
	printf("cbrt(%d) = %f (^3 = %f)\n", n, x, x * x * x);

	return EXIT_SUCCESS;
}

[] #プログラム大喜利 出題その(3) 13:14

Twitter

やることは中学校で習うレベルなので、わざわざググる程のものでもなく出来る筈。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define PARAM_R  200.0f
#define PARAM_THETA  (M_PI / 12.0)

typedef struct {
	double x, y;
} VEC;

/*
 * 角度の単位は [rad] で与える。
 */
void polar2rect(double r, double theta, VEC * retVector)
{
	retVector->x = r * cos(theta);
	retVector->y = r * sin(theta);
}

int main(int argc, char **argv)
{
	VEC result;
	
	polar2rect(PARAM_R, PARAM_THETA, &result);
	printf("(%f, %f)\n", result.x, result.y);
	return EXIT_SUCCESS;
}

とっても簡単。初級問題。

[][] #プログラム大喜利 出題その(4) 14:21

Twitter

これでむしろ必要なのは、プログラムを組む前に「言葉の意味を理解する能力」と「言葉が指すものについての知識」、その上で「式を組み立てる能力」だな。要求される仕様によっては、何をおいても基礎学力が必要という例でもある。

/*
gso.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define G 6.673e-11

double GSOheight(double M, double r, double p)
{
	return cbrt((M * G * p * p) / (4.0 * M_PI * M_PI)) - r;
}

int main(int argc, char **argv)
{
	double h = GSOheight(5.974e24, 6378.137e3, 24 * 60 * 60 * 364.25 / 365.25);
	
	printf("GSOheight: %f[km]\n", h / 1000.0);
}

これは解説が要るかもしれんので書いてみる。

  • 求めるものは「静止軌道高度」。
  • 静止軌道」は「中心天体の自転と同じ周期を持ち、天体表面から見て一点に留まっているようにみえる衛星の軌道」のこと。
  • 静止軌道は必ず「赤道上空の円軌道」になる。
  • 「赤道上空の円軌道」の「地上高度」は、円軌道の「半径」から「天体の赤道半径」を引いたものになる。
  • 円軌道においては、中心天体の引力と、公転による遠心力が釣り合っている。
  • 天体の引力:F_g = ¥frac{GMm}{r^2} (G は「万有引力定数」)
  • 遠心力:F_p = ¥frac{mv^2}{r}
  • つまり、¥frac{GMm}{r^2} = ¥frac{mv^2}{r} が成り立ち、r=¥frac{GM}{v^2}
  • 円軌道の「長さ」は円周の値なので  l=2¥pi r
  • この軌道を速度 v で一周する時間が周期 p なので、 p = ¥frac{2¥pi r}{v}
  • つまり、連立方程式

¥begin{eqnarray}¥¥ ¥begin{cases}¥¥ r=¥frac{GM}{v^2} & ¥¥ p=¥frac{2 ¥pi r}{v} &¥¥ ¥end{cases} ¥¥ ¥end{eqnarray}

を、rについて解けば、中心天体の質量と周期から、静止軌道の半径を求める式が得られる。何を求めるかさえ分かれば、式自体は中学校の算数レベル。中学校で教えないものとしては立方根が出てくることぐらいか。

  • 求めた静止軌道半径と、天体の赤道半径をどうするかは先に述べた通り。

物理定数 G の値ぐらいは Wikipedia にも載ってるし Google でもすぐ調べがつくので、その程度のことは即座に調べられるのも現代のプログラマに要求される能力の一つ。

[] #プログラム大喜利 出題その(5) 23:25

Twitter

誤答のため削除。

[] #プログラム大喜利 出題その(5) 再回答 00:44

上記と同じ問題を再回答。

#!/usr/bin/perl

my %hash = ( 'B' => 1, 'K' => 2**10, 'M' => 2**20, 'G' => 2**30, 'T' => 2**40 );

while(<STDIN>) {
	/^\s*([0-9\.]*)([KMGTB])/; $sz = $1 * $hash{$2};
	eval("s/^\\s*[0-9\.]*[KMGTB]/".$sz."/");
	print $_;
}
1;

上記が filter.pl ならば、

$ du -h * | ./filter.pl | sort -n -k 1,1

でいけるはず。

朝日薫朝日薫 2011/12/18 12:44 例えば8を与えた場合でも30回ループしちゃうのは哀しいかも…

big-brosbig-bros 2011/12/18 12:48 収束判定で手を抜いて「たぶん大丈夫と思われる回数」にしてありますw

ちゃがまちゃがま 2011/12/18 13:52 やべぇ。ポケコンのBASICだと、組み込み関数一発で・・・。

トラックバック - http://d.hatena.ne.jp/big-bros/20111218

2011-12-17

[][] プログラム大喜利 08:37

Twitter

こんなツイートをした以上は自分が書かないと怒られそうなので書いてみる。

#include <stdio.h>
#include <stdlib.h>

void select_char(char * buf, int pos)
{
	int i, c;
	
	if(pos == 9) {
		buf[pos] = '\0';
		printf("%s\n", buf);
		return;
	}

	// 2文字目までは連続のチェック不要
	if(pos < 2) {
		for(i = 'A'; i <= 'Z'; i++) {
			buf[pos] = i;
			select_char(buf, pos + 1);
		}
		return;
	}
	
	// 直前二つが同じ文字の場合は、踏んではいけない文字を決め、
	// その直前までとその直後からについて処理する。ループの中でif文を使いたくないので。
	c = 'A';
	if(buf[pos - 1] == buf[pos - 2]) {
		c = buf[pos - 1];
		for(i = 'A'; i < c; i++) {
			buf[pos] = i;
			select_char(buf, pos + 1);
		}
		c++;
	}
	for(i = c ; i <= 'Z'; i++) {
		buf[pos] = i;
		select_char(buf, pos + 1);
	}
}

int main(int argc, char **argv)
{
	char buf[10];
	
	select_char(buf, 0);
	return EXIT_SUCCESS;
}

[][] #プログラム大喜利 (その2) 22:21

考えてみリゃループの中で if 文使ってなくても、跳んだ先の関数内でif分岐してりゃ分岐の処理数は同じなんだよな。それだったら最初からループの中でやったほうが速い。

#include <stdio.h>
#include <stdlib.h>

void select_char(char * buf, int pos)
{
	int i;
	
	if(pos == 9) {
		buf[pos] = '\0';
		printf("%s\n", buf);
		return;
	}

	for(i = 'A'; i < 'Z'; i++) {
		if(pos >= 2 && buf[pos - 1] == buf[pos - 2] && buf[pos - 1] == i) continue;
		buf[pos] = i;
		select_char(buf, pos + 1);
	}
}

int main(int argc, char **argv)
{
	char buf[10];
	
	select_char(buf, 0);
	return EXIT_SUCCESS;
}

ちゃがまちゃがま 2011/12/17 10:19 やってみました。
最近 C書いてなかったから手間取った。
あー、グローバルなんて使ってしもーた・・・。

#include <stdio.h>

#define LENGTH 9

char string[LENGTH];

main()
{
fixchar(0);
}

fixchar(int pos)
{
char ch;

if (pos >= LENGTH) {
printf("%s\n", string);
return;
}
for (ch = 'a'; ch <= 'z'; ch++) {
// printf("pos = %d, ch = \'%c\'\n", pos, ch);
if (pos > 1 && ch == string[pos - 1] && ch == string[pos - 2])
continue;

string[pos] = ch;
fixchar(pos + 1);
}
}

ちゃがまちゃがま 2011/12/17 10:20 あ、やっぱりインデント潰れちゃうのね・・・。

big-brosbig-bros 2011/12/17 22:14 別にCでなくてもいいんですけどね。寧ろ言語の選択も含め、他にどのような手があるかを見てみたいというか。

big-brosbig-bros 2011/12/18 01:16 そして結果的に同じようなコードに落ち着くという。

ちゃがまちゃがま 2011/12/18 02:14 まぁ、このお題だと、私は Cかな。
朝日薫さんは Perl を選ぶと思う。

あ、ホンマや。そっくりになった。

ちゃがまちゃがま 2011/12/18 11:21 やっぱり perl で参戦のようです。
http://t.co/JBsh89HA

朝日薫朝日薫 2011/12/18 11:57 ちゃがま氏ならshellで行くと思ったがw
bashなら配列変数とprintfコマンドで泥臭く書けるはず

トラックバック - http://d.hatena.ne.jp/big-bros/20111217

2011-12-16

[][] 90億の神の御名 新訳 13:22

2007-02-23 - 帰ってきた人生戦闘詳報

2007年ごろにこんなお題でプログラムチャレンジを行ったのだけど、2009年に出たクラークのベスト版では本の表題にもなっている。

あれから4年以上経つが、今の俺ならどんなコードを書くだろうか。

トラックバック - http://d.hatena.ne.jp/big-bros/20111216