ラベル なんとなくPシリーズ の投稿を表示しています。 すべての投稿を表示
ラベル なんとなくPシリーズ の投稿を表示しています。 すべての投稿を表示

2008年12月4日木曜日

MS-DOSバッチファイルでFizzBuzz

昨日の記事:VBScript で FizzBuzz の続編。
FizzBuzz、今度はDOSバッチファイルで作ってみた。
単純にやってみたかっただけなので、コードを短くする工夫とかはまあ置いておこう。
 
 

FizzBuzzをDOSバッチでやる際に、とりあえず剰余(mod)が取れるのかよく分からなかったので3の倍数、5の倍数を判断するカウンタをつくることにして書いてみた。
記事書く前に、本で算術演算子を調べたら、mod 取れるみたい…
100を3で割った余りなら、
> set /a hoge=100%3
でOKだった。まぁコレはコレでありかな。。

 

ちょっと引っかかったのが遅延展開、forの中で普通に変数 %hoge% って使うと、forの中でいくら中身をいじろうと、for に入る前の変数の中身が展開されちゃう。
setlocalで 遅延展開有効 にして、!hoge! って使えばfor内でもその時点の変数内容が展開される。
あとは特に工夫も無く。
[sourcecode language='sh']
@echo off

REM // 遅延展開有効
setlocal enabledelayedexpansion

REM // ウインドウタイトル変更
TITLE FizzBuzz

REM // カウントダウン用の変数
set SAN=3
set GO=5

REM // 繰り返し
for /l %%I in (1,1,100) do (

rem // 表示用の変数、空っぽ比較用も
set STRING=
set STRING_FLG=

rem // 3と5のカウンターが0ならリセット
if !SAN!==0 (set SAN=3)
if !GO!==0 (set GO=5)

rem // 3と5のカウンターを一つ減らす
set /a SAN=!SAN!-1
set /a GO=!GO!-1

rem // 3のカウンターが0なら表示用にFizz追加
if !SAN!==0 (set STRING=Fizz)

rem // 5のカウンターが0なら表示用にBuzz追加
if !GO!==0 (set STRING=!string!Buzz)

rem // ここでFizzもBuzzも入ってなければループ数字追加
if !STRING!==!STRING_FLG! (set STRING=!STRING!%%I)

rem // 現在の数と、FizzBuzzの結果を出力
echo %%I !STRING!
)
pause
endlocal
[/sourcecode]


実行結果。
[caption id="attachment_1099" align="alignnone" width="428" caption="画像:FizzBuzz"]画像:FizzBuzz[/caption]
 

デキター。
 

剰余の計算処理もちゃんと調べてればもう少しスマートだったが、まあ結果オーライという事にしよう。
set のヘルプに一応ヒントらしきものはあったが見逃した。
setの詳しい解説があった。

2008年11月14日金曜日

EXCELのマクロ(VBA)を触ってみた、ランダム陣取りシミュレータ?

ちょっと待ち行列を計算する用事があったので、今まで操作録以外全然触らなかったExcelのマクロの書き方を勉強してみた。
 
 

しかし、なぜか予定と違って観察系の陣取りシミュレータができた。。
    仕様はこんな感じ
  • セルに1-100までの数字を並べる

  • ランダムに1つチョイス、これまたランダムな場所に上書きコピーする

  • 繰り返す。そのうち1つの数字が天下を統一する。


 

待ち行列の計算しようとおもったんですよ、本当ですよ?
 

ということで、数字のセットとランダムコピーのマクロ(VBA)を載せておこう。
 
 


準備用マクロ(VBA):1-100までの数字をセット、ついでにカウンタを0にリセットする


1行に5列ずつで、20行目まで数字を置いていく。

[sourcecode language='vb']Sub reset()
s = 1
For j = 1 To 20
For i = 1 To 5
' セルに数字を書き込む
Cells(j, i) = s
s = s + 1
Next i
Next j

' カウンタリセット、F5がカウンタ
Range("F5") = 0
End Sub[/sourcecode]
 

これで準備はOK、リトライも簡単。
 
 

シミュレータ実行用マクロ(VBA):ランダムに数字を選んで1回コピーの繰り返し


ところどころ乱暴だけど、ランダムコピーの処理。
rnd()の周りはなんかきれいなやり方があるんだろうなぁ。

[sourcecode language='vb']Sub Shuffle()
Randomize
s = Range("F5")

