同じ処理を関数にまとめるには?

解決


なんとん  2003-12-29 15:28:58  No: 6466

Delphiで,ソフト作りに初挑戦しています。

まだローマ字もわからない我が子に,ローマ字で文字入力を
させるためのソフトです。

Formには,キーボードのボタン用にPanelを26個,StringGridに
五十音(まだ増える)の表,Editを3個をのせてあります。

procedure TForm1.StringGrid1MouseMove  に
・・・・・・・・・・・・・・・・・・・・・・・
  Case RC of
    9: begin
      Case CC of
      0: begin
        for i := 1 to 26 do     // ここから
        begin
          Panels[i].color := clBtnFace;
        end;
        Panels[1].color := clYellow;  // ここまで
        Edit2.Text := 'a';
        Edit3.Text := 'A';
        end;
      1: begin
       for i := 1 to 26 do    //  ここから
       begin
         Panels[i].color := clBtnFace;
       end;
        Panels[2].color := clYellow;  // ここまで
        Edit2.Text := 'i';
        Edit3.Text := 'I';
        end;

「ここから」「ここまで」の部分を,1つの関数にまとめたい
のですが,どうしたらいいのでしょうか。
私は次のように自己流(VBA風)でやったら,「書き込み違反」の
エラーになりました。
procedure TForm1.IroHenko1(Pi: integer);
var
  i : Integer;
begin
  for i := 1 to 26 do
  begin
    Panels[i].color := clBtnFace;
  end;
    Panels[Pi].Color := clYellow;

よろしくお願いします。


HOta  2003-12-29 16:35:29  No: 6467

多分、Panelsの数よりPiが多くなってしまったのでしょう。


なんとん  2003-12-29 17:21:01  No: 6468

HOta さん,こんにちは。

>Panelsの数よりPiが多くなってしまったのでしょう
意味がよくわかりませんが,引数の渡し方に問題があるのでしょうか。
最初の「ここから」「ここまで」の変わりに,
    
    IroHenko1(1);

としていて,Panels[1]の色はちゃんと変わります。
引数は26以下を渡しているのですが,それでも
Panelsの数よりPiが多くなることがあるのでしょうか。


Halbow  2003-12-29 17:38:12  No: 6469

Halbow です。

> 引数は26以下を渡しているのですが,

引数をどのように算出しているか、本当にどのような場合でも 26 以下になってい
るか確認してください。関数の中に

if Pi > 26 then exit;

などと範囲チェックするといいかもしれません。

それから Pi というのは、Delphi では 円周率パイの定数にも使われている
ので違う名前の変数にしたほうが混乱がなくていいです。


Halbow  2003-12-29 18:03:09  No: 6470

Halbow です。

追伸です。

Pi がゼロの場合も考慮して

> if Pi > 26 then exit;

じゃなくて

if (Pi > 26) or (Pi<0) then exit;

のほうがいいかもしれません。

一般論ですが、RC と CC はどのように算出しているか分かりませんが
case をたくさん書く必要が出てきたときは論理の整理が出来る可能性が
ある場合があります。上のコードの場合、ひらがな全部を case 文で
判別するのはいかにも効率よくありません。RC = 9 で 

CC が 0 のときは Panels[1] を黄色にし、Edit を 'a' と 'A' にする
CC が 1 のときは Panels[2] を黄色にし、Edit を 'i' と 'I' にする
..........

という論理は CC から関連づけた配列にしておくと、論理の整理ができます。
例えば

type
  TRC9 = record
    PanelNo:integer;
    komoji:Char;
    oomoji:Char;
  end;

const
  RC9:array[0..4] of TRC9 = ((PanelNo: 1; komoji: 'a'; oomoji: 'A'),
                             (PanelNo: 2; komoji: 'i'; oomoji: 'I'),
                             (PanelNo: 3; komoji: 'u'; oomoji: 'U'),
                             (PanelNo: 4; komoji: 'e'; oomoji: 'E'),
                             (PanelNo: 5; komoji: 'o'; oomoji: 'O'));

