副作用関数のユニットテスト

ユニットテストの自動化で困るのが副作用を起こすコード。例えば、ファイルのI/Oを含む関数は自動テスト化に少し悩む。

# hoge.pl
use strict;
use warnings;

package HOGE;
use IO::File;

main(@ARGV) if ($0 eq __FILE__);

# テストしたい関数
sub main {
  my (@args) = @_;
  return 1 unless (@args >= 2);
  return 2 unless defined( my $io = IO::File->new($args[0], 'w') );
  unless ( defined $io->print($args[1]. "\n") ) {
    $io->close;
    return 3
  };
  return 4 unless defined $io->close;
  return 0;
}
1;

この場合、テストドライバから"use lib"でモジュール参照先を変更してIO::Fileを強制的にテストスタブにしてやれば良い。

続きを読む

Perlで遅延評価

遅延評価とは、HaskellD言語なんかにある「アレ」のことだ。

遅延評価(ちえんひょうか、lazy evaluation、delayed evaluation)とは、計算機科学におけるプログラムの評価手法の一つ。主に関数型言語で使用される。対義語は先行評価(eager evaluation、strict evaluation)。

評価しなければならない値が存在するとき、実際の計算を値が必要になるまで行わないことをいう。評価法が指示されているが実際の計算が行われていない中間状態の時それをプロミス (promise) や、計算の実体をさしてサンク (thunk) といい、プロミスを強制(force)することで値が計算される。一旦計算された値はキャッシュをすることが可能であり、遅延プロミスは最大で一度しか計算されないようにすることができる。ただし、Haskellの実装によっては、何度でも同じ計算を行う。

http://ja.wikipedia.org/wiki/%E9%81%85%E5%BB%B6%E8%A9%95%E4%BE%A1

要するに式の評価を結果が必要になる時点まで遅らせるテクニックである。
Perlでやってみた。定番のFizzBuzz

続きを読む

Lispお勉強中

 心に余裕が出てきたこともあってLispを再学習中。今は『実践Common Lispを読んでいる。やっと第12章が終わった。
 思い返すと腰を落ち着けてプログラミング言語の勉強をやってない。何とか使いこなしているのはC言語とPerlくらい。どちらも仕事で使っているおかげだ。他は書籍を斜め読みしてわかった気になっているだけ。実践が足りない。広く浅くは土地勘を養うためも重要だ。でも身につけるには狭く深く追求することが必要になる。

 興味がある言語はたくさんある。手始めにLispをマスターしたい。Lispといえばマクロ。マクロによる抽象化を習得できたらプログラミングの幅が広がると期待している。

 どうやって勉強しようか?まずは書籍を1冊に絞って繰り返し読む。そして、何か実装してみる。後は、ひたすらソースを読み書きする。

 余裕が大事と感じる今日この頃。今だから言えるけど、精神を壊すまで働くのはNG。人生は1度しかない。自分を大事にしたほうが良い。一度ダウンすると結構長引くし医療費もかさむ。

 ある程度のゆとりがないと生産性はあがらない。会社的にもマイナスになる。当たり前だと思うけど当たり前じゃない現実。

 愚痴ばかり・・・


Binary Hacks

『Binary Hacks ―ハッカー秘伝のテクニック100選』を読み終えた。

 本書はその名のとおりGNU/Linux依存のハックが満載だ。ダンプを読み読みしたり、プロセスの中からプロセス情報を読み読み・書き書きしたり、etc。
 抽象化には限界がある。移植性を完全に無視した内容には好感が持てる。とはいえ現実はデバッグやテスト、解析なんかに役立てるのが主だろう。
 というわけで、Linux系のOSで開発している人にオススメ。もちろん、他のOSを使っていても環境べったりの知識は有用なので読んで損は無い。


C言語でクロージャ

クロージャって何だろう?

クロージャは関数と環境を一緒にしたものである。クロージャは、関数がレキシカル環境から何かを参照するときには暗黙に必ず作られている。
ANSI Common Lisp』- 6.5 クロージャ より

どんな風に使うのだろう?

アキュムレータを生成する関数、すなわち、数nを取り、「数iを取ってnをiだけ増加させ、その増加した値を返す関数」を返す関数だ(「増加させる」に注意。ただ足すわけではない。アキュムレータ(蓄積器)だから蓄積されなければ)。
 Common Lispではこんなふうになるだろう。

(defun foo (n)
  (lambda (i) (incf n i))

ハッカーと画家』- 第13章 付録 より

レキシカル環境を暗黙的でなく明示的に作るのとどう違うのだろうか?
というわけでC言語に翻訳してみた。

続きを読む

map and grep, foreach on C

Perlでリスト処理といえばmapとgrepにforeachだと思う。
たとえば、こんな感じで使える。実行すると"6 12 18 24 30"が標準出力に出力される。

#! /usr/bin/perl
use strict;
use warnings;

# 要素に1から10を持つリストを生成する。
my @list1 = (1..10);

# 各要素が3倍のリストを生成する。
my @list2 = map { $_ * 3 } @list1;

# 偶数の要素のリストを生成する。
my @list3 = grep { $_ % 2 == 0 } @list2;

# 各要素を表示する。
foreach (@list3) { print $_, " "; }
print "\n";

これを無性にC言語でやりたくなったので実装してみた。

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

#include "list.h"
List_template(int)

int multi_3(int *in, int *out) {  /* 3倍する */
  *out = *in * 3;
  return TRUE;
}

int is_even(int *in, int *bool) { /* 偶数か? */
  *bool = (*in % 2 == 0) ? TRUE : FALSE;
  return TRUE;
}

int main() {
  List list1 = NULL, list2 = NULL, list3 = NULL;
  int i, *n;

  /* 要素に1から10を持つリストを生成する。 */
  list1 = List_func(create, int)();
  if (list1 == NULL) goto END;
  for (i = 1; i <= 10; i++) {
    if (! List_func(push, int)(list1, &i)) goto END;
  }

  /* 各要素が3倍のリストを生成する。 */
  list2 = List_func(map, int)(list1, multi_3 );
  if (list2 == NULL) goto END;

  /* 偶数の要素のリストを生成する。 */
  list3 = List_func(grep, int)(list2, is_even );
  if (list3 == NULL) goto END;

  /* 各要素を表示する。 */
  List_foreach (n, list3) {
    printf("%d ", *n);
  }
  printf("\n");

END:
  List_func(delete, int)(list1);
  List_func(delete, int)(list2);
  List_func(delete, int)(list3);

  return 0;
}

それでは恐ろしいリストの中身を。
まず、list.hの定義内容はこう。

続きを読む