For i = 1 To 500
rndset:
m = Round((Rnd * 40 / 2 + 1), 0)
j = Round((Rnd * 10 / 2 + 1), 0)
k = Round((Rnd * 10 / 2 + 1), 0)
l = Round((Rnd * 40 / 2 + 1), 0)
' 6とか21とか、はみ出し系の結果が出るのでやり直しさせる
If j = 6 Or k = 6 Or m = 21 Or l = 21 Then
GoTo rndset
Else
' 参考用に座標を出力しておく
Range("F1") = j
Range("F2") = k
Range("F3") = m
Range("F4") = l
' これはカウンタ
Range("F5") = s + i
' ランダムに選んだマスの数字を任意の場所に上書きコピーする
Cells(m, j).Copy Destination:=Cells(l, k)
End If
Next i

End Sub[/sourcecode]
 
 


実行してみる、グラフつけると面白い


視覚的なことを考慮してグラフとか色々つけている、リアルタイムで勢力図が見れて面白い。
これもマクロでセットアップしたら良いんだろうけどそれはまた今度かな。
 

統一させるまで見届ける場合は While でまわせばOK、大体 4000-8000 位の施行で終わるがたまに1万超えるし。
 

スタート時点の図、すべての勢力は1つで均等
[caption id="attachment_1051" align="alignnone" width="300" caption="画像:陣取り合戦スタート"]画像:陣取り合戦スタート[/caption]
 
 

ランダムコピー500回実行したところ。93が最大勢力(26ヶ)
[caption id="attachment_1052" align="alignnone" width="300" caption="画像:500ターン"]画像:500ターン[/caption]
左下のところはグラフ用、COUNTIF文 と IF文 で0個になったら勝手に消えるようにしてグラフを見やすいようにしている。
 
 

統一までほったらかしてみる、2300くらい。
とても三国時代だ、NINTENDO64(仮名) vs PC-98(仮名) vs .. JR-100(仮名)? といった所か。
[caption id="attachment_1053" align="alignnone" width="300" caption="画像:2300ターンくらい、三国時代"]画像:2300ターンくらい、三国時代[/caption]
 
 

完全に天下統一 ※Whileに変更したので500回のサンプルとは違う回
[caption id="attachment_1054" align="alignnone" width="300" caption="画像:天下統一!"]画像:天下統一![/caption]
 

見事9102ターン目で PC-98(仮名) が天下統一を果たした、Cバス万歳と言うことなんだろうか。
 
 
 

えーかげん と言うか 今更というか、Excelマクロ も活用したほうがいいなと思った。

2008年9月23日火曜日

なんとなくWindowsでC言語プログラミング_(2)

「なんとなくPシリーズ」タグを作って続行。タイトルは 続 → 新 → … と続けたかったがやめておく。
一応コンセプトとしてはC言語を勉強してLinuxカーネルソースに迫ろうとしているのだが、果たして。
 
 

前回のなんとなくWindowsでC言語プログラミングで思い出したものがある。
こんにちはマイコン」、1982年!の本。次は折角なのでそれっぽいことをやってみよう。
変数のお勉強でたしかこんなコード、N-88 Basic だっけ?
10 INPUT "ナマエ ヲ ニュウリョク";A$
20 PRINT A$;"ノ アホー"
(30 goto 20)
※3行目gotoもしたかったけど、今回大分話がそれていくのでパス。
 
キーボードから文字列の入力を促し、変数に代入。それを標準出力から表示する単純なプログラムだ。
これの下りは面白かった記憶がある。
 

ついでなのでbatで再現


[sourcecode language='bash']
@echo off
set /P name="ナマエ ヲ ニュウリョク"
:10
echo %name% ノ アホー
goto 10
[/sourcecode]
簡単だが、やはりこれでは展開がない。
 
 

それではCで再現


main(void)とコメントを覚えた。
[sourcecode language='c']
#include

int main(void) {
char name[8]; /*メモリ8バイト確保!*/
printf("ナマエ ヲ ニュウリョク");
scanf("%s",name); /*入力をnameのポインタのとこに並べる*/
printf("%sノ アホー\n",name); /*nameの中身を出力*/
return 0;
}

[/sourcecode]

収穫は変数の宣言とscanfのおかげでポインタがなんとなく分かった事。

実行してみる。
>inputter.exe
ナマエ ヲ ニュウリョクarashi
arashiノ アホー

上々。
 
char宣言で8バイトメモリを確保、アドレスの情報をポインタとしてnameに格納しているというわけかいな。
 

実験タイム1


ほなここで実験してみよう、変数2つ宣言したら並ぶの?
[sourcecode language='c']
#include

int main(void) {
char name[8]; /*メモリ8バイト確保!*/
char name1[8]; /*同様にname1を宣言する*/
printf("ナマエ ヲ ニュウリョク");
scanf("%s",name);
printf("%sサンハ テンサイ\n",name);
printf("%x\n",&name); /*nameのアドレス*/
printf("%x\n",&name1); /*name1のアドレス*/
return 0;
}

