PHPとファーストコンタクト

食わず嫌いだったPHP。触ってみると手に馴染みそうで良い。
早速、フィボナッチ数列に挑む。

<?php
# 整数の無限リスト
function stream($i) {
  return function() use (&$i) {
    return $i++;
  };
}

# フィボナッチ数列の無限リスト
function fibos() {
  $lazy  = stream(0);
  $prevs = array(0, 0);
  return function() use ($lazy, &$prevs) {
    $now = $lazy();
    if ($now == 0 || $now == 1) {
      $ans = $now;
    }
    else {
      $ans = $prevs[0] + $prevs[1];
    }
    $prevs = array($prevs[1], $ans);
    return $ans;
  };
}

以前もやった遅延評価を使った。

続きを読む

Perlで対話型コマンドの実行

前回TeraTermマクロで対話型コマンドの実行をがんばった。けど扱いづらい。Perl使いたい。というわけで、telnetコマンドをfork/execしてpipeで標準入出力を操作してみた。*1 *2

まず、ファイルハンドルから値を読み込む関数と書き出す関数。読み出しでは応答が1秒間無かったら入力待ちと判断してる。*3

use strict;
use warnings;
use IO::Select;

sub read_all {
  my ($stdout_r) = @_;
  my $read_line;
  my $s = IO::Select->new($stdout_r);
  while (1) {
    my @ready = $s->can_read(1);
    last if (@ready == 0);
    sysread($stdout_r, my $in, 1);
    $read_line .= $in;
  }
  return $read_line;
}

sub writeln {
  my ($stdin_w, $write_line) = @_;
  syswrite($stdin_w, "$write_line\n", length("$write_line\n"));
}

次がtelnetをfork/execするtelnet関数。ログイン処理と終了処理も行う。*4 *5

*1:CPANが使えればNet::Telnet使うんだけどな・・・。

*2:直接対話型コマンドをfork/execしても良いけど、よりユーザ環境に近づけるためにtelnetを挟みたかった。

*3:タイムアウト監視にはalarm関数を使う方法もある。

*4:exec失敗で固まったりバグだらけ。

*5:fork/execよりopen3関数を使うほうが簡潔だけど、今回はお勉強のためfork/exec

続きを読む

TeraTermマクロで対話型コマンドのテスト

定期的に行われる対話型コマンドの回帰テスト。バッチ化できないからって毎度手動でやるのはアホらしいよね。というわけで、TeraTermマクロでがんばってみる。

まずは簡単なテスト対象。

#include <stdio.h>

int main()
{
  int n = 0;

  do {
    printf("in => ");
    scanf("%d", &n);
    if (n == 1) {
      printf("Hello, world%d\n", n);
    }
    else {
      printf("error%d\n", n);
    }
  }
  while (n != 1);

  return 0;
}

プロンプトを表示して入力を待つ。「1」を入力したらプログラムを終了する。入力値に応じて文字列を出力する。かなり手抜き。

そして、上記を実行するTeraTermのマクロ。

続きを読む

そしてCOBOLerへ…

仕事でCOBOLを扱うことになった。少し勉強中。とりあえずオブジェクト指向してみる。

まず、speakメソッドを有するAnimalインタフェースを定義する。

      *>「Animal」インタフェース
       identification division.
       interface-id. Animal.

       procedure division.
         identification division.
         method-id. speak.
         procedure division.
         end method speak.
       end interface Animal.

で、それを実装するのがDogクラスとDuckクラス。

続きを読む

非決定性 with 例外

『On Lisp』の非決定性が面白い。

(define (two-numbers)
  (list (choose '(0 1 2 3 4 5))
        (choose '(0 1 2 3 4 5))))

(define (parlor-trick sum)
  (let ((nums (two-numbers)))
    (if (= (apply + nums) sum)
        `(the sum of ,@nums)
        (fail))))

> (parlor-trick 7)
(THE SUM OF 2 5)
On Lisp - 非決定性

 choose関数が正しい選択を行うと仮定したコードになっている。カラクリは単純。choose関数に指定された数値分の継続を順番に実行しているだけ。継続の実行をfail関数で行うことでfail関数が呼ばれないパスが求めるべき解になる。
 実は継続を使わなくても例外でできる。というわけで、D言語で実装してみる。

続きを読む

Perlで継続

現在、『On Lisp』が3巡目の終盤に差し掛かっている。でも、いまいち継続を理解できていない。分かり易い解説を見つけたのでPerlに翻訳してみた。(本を読む FizzBuzzとGaucheで学ぶ継続の基礎)

use strict;
use warnings;

sub fizzbuzz {
  my ($next, ($n)) = @_;
  my $answer = $n % 15 == 0 ? "FizzBuzz"
             : $n %  3 == 0 ? "Fizz"
             : $n %  5 == 0 ? "Buzz"
             : $n;

  # $nextを挟んで再帰呼び出し
  $next->(\&fizzbuzz, ($n + 1, $answer));
}

sub continuation {
  my ($next, ($n, $answer)) = @_;

  # 状態を閉じ込めた状態で関数終了
  my $cc = sub {
    my ($fun) = @_;
    $fun->($answer) if (ref $fun eq 'CODE');
    $next->(\&continuation, ($n));
  };
}

# 1〜100のfizzbuzz継続を生成してリストに保存
my @restart = fizzbuzz(\&continuation, (1));
push @restart, $restart[$#restart]->() for 2..100;

# 1〜100のfizzbuzz継続を実行
$restart[$_]->( sub{ print @_, $_ != $#restart ? '|' : '' } ) for 0..$#restart;

100までのFizzBuzzが表示される。

続きを読む

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

ユニットテストの自動化で困るのが副作用を起こすコード。例えば、ファイルの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を強制的にテストスタブにしてやれば良い。

続きを読む