カーニハンのプログラミング作法を Delphi で

| | コメント(0) | トラックバック(0)

プログラミング作法』の中に出てくる、マルコフ連鎖 C バージョンを Delphi で書いてみた。ただし、標準入出力は使わずに、TStringList にテキストを読んでしまってから処理。結果も TStringList に返ってくるようにしてみた。

unit Markov_C;
interface
uses Classes, SysUtils, Windows;
const NPREF = 2; NHASH = 4093; MAXGEN = 10000; NONWORD = #$0A;
type PPrefix = ^TPrefix; TPrefix = array[0..NPREF-1] of PChar; PSuffix = ^TSuffix; PState = ^TState; TState = packed record Pref: TPrefix; Suf: PSuffix; Next: PState; end; TSuffix = packed record Word: PChar; Next: PSuffix; end; var StateTab: array[0..NHASH-1] of PState;
function Hash(S: PPrefix): Cardinal; function Lookup(Prefix: PPrefix; Create: Boolean): PState; procedure Build(Prefix: PPrefix; FileName: string); procedure Add(Prefix: PPrefix; Suffix: PChar); procedure AddSuffix(SP: PState; Suffix: PChar); procedure Generate(nWords: Integer; SL: TStringList);
implementation
function Hash(S: PPrefix): Cardinal; var H: Cardinal; P: PChar; I: Integer; const MULTIPLIER = 31; // set 31 or 37 begin H := 0; for I := 0 to NPREF - 1 do begin P := S[I]; while Ord(P^) <> 0 do begin H := MULTIPLIER * H + Ord(P^); Inc(P); end; end; Result := H mod NHASH; end;
function Lookup(Prefix: PPrefix; Create: Boolean): PState; var I, H: Integer; SP: PState; begin H := Hash(Prefix); SP := StateTab[H]; while SP <> nil do begin for I := 0 to NPREF - 1 do if StrComp(Prefix[I], SP.Pref[I]) <> 0 then Break; if I = NPREF then begin Result := SP; Exit; end; SP := SP.Next; end; if Create then begin GetMem(SP, Sizeof(TState)); for I := 0 to NPREF - 1 do SP.Pref[I] := Prefix[I]; SP.Suf := nil; SP.Next := StateTab[H]; StateTab[H] := SP; end; Result := SP; end;
procedure Build(Prefix: PPrefix; FileName: string); var SL: TStringList; I: Integer; Buf: PChar; begin SL := TStringList.Create; try SL.LoadFromFile(FileName); SL.Text := StringReplace(SL.Text, ' ', #$0D#$0A, [rfReplaceAll]); for I := 0 to SL.Count - 1 do if SL[I] <> '' then begin GetMem(Buf, Length(SL[I]) + 1); StrCopy(Buf, PChar(SL[I])); Add(Prefix, Buf); end; finally SL.Free; end; end;
procedure Add(Prefix: PPrefix; Suffix: PChar); var SP: PState; begin SP := Lookup(Prefix, True); AddSuffix(SP, Suffix); System.Move(Prefix[1], Prefix[0], (NPREF-1)*SizeOf(Prefix[0])); Prefix[NPREF-1] := Suffix; end;
procedure AddSuffix(SP: PState; Suffix: PChar); var Suf: PSuffix; begin GetMem(Suf, SizeOf(TSuffix)); Suf.Word := Suffix; Suf.Next := SP.Suf; SP.Suf := Suf; end;
procedure Generate(nWords: Integer; SL: TStringList); var SP: PState; Suf: PSuffix; Prefix: TPrefix; W: PChar; I, nMatch: Integer; begin for I := 0 to NPREF - 1 do Prefix[I] := NONWORD; Randomize; W := nil; //コンパイラを黙らせる for I := 0 to nWords - 1 do begin SP := Lookup(@Prefix, False); nMatch := 0; Suf := SP.Suf; while Suf <> nil do begin Inc(nMatch); if (Random($7FFF) mod nMatch) = 0 then W := Suf.Word; Suf := Suf.Next; end; if StrComp(W, NONWORD) = 0 then Break; SL.Add(W); System.Move(Prefix[1], Prefix[0], (NPREF-1)*SizeOf(Prefix[0])); Prefix[NPREF-1] := W; end; end;
end.

使うときはこんな感じ

procedure TForm1.btnBuildAndSaveClick(Sender: TObject);
var
  Prefix: TPrefix;
  I: Integer;
  SL: TStringList;
  FileName: string;
begin
  if not OpenDialog1.Execute then Exit;
  FileName := OpenDialog1.FileName;
  for I := 0 to NPREF - 1 do
    Prefix[I] := NONWORD;
  Build(@Prefix, FileName);
  Add(@Prefix, NONWORD);
  SL := TStringList.Create;
  try
    Generate(MAXGEN, SL);
    SL.Text := StringReplace(SL.Text, #$0D#$0A, ' ', [rfReplaceAll]);
    SL.SaveToFile(ChangeFileExt(FileName, '_out.txt'));
  finally
    SL.Free;
  end;
end;

なんか冗長な感じ・・・ツッコミ希望。次は Java, C++ バージョンを Delphi らしく書いてみたい。

トラックバック(0)

このブログ記事を参照しているブログ一覧: カーニハンのプログラミング作法を Delphi で

このブログ記事に対するトラックバックURL: http://www.towofu.net/cgi-bin/mt/mt-tb.cgi/67

コメントする

このブログ記事について

このページは、towofuが2006年1月31日 22:05に書いたブログ記事です。

ひとつ前のブログ記事は「こわい紙芝居」です。

次のブログ記事は「ちゃっかり google」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。