2013年9月15日日曜日

Lazarus 1.0.10 to 1.0.12

Lazarus 1.0.10 の環境から ようやく Lazarus 1.0.12 に移行しました。

FPCは 2.6.2 のままのようです。

http://forum.lazarus.freepascal.org/index.php/topic,21876.msg128492/topicseen.html#new

http://wiki.lazarus.freepascal.org/Lazarus_1.0_fixes_branch#Fixes_for_1.0.12_.28Merged.29

http://wiki.lazarus.freepascal.org/User_Changes_2.6.2

バグの修正が結構あるようなのでアップデートしておいたほうが良さそうです。

最初に 1.0.10 をアンインストールすることなく、1.0.12 のインストーラーを起動しても大丈夫です。

途中でアンインストールできますし、両バージョンを生かした環境も作れるようです。

インストール途中に旧バージョンをアンインストールした場合は、残ったファイルのうちLCLや Components 等で特に変更した覚えのないものは削除したほうが良いと思います(私的には)

設定などはIDE起動時にアップグレードされて引き継がれます。

特に問題無さそうです。

以上です。

TFileStream の 438 の謎


Lazarus,FPC の

(from FileUtil streams.inc)

constructor TFileStream.Create(const AFileName: string; Mode: Word);

begin
  Create(AFileName,Mode,438);
end;  

438 というのが、あります。

https://github.com/graemeg/freepascal/blob/master/rtl/solaris/ostypes.inc


 S_IRUSR = $100;           { Read permission for owner   }
 S_IWUSR = $080;           { Write permission for owner  }
 S_IXUSR = $040;           { Exec  permission for owner  }
 S_IRGRP = $020;           { Read permission for group   }
 S_IWGRP = $010;           { Write permission for group  }
 S_IXGRP = $008;           { Exec permission for group   }
 S_IROTH = $004;           { Read permission for world   }
 S_IWOTH = $002;           { Write permission for world  }
 S_IXOTH = $001;           { Exec permission for world   }  

ググっても出てこないので一応計算してみると

S_IRUSR  or S_IWUSR or S_IRGRP or
S_IWGRP or S_IROTH  or S_IWOTH = 438 でした。

それだけ。

すっきり。

Windows の場合 0 でも良さそうです。

TFileStream - THandleObject - TStream - TObject

ちなみに、このconstructor TFileStream.Create は、2つあり、どちらの Create も inherited されていません。
何故なのでしょう。

Create は THandleStream で初めて実装されています。
派生元の THandleStream は Handle の設定しかしていません。
こちらは inherited されているようですが・・・。

THandleStream の FHandle がスコープにあるので、アクセスできます。
それで一応、FHandleに値は設定もされているし動作も影響無さそうです。
var
 h: THandle;
inheritec Create( h)

とするか

FHandle:= h;

とするかの違いしかありません。

destructor も inherited されていませんが、TFileStream で初めて実装されます。

こちらも全く問題無さそうです。

一瞬放置されたコードなのかと思ってしまいました。

Delphi も一応確認したら似たような感じでした。





PS
Delphi の定義と異なるらしい。(上記の定義はFPCのもの。)詳細不明。(最新のは持ってないので)

2013年9月14日土曜日

TThreadList.Duplicates は dupIgnore

TStringList も TThreadList も Duplicates の型は同じです。

TDuplicates = (dupIgnore, dupAccept, dupError);

(Delphi とは順番が違いますが、先頭は同じなので default 指定や、コンストラクタ等で値が設定されていなければ dupIgnore になります)

dupIgnore は同じ値を追加しようとしても無視されて追加されません。

しかし、 TStringList は Sorted=True にしないと、dupIgnore が機能しません。

なので

Duplicates が dupIgnore のままであっても

sl:=TStringList.Create;
sl.Add('A');
sl.Add('A');
sl.Add('A');

とやると

sl.Count = 3 です。


sl:=TStringList.Create;
sl.Sorted:=True;
sl.Add('A');
sl.Add('A');
sl.Add('A');

とすれば

sl.Count = 1 です。

問題なのは TThreadList で、Duplicates のデフォルトは同じように dupIgnore になっています。
Sorted プロパティなどは無く、インスタンス作成直後から同じ値が追加されない仕様です。

TThreadList で、オブジェクトのインスタンスなどを管理する場合は、毎回異なる値が追加されるので問題ありませんが、その他の目的で利用しようと思う場合は注意が必要です。

obj:= TTest.Create;
lst:= TThreadList.Create;
lst.Add(obj1);
lst.Add(obj1);
lst.Add(obj1);