[/sourcecode]


>inputter.exe
ナマエ ヲ ニュウリョクsatoru
satoruサンハ テンサイ
12ff64
12ff6c


えーと、8バイトだからー。 12ff64 から 12ff6b までで8バイト、次は 12ff6c から、、合ってる。並んでるね。
 

実験タイム2


入出力で実験、入力してない変数を出力するとか。
[sourcecode language='c']
#include

int main(void) {
char name[8];
char name1[8];
printf("ナマエ ヲ ニュウリョク");
scanf("%s",name);
printf("%sサンハ テンサイ\n",name);
printf("%sノ アホー\n",name1); /*入力してないname1を出力する*/
return 0;
}
[/sourcecode]

実行する、まずは普通の入力
>inputter.exe
ナマエ ヲ ニュウリョクippata
ippataサンハ テンサイ
3n@ノ アホー

3n@ てなんじゃらほい。確保したメモリに元からあった情報と言うことらしいけど、まあ0で埋めるなりの初期化すれば問題ないことが分かった。
 

でも次の入力。20文字入れてみる。
>inputter.exe
ナマエ ヲ ニュウリョク123456789a123456789b
123456789a123456789bサンハ テンサイ
9a123456789bノ アホー

ああああぁ、は、はみ出したーー
scanf()に脆弱性が、っていうのはテクニカルエンジニアで習ったけどこういうことか、ものすごく納得。
とはいえ、狙ったコードの実行は至難の技にも見えるんだけど、どうなんだろう。なんか手法が確立されてるんだろうか。
 
 

そして脱線へ


これはどうやって動いているんだろうと思ったのでちょっと追う。
WindowsのCコンパイラは "/Fa" というオプションをつけたらアセンブリリストのファイルを作ってくれる。
で、作ってみた。ちょっと省略してこんな感じ。
_main PROC
; File c:\cprog\inputter.c
; Line 3
push ebp
mov ebp, esp
sub esp, 20 ; 00000014H
mov eax, DWORD PTR ___security_cookie
xor eax, ebp
mov DWORD PTR __$ArrayPad$[ebp], eax
; Line 6
push OFFSET $SG2471
call _printf
add esp, 4
; Line 7
lea eax, DWORD PTR _name$[ebp]
push eax
push OFFSET $SG2472
call _scanf
add esp, 8
; Line 8
lea ecx, DWORD PTR _name$[ebp]
push ecx
push OFFSET $SG2473
call _printf
add esp, 8
; Line 9
lea edx, DWORD PTR _name1$[ebp]
push edx
push OFFSET $SG2474
call _printf
add esp, 8
; Line 10
xor eax, eax
; Line 11
mov ecx, DWORD PTR __$ArrayPad$[ebp]
xor ecx, ebp
call @__security_check_cookie@4
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

 

うーん。。まさにこれは全部書いてあるんだろうけど、次回だな。
 
 

しかし カーネル への道のはずなんだが。。。やっぱり遠回りなのでは。
そうだコンテキストスイッチというのはこれを切り替えているんだろうなと言うことでまとめよう。

2008年9月22日月曜日

なんとなくWindowsでC言語プログラミング

Linuxのカーネルコードを読みたいと思ったんだが、家にLinuxないしC読めない。
じゃあどうしよう。
 
 

まずMicrosoftがCの開発環境をタダで配っているので自宅のWindows XP SP2にインストールしてみる。

Microsoft Visual C++ 2008 Express Edition
http://www.microsoft.com/japan/msdn/vstudio/express/default.aspx


 

うむ、インストール問題無し。
 
では "hello.c" としてc言語のファイルを適当なテキストエディタで書く。
 

標準入出力の関数色々はいったヘッダファイル "stdio .h" を取り込んでー
正常終了に0を返す main 関数を一個書いて名前をつけて保存、"hello.c"
[sourcecode language='c']
# include

int main() {
printf("Hello Work!\n");
return 0;
}
[/sourcecode]

 

付属のCコンパイラらしきツール "cl.exe" でコンパイルしよう。
>cl hello.c
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.


hello.c
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.


/out:hello.exe
hello.obj

 

よし、 "hello.exe" が完成したぞ、早速実行だ。
>hello.exe
Hello Work!

 

ようし問題なし。
 
 

あとはシステムコールを把握するために C言語の勉強しながら "stdio.h" を読み解いてー
Windowsのカーネルソースを想像して、Linuxに当てはめてー
POSIXに思いを馳せて行けばよいのかしら。。。
 

!? 。これはもしやすごく遠回りしているのではなかろうか?