昔、ホントにお世話になった「C言語による標準アルゴリズム事典」ですが、いつの間にか改訂新版になっていたことを最近知りました。 もう業としてはプログラミングしていないのだけれど、思わず買いなおしてしまい、改めてコツコツ読んでます (^^)。
以前は仕事で必要になった時に参照するばかりで、理解するのにせいいっぱいだったのですが、 今改めてコツコツ読んでいると、この本、アルゴリズムだけでなく、サンプルソースの中にある、 小さなコードのかけらも面白いなぁ…と。
読みながらそんなかけらを見つけたら、ちょっとずつここに追記していく予定です。
なお、下記に掲載しているコードは、見つけた部分をわかりやすくするために、元のソースから抜粋・改変しています。
※できたらほかのコードのかけらも集めたいのですが、当分の間は当該本が主な出典になりそうです。
「C言語による標準アルゴリズム事典」の Luhn(ルーン) のアルゴリズムのソース内で見つけました(luhn.c)。
下記のループ内で変数 w は、交互に 1 または 2 の値を取ります。
#include <stdio.h> int main(void) { int w = 1; for (int i = 0; i < 10; ++i) { printf("w: [%d]\n", w); w = 3 - w; } return 0; }
最初見たとき意味がわかりませんでした。本の説明から、変数 w が交互に 1 または 2 の値を取ることはわかっていましたが、 ソースを実行し初めてコードの真意がわかりました。 自分には絶対思いつかないロジックで、純粋にすごいなぁ!、と感じました。
「C言語による標準アルゴリズム事典」の簡単な暗号化・復号プログラムのサンプルソース内で見つけました(crypt.c)。
#include <stdio.h> #include <stdlib.h> #include <time.h> int get_rand_value() { int r; do { // NOTE: 前提として RAND_MAX + 1 は 256 以上であること r = rand() / ((RAND_MAX + 1U) / 256); } while (r >= 256); return r; } int main(void) { srand((unsigned int) time(NULL)); // TODO: 適切な引数に変更してください // 乱数を 10 個取得する for (int i = 0; i < 10; ++i) { int r = get_rand_value(); printf("r: [%d]\n", r); } return 0; }
本の説明にもありますが、RAND_MAX + 1 が 256 の倍数なら do {} while(); でループさせる必要はありません(r = rand() ... の呼び出し部分のみで良い)。
乱数を範囲内に収めるというとパッと思いつくのは剰余演算(%)を使うことですが、 上記方法の方が、乱数の元の分布状態に基づいた感じで取得できるような気がしていいのかも(よくわかってない… ^^;)。。。
「C言語による標準アルゴリズム事典」の 石取りゲーム 1 のサンプルソース内で見つけました(ishi1.c)。
下記のループ内で変数 flag は、交互に 0 または 1 の値を取ります。
#include <stdio.h> int main(void) { int flag = 0; for (int i = 0; i < 10; ++i, flag ^= 1) { printf("%d: %d\n", i, flag); } return 0; }
排他的論理和をこのように使ったことがありませんでした。 自分は以下のように論理否定を使っていた記憶が…。いろんな方法があるのだなぁと面白く感じました。
flag = !flag
「C言語による標準アルゴリズム事典」の 石取りゲーム 1 のサンプルソース内で見つけました(ishi1.c)。
#include <stdio.h> int main(void) { int num = 0; while (num != 9) { printf("1 から 9 までの数字を入力してください: "); int r = scanf("%d", &num); scanf("%*[^\n]"); // 数字以外を読み飛ばす if (r != 0) { printf("r: [%d], num: [%d]\n", r, num); } } return 0; }
上記コードのループ内の以下の部分は、数字以外の入力文字を読み飛ばします(代入先の変数は不要)。
scanf("%*[^\n]"); // 数字以外を読み飛ばす
もし、以下のように読み飛ばしのための scanf がないまま動作させ、数字以外を入力すると、入力から数値を取り出せず、 いつまでもループを回り続けてしまいます。
#include <stdio.h> int main(void) { int num = 0; while (num != 9) { printf("1 から 9 までの数字を入力してください: "); int r = scanf("%d", &num); ////scanf("%*[^\n]"); // 数字以外を読み飛ばす if (r != 0) { printf("r: [%d], num: [%d]\n", r, num); } } return 0; }
1 から 9 までの数字を入力してください: a 1 から 9 までの数字を入力してください: 1 から 9 までの数字を入力してください: 1 から 9 までの数字を入力してください: ... 以下略
久々に scanf の代入抑止(代入先の変数が不要)を見て、すっかり忘れていた機能を思いだし、嬉しかったのです。 上に書いた読み飛ばし云々は脱線・蛇足です。。。
これは「C言語による標準アルゴリズム事典」とは関係なく、また、TIPS 的なものでもありません。
とあるサンプルソースでみつけた以下のようなコードに悩まされました(言語は kotlin)。
※元のコードとは違いますが、改変し単純化しました。
fun main() { sf() { pv -> println(pv.getName()) } }
ぱっと見たときに pv はどっから来た?、と思っちゃったのですよね。
pv はラムダの仮引数定義なので問題ないコードなのですが、 kotlin に慣れてない自分には型省略やトレーリングラムダとあいまって、 混乱してしまいました。
もうちょっときちんと?書いた全体は以下のような感じで、PV や sf はライブラリ側にあります。
class PV(cname: String) { private val name: String = cname fun getName(): String { return name } } fun sf( cn: (PV) -> Unit ): Unit { val pv = PV(cname = "Foo") cn(pv) } fun main() { sf(cn = { pv -> println(pv.getName()) }) }
kotlin、私にとっては癖がありなかなか手強いです (*_*;)