with lst.LockList do
try
  OutPutDebugString(PChar(IntToStr(Count))
finally
  lst.UnlockList;
end;

イベントログに表示される Count は 1 です。

なので、重複を許可したい場合は、インスタンス作成後に Duplicates:= dupAccept に設定するのを忘れないようにしないといけません。

lst:= TThreadList.Create;
lst.Duplicates:= dupAccept;
lst.Add(obj1);
lst.Add(obj1);
lst.Add(obj1);

with lst.LockList do
try
  OutPutDebugString(PChar(IntToStr(Count))
finally
  lst.UnlockList;
end;

イベントログに表示される Count は 3 です。

ということを忘れたまま、オブジェクトのスタック的なものを実装しようとしているとハマってしまいますのでご注意あれ。

ちなみに TList は Duplicates プロパティはありません。


FPC も Delphi も同じです。

2013年9月11日水曜日

Lazarus IDE でのコードの辿り方。宣言と実現部の切り替えとか。

Lazarus IDE でのコードの辿り方。

Interface部 と Implementation部 の切り替え。

CTRL + SHIFT + UP(カーソルキー↑)
CTRL + SHIFT + DOWN(カーソルキー↓)


他のIDE同様、関数や変数名等の単語 を CTRL + マウスクリック すると宣言部までジャンプしてくれます。(手続き・関数の場合は実装まで)

また、CTRL + SHIFT + UP で、宣言と実現部を交互に切り替えてジャンプできます。
UP も DOWN も同じです。

ライブラリの実装が .inc により分離されているものが多く、この機能なしにはコードを深く辿ることが困難なように思います。


以上。

IFNDEF DELPHI は使わない

Delphi 互換のコードを書く場合に、{$IFNDEF DELPHI} は使わないほうが良いらしい。

やってたし・・・。

また、Lazarus, FPC でコンパイルする場合、指示しなくとも FPC が DEFINE される。

という事なので、{$IFDEF FPC} と {$IFNDEF FPC} ならば使っても問題無さそうです。

気が付いてよかった・・・。


情報元: http://wiki.freepascal.org/Code_Conversion_Guide#Compiler_Issues

によると

{$MODE Delphi} によって Delphi モード にした場合に、DELPHI が DEFINE されてしまう。

なので IFNDEF Delphi が FPC 上であってもコンパイルされなくなってしまう。

また、Delphi 互換のコードを書く場合、{$MODE} は Delphi によってエラーとされてしまうので

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

のように書くといいそう。

そのうち Delphi でもコンパイルしてみようとは思っていたけど、Delphi の環境無いからテストできぬ。

2013年9月10日火曜日

Lazarus IDE をドッキング対応にする


Lazarus IDE をドッカブルにできるようです。
使うかどうかはともかく一応できたようです。
既にSDIで慣れてしまいましたが・・・。

インストールとアンインストール
(1) パッケージパッケージをインストールもしくはアンインストール
(2) anchordock anchordockingdsgn をダブルクリックして左側のインストールに表示させます。
保存してIDEを再構築」します。
(アンインストールは同様にインストール項目からダブルクリックしてこれらを外して再構築すればOKです。)


 



昔の Delphi 風
再構築が完了して再起動されると、そのままでは難ありな配置になっていますので調整します。
ドッキングするにはべベル部分をドラッグして他のウィンドウに重ねます。




近頃の IDE 風



のように配置できるようになります。
ウィンドウの情報を保存するにはツールから保存しないといけないようです。
IDEを終了、再起動しただけでは配置が復元されませんでした。

べベル(Headers)の非表示
べベルを右クリックして、ポップアップメニューの Show Headersオフにすると、べベルが表示されなくなります。

べベル(Headers)の再表示
べベルがなくなると、調整できなくなってしまいます。
公式の方法は不明ですが、とりあえず
べベルを再表示するには

  表示アンカーエディタを表示

から表示されるアンカーエディタのべベルを右クリックして Show Headersオンにすれば他のウィ ンドウのべベルも表示されるようになります。



以上です。

LevenshteinDistance

あんまりネタもないので、たまにはコードでも書いてみよう。

LevenshteinDistance については wiki 参照。

http://ja.wikipedia.org/wiki/レーベンシュタイン距離

書いては見たけど使わないことになってしまった。

まあたいしたものではないけど。
勿体ない気もするので晒しておきます。

NewBSD License のもと、ご自由に。

一応 Lazarus,FPC 用に書いたつもりですが、基本的に私はObjectPascal については Borland系 のコードしか書けないので Delphi でも動くはずです。
(FillByteはZeroMemoryにしてください)

課題とか。
とりあえず、arrayはとりましたが興味が失せてきたので終わりにします。
式を単純化したり変数やアドレスの計算も減らせそうです。

wikiのまんまの関数は基礎についての解説の一部なんだと思うんですが、おそらく

     // const  SignChars=[33..47,58..63,91..96,123..126];

        case  b1^-b2^ of
          0:
            cost:= 0; // 同じ文字

          -32,32:
            cost:= 2  // 大文字、小文字

          else        // 違う文字
            begin
              if (b1^ = 32) or (b2^ = 32) then
                cost:= 3   // どちらかが空白
              else
              if (b1^ in SignChars) and (b2^ in SignChars) then // *1
                cost:= 1   // どちらも記号
              else
              if (b1^ in SignChars) or (b2^ in SignChars) then
                cost:= 4   // 記号とAaZz
              else
              if (b1^ in [48..57]) and (b2^ in [48..57]) then
                cost:= 1   // どちらも数字
              else
              if (b1^ in [48..57]) or (b2^ in [48..57]) then
                cost:= 4   // 数値とAaZz
              else
                cost:= 5;  //その他(上記以外の文字)
            end;
        end;


のようにして使う事を前提に書かれた解説だと思うんですが、どうなんだろう。
実例が知りたいのだけど、ググっても海外含めて追加・削除・置換しか出てこないんでスッキリしない。
コスト判定が行動主体だけではあんまり意味ないような気がする。
(以下に組み込む場合は、dIns:= iptr^; // + 1; を  dIns:= iptr^+4; // + 5; ぐらいに調整。us-ascii しか考慮していません)

上のまんまなんだけど、例えば数字。
tes4 と入力して tes5 と tes3 は近いほうがいいと思いますし、test はそれより遠いほうがいいように思います。
まあやっぱその辺は状況に応じてカスタムするんだろうな。
そのまま使うわけないよな・・・。


以下コード。

function LevenshteinDistance(s1, s2: string): integer; experimental;
var
  arr : pointer;  // data

  isz : integer;  // size of integer
  sz  : integer;  // total size
  xlen,           // s1 length +1
  ylen: integer;  // s2 length +1

  x,y : integer;
  xptr,
  yptr,
  iptr: pinteger;
  b1,b2: pbyte;   // s1,s2 pointer

  dIns,           // insert cost
  dDel,           // delete cost
  dRpl,           // replace cost
  dRes,           // result
  cost: integer;  // cost

  dist1,           // distance of dIns,dDel address
  dist2: integer;  // distance of dDel,dRpl address
begin
  xlen:= Length(s1)+1;
  ylen:= Length(s2)+1;

  isz:= SizeOf(integer);
  sz:= (xlen * ylen) * isz;
  GetMem(arr,sz);
  try
  {$IFDEF DELPHI}
  ZeroMemory(arr,sz);  // Delphi, or Lazarus,FPC with Windows.pas
  {$ELSE}    
   FillByte(arr^,0,sz);
  {$ENDIF}

    //[x,0]
    xptr:= arr;
    for x:=0 to xlen-1 do
    begin
      xptr^:= x;
      inc(xptr);
    end;
    //[0,y]
    yptr:= arr;
    for y:=0 to ylen-1 do
    begin
      yptr^:= y;
      inc(yptr,xlen);
    end;

    for x:=0 to xlen-1 do
    begin
      for y:=0 to ylen-1 do
      begin
        //iptr := arr + ((x  ) + ((y  )*xlen))*isz;
        iptr := arr;
        inc(iptr, (x  ) + ((y  )*xlen));
      end;
    end;

    x:= 1;
    y:= 1;
    dist1:= ((x-1) + ((y  )*xlen)) - ((x  ) + ((y-1)*xlen));
    dist2:= ((x-1) + ((y-1)*xlen)) - ((x-1) + ((y  )*xlen));

    //b1:=PByte(PChar(s1));
    b1:=@s1[1];
    for x:=1 to xlen-1 do
    begin
      //b2:=PByte(PChar(s2));
      b2:=@s2[1];
      for y:=1 to ylen-1 do
      begin
          // 最後に足す事にして全ての cost から -1 する
          if b1^-b2^=0 then
             cost:= -1 // 0
          else
             cost:= 0; // 1;

        //iptr:= arr + ((x  ) + ((y-1)*xlen))*isz;
        iptr:= arr;
        inc(iptr,(x  ) + ((y-1)*xlen));
        dIns:= iptr^;  // + 1;

        //iptr:= arr + ((x-1) + ((y  )*xlen))*isz;
        //iptr:= arr;
        //inc(iptr,(x-1) + ((y  )*xlen));
        inc(iptr,dist1);
        dDel:= iptr^;  // + 1;

        //iptr:= arr + ((x-1) + ((y-1)*xlen))*isz;
        //iptr:= arr;
        //inc(iptr,(x-1) + ((y-1)*xlen));
        inc(iptr,dist2);
        dRpl:= iptr^ + cost;

        dRes:= dIns;
        if dRes>dDel then dRes:= dDel;
        if dRes>dRpl then dRes:= dRpl;

        //iptr := arr + ((x  ) + ((y  )*xlen))*isz;
        iptr:= arr;
        inc(iptr,(x  ) + ((y  )*xlen));

        //iptr^:=dRes;
        iptr^:=dRes + 1; //最後に足せばいい

        inc(b2);
      end;
      inc(b1);
    end;

    //iptr:= arr + ((xlen-1) + ((ylen-1)*xlen))*isz;
    iptr:= arr;
    inc(iptr,(xlen-1) + ((ylen-1)*xlen));

    Result := iptr^;
  finally
    FreeMem(arr,sz);
  end;
end;

補足
コメントアウトされたコードは最適化前のコードです。
直後のコードと等価です。

注意
inc で進むポインタのアドレスは isz 倍しなくても大丈夫です。
逆に iptr のアドレスを計算する際は arr から 進める分を isz 倍しないと、おかしくなります。