としておくと

    0..4: begin
          IroHenko1(RC9[CC].PanelNo)
          Edit2.Text := RC9[CC].komoji;
          Edit3.Text := RC9[CC].oomoji;
        end;

というふうに一つの Case 文にまとめることが出来ます。

それから OnMouseMove イベントはものすごく頻繁に起こります。
ですから前回の RC と CC をグローバル変数などで記憶しておき
変更があったときだけ処理を実行するようにするとよいです。


Halbow  2003-12-29 18:04:20  No: 6471

すみません。訂正です。

> if (Pi > 26) or (Pi<0) then exit;

if (Pi > 26) or (Pi<1) then exit;

です。


なんとん  2003-12-29 19:40:32  No: 6472

Halbow さん,こんにちは。

たくさんのご指導有難うございます。
私は,エラーの原因が関数か引数かと思っていましたが,
どうやら,そうではないようです。

関数部分を全部コメントアウトして,実行しても思い通りに
動いていますが,終了のときに次のエラーが出ます。
(関数を書く前までは,正常に終了していました。)

「EAccessViolationがモジュール 〜〜.EXE のFFFFF235で発生しました。
  アドレス00000235でアドレス0344315Eに対する書き込み違反が起きました」

ファイルが壊れたのでしょうか。


Halbow  2003-12-29 19:50:14  No: 6473

Halbow です。

> ファイルが壊れたのでしょうか。

さぁ、だれにも分かりませんよ。
新しくエクスプローラで、ディレクトリをつくって、そこに

*.dpr
*.dfm
*.pas
*.res

のファイルだけをコピーしてから、 *.dpr をダブルクリックして Delphi を
立ち上げ、F9 を押してコンパイル・実行しても、現象が再現するなら、
なんとんさんの書かれたコードが原因だと思います。


なんとん  2003-12-29 20:39:51  No: 6474

Halbow さん,またお世話になります。

四つのファイルをコピーして実行しました。
結果は,見事に同じエラーの発生です。

関数を書くまでは順調だったんですが,その後のコードに
問題があることがわかりました。んーっ。Delphi難しいですね。
でも,これに懲りずに再挑戦します。

それと,引数を渡して処理してもらう方法を調べたいのですが,
そのようなサイトをご存知でないでしょうか。参考書にも事例がなく,
お手上げ状態です。

ということで,ここは解決にしておきます。

HOtaさん,Halbowさん,朝早くからお付き合いくださって
有難うございました。
今後も,よろしくお願いします。


Halbow  2003-12-29 21:43:18  No: 6475

Halbow です。

> それと,引数を渡して処理してもらう方法を調べたいのですが,
> そのようなサイトをご存知でないでしょうか。

これは、あまりに一般的すぎて Tips とするには普通すぎます。
普通の文法のレベルだと思います。

seventh delphi
http://kakinotane.s7.xrea.com/

ここなんか参考になりませんか。


なんとん  2003-12-30 00:22:27  No: 6476

Halbow さん,解決マーク後も見ていただき,感謝しています。
早速下記の,アドレスで調べました。

http://kakinotane.s7.xrea.com/dirDelphi2/d053.html
引数の渡し方は,あまり変わらないなあと思いましたが,私の場合
関数名の前に TForm1.IroHenko1(P1: integer); と TForm1 を
つけないと Panels を認識してくれませんでした。
それで,あちこちのサイトの部品をつぎはぎしたコードではずかしいの
ですが,前半部分を載せます。もし,お気づきの点がありましたら,いろ
いろご指導ください。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Pnl17: TPanel;
    Pnl23: TPanel;
  ……………………… TPanel は合計26個
   StringGrid1: TStringGrid;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure StringGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
  private
    { Private 宣言 }
 //   procedure IroHenko2(P1, P2: integer);
  public
    { Public 宣言 }
    Panels: array[1..11] of TPanel;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i : integer;
begin
  for i := 0 to 9 do
  begin
    StringGrid1.ColWidths[i] := 33;
  end;
  StringGrid1.Rows[0].CommaText :=
    'わ,ら,や,ま,は,な,た,さ,か,あ';
  StringGrid1.Rows[1].CommaText :=
    ',り,,み,ひ,に,ち,し,き,い';
  StringGrid1.Rows[2].CommaText :=
    'を,る,ゆ,む,ふ,ぬ,つ,す,く,う';
  StringGrid1.Rows[3].CommaText :=
    ',れ,,め,へ,ね,て,せ,け,え';
  StringGrid1.Rows[4].CommaText :=
    'ん,ろ,よ,も,ほ,の,と,そ,こ,お';

  for i := 1 to 26 do begin
    Panels[i] := FindComponent('Pnl'+IntToStr(i)) as TPanel;
    Panels[i].Tag := i;
  end;
end;

//procedure TForm1.IroHenko2(P1, P2: integer);
//var
//  i : Integer;
//begin
//   if (P1 > 26) or (P1 < 1) then exit;  // ここを貼りつけ
//   if (P2 > 26) or (P2 < 1) then exit;
//
//  for i := 1 to 26 do
//  begin
//    Panels[i].color := clBtnFace;
//  end;
//    Panels[P1].Color := clYellow;
//    Panels[P2].Color := clYellow;
//end;

procedure TForm1.StringGrid1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  RC, CC: integer;
begin
  StringGrid1.MouseToCell(x, y, RC, CC);
  if (RC = -1) or (CC = -1) then Exit;
  Edit1.Text := StringGrid1.Cells[RC, CC];
  Case RC of
    9: begin
      Case CC of
      0: begin
        Edit2.Text := 'a';
        Edit3.Text := 'A';
        end;
      1: begin
        Edit2.Text := 'i';
        Edit3.Text := 'I';
        end;
      2: begin
        Edit2.Text := 'u';
        Edit3.Text := 'U';
        end;
      3: begin
        Edit2.Text := 'e';
        Edit3.Text := 'E';
        end;
      4: Begin
        Edit2.Text := 'o';
        Edit3.Text := 'O';
        end;
      end;
    end;
    8: begin
      Case CC of
      0: begin
//        IroHenko2(11, 1);  ←ここで関数を呼び出していました。
        Edit2.Text := 'ka';
        Edit3.Text := 'KA';
        end;

あとは,二つのCaseの繰り返しです。すごい力技ですね。
 TRC9 = record が便利そうなので,今から勉強を始めます。
以上大変長くなりましたが,私にはどこがいけないのかさっぱり
分かりません。
ご指導よろしくお願いします。


なんとん  2003-12-30 00:26:33  No: 6477

あれっ,間違えてる。

   { Public 宣言 }
    Panels: array[1..11] of TPanel;

array[1..26] なのに。
早速書き換えてみます。


なんとん  2003-12-30 00:43:54  No: 6478

Halbow さん,直りました。エラーなしです。

何と単純なミス。自分でもあきれています。
とりあえず直ったので,TRC9 = record の理解と
Panels の色を順に変えていく方法(「か」の場合「K」の
ボタン文字が青に0.4秒後位に「A]が青)に取り組みます。

でも,コードで気付いたことがあったらどしどし教えてください。
よろしく。


Halbow  2003-12-30 03:30:24  No: 6479

Halbow です。

解決しそうでなによりです。

> 関数名の前に TForm1.IroHenko1(P1: integer); と TForm1 を
> つけないと Panels を認識してくれませんでした。

えーと、TForm1 クラスのメソッドにすると、そのなかではデフォルトで TForm1
クラスのフィールド変数やメソッド、TForm1 自身のプロパティーやメソッドに
アクセスできます。self の意味が分かっていれば自明です。で、一般の関数や
手続きにすると、特定のインスタンスを参照しなければなりません。上の例なら

Form1.Panels[i].Color := clBtnFace; 

というふうにアクセスします。クラスや可視性などについて理解するといろいろ
と視野が広がります。


なんとん  2003-12-30 03:52:47  No: 6480

Halbow さん,何度も何度もお世話になります。

>self の意味
>クラスや可視性など

まだ全く分かっていません。でも,今回の質問で雰囲気はつかめました。
もっと勉強しなくっちゃ。

明日から,里帰りです。Halbowさんも良い年をお迎えください。
来年も,またよろしくお願いします。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加