Avatar billede tuctoh Nybegynder
20. april 2004 - 16:31 Der er 11 kommentarer og
1 løsning

Speciel string replace

Hej,

Jeg har et måske lidt underligt behov for en funktion, som kan gennemløbe en streng, og replace dele af denne med _
Dette skal kunne gøres i 3 steps; eksempler:

Input: Hestekød
Output #1: He______
Output #2: Heste___
Output #3: Hestekø_

Input: Hestekød og hundefodder
Output #1: He______ __ hun________
Output #2: Heste___ __ hundefo____
Output #3: Hestekø_ o_ hundefodde_

Dette skal bruges til en quiz af en art; jeg indtaster svaret, og den generer selv 3 hints hvor der ligeså stille bliver givet mere og mere af ordet. Hvert ord i strengen (svaret) skal behandles for sig; hvis der er for eksempel 4 bogstaver i ordet, skal der gives et nyt bogstav for hver hint - hvis der kun er 3 bogstaver gives intet i det første hint, og to i hver af de to næste.


Det skal ikke nødvendigvis sættes ind i en procedure eller funktion; hvis den bare gør noget ala
input := 'hej';
hint1 := '___';
hint2 := 'h__';
hint3 := 'he_';
burde jeg kunne implementere det i mit program... hvordan det nu engang "nemmest" kan programmeres.


Jeg håber i er friske på at hjælpe mig her, eller hvis det er for stor en mundfuld kan hjælpe mig på rette vej :)

På forhånd mange tak!!
~Casper
Avatar billede hrc Mester
20. april 2004 - 18:04 #1
Prøv denne klasse:

type
  TQuizWord = class
  private
    fQuizWord : string;
    fSnippets : integer;
    fLgd : integer;
    fTries : integer;
    fIndex : integer;
  public
    constructor Create(const aQuizWord : string);
    property QuizWord : string read fQuizWord;
    property Snippets : integer read fSnippets write fSnippets;
    function HitMe : string;
    function Match(const aQuizGuess : string) : boolean;
  end;

implementation

{ TQuizzWord }

// PreCon: The QuizWord cannot be shorter than three characters
constructor TQuizWord.Create(const aQuizWord: string);
begin
  inherited Create;
  fQuizWord := aQuizWord;
  fSnippets := 4; // By default
  fTries := 0;
  fIndex := 0;
  fLgd := length(fQuizWord);
end;

function TQuizWord.HitMe : string;
var
  i : integer;
begin
  if fTries = 4 then
    exit;

  inc(fTries);
  fIndex := (fLgd div fSnippets) * fTries;
  if fTries = 4 then
    result := fQuizWord
  else begin
    result := copy(fQuizWord,1,fIndex);
    for i := fIndex + 1 to fLgd do
      result := result + '_';
  end;
end;

function TQuizWord.Match(const aQuizGuess: string): boolean;
begin
  result := UpperCase(fQuizWord) = UpperCase(aQuizGuess);
end;

... Det kan godt være at du ikke er tilfreds med løsningen, men jeg HitMe funktionen er nok også det vigtigste.

procedure TfrmMain.btnCreateClick(Sender: TObject);
begin
  fQuiz := TQuizWord.Create(eQuizWord.Text); // Feed word to object
end;

procedure TfrmMain.btnTestClick(Sender: TObject);
var
  st : string;
begin
  st := fQuiz.HitMe; // Give up - give me one more snippet
  if st <> '' then
    mLog.Lines.Add(st); // Add to TMemo
end;

procedure TfrmMain.btnFreeClick(Sender: TObject);
begin
  fQuiz.Free;
end;

For at løse "Hestekød og hundefoder" problematikken må du udskille ordet i 3 TQuizWord instanser:

  := TQuizWord.Create('Hestekød');
  := TQuizWord.Create('og');
  := TQuizWord.Create('hundefoder');

Du kan eventuelt lave en nedarving af TQuizWord til en QuizSentence som håndterer dette. Lad høre om du behøver hjælp til dette.
Avatar billede tuctoh Nybegynder
21. april 2004 - 15:13 #2
grr, jeg er ved at være træt af experten!!! Den har det med ikke at sende indlægget, selv om jeg skriver :@

Anyway, hvad jeg prøvede at sige var at jeg nok er for meget Delphi noob til at kunne forstå dit forslag :s
Det er dog lykkedes mit til sidst at få lavet en funktion som kan tage ét ord, og sætte _ ind som det er nødvendigt.
Hvad jeg dog ikke kan finde ud af er at løbe igennem sætningen, og kalde funktionen for hvert ord der er i sætningen.

Er der nogen som kan hjælpe mig med en template om at splitte en streng op, og derefter løbe igennem den ord for ord (for at kunne kalde funktionen for hvert ord i sætningen)!?

MVH
~Casper Bang
Avatar billede stoney Nybegynder
21. april 2004 - 15:39 #3
procedure Tfrmprofil.Button2Click(Sender: TObject);
var
sl : Tstringlist;
i : integer;
begin
sl := Tstringlist.Create;
sl.Delimiter := ' ';
sl.DelimitedText := edit1.Text;
for i := 0 to sl.Count - 1 do
showmessage(sl[i]); // her skal du kalde din function/procedure
sl.Free;

end;

Stoney
Avatar billede tuctoh Nybegynder
21. april 2004 - 15:43 #4
Hej,

Den givder følgende fejl når jeg prøver at kompilere:

[Error] Unit2.pas(83): Undeclared identifier: 'Delimiter'
[Error] Unit2.pas(84): Undeclared identifier: 'DelimitedText'

Jeg bruger Delphi 4 - så de funktioner er måske ikke understøttede!?
Avatar billede hrc Mester
21. april 2004 - 17:19 #5
nu skal du ikke gøre dit mere noob (hvad det så end beytder - sikkert nybegynder) end rimeligt er. Jeg har lavet en meget minimal klasse der fylder total 50 linier (incl mellemrum og kommentarer), selve HitMe fylder 13 fra "begin" til "end". Måske har jeg ikke noob-kasketten på, men har du egentlig studeret løsningen eller overset, at klassen faktisk kun udgør halvdelen af mit svar?

Den sidste halvdel er tre-knapper der hhv. opretter objektet, snupper en bid hver gang den kaldes og frigiver den igen - bare for at splitte det op. I den private del af din form skal du definere "fQuiz : TQuizWord" og du er kørende. Foruden de tre knapper er der en TEdit: eQuizWord, en TMemo: mLog. Det er alt.

- undskyld hvis jeg lyder lidt vrissen, men jeg hader at et, så vidt jeg kan se, rigtig objektorienteret løsning blive kasseret for en noget mere primitiv - og jo, med den rigtige Delphi-version vil Stony's løsning fungere glimrende.

Det havde været rigtig let at lave en TQuizzSentence der kunne håndtere et vilkårligt antal ord. Skidt, så laver jeg den nu:

type
  TQuizzSentence = class(TObjectList)
  private
    fSentence : string;
  public
    constructor Create(const aSentence : string);
    function HitMeHard : string;
    property Sentence : string read fSentence;
  end;

implementation

constructor TQuizzSentence.Create(aSentence : string);
var
  i, p : integer;
begin
  inherited Create;
  fSentence := aSentence;
  while fSentence <> '' do begin
    p := pos(' ',fSentence);
    if p = 0 then begin
      Add(TQuizzWord.Create(fSentence));
      fSentence := ''; // Exit loop
    end
    else begin
      Add(TQuizzWord.Create(copy(fSentence,1,p-1));
      delete(fSentence,1,p);
    end;
  end;
end;

function TQuizzSentence.HitMeHard : string;
var
  i : integer;
begin
  result := '';
  for i := 0 to Count - 1 do
    result := result + TQuizzWord(Items[i]).HitMe + ' ';
  result := trim(result); // Remove final space char.
end;

Jeg har ikke testet det, men er rimelig sikker på at det fungerer (måske med få syntaksrettelser).
Avatar billede hrc Mester
21. april 2004 - 17:20 #6
En tanketorsk! Lige en rettelse til constructoren:

constructor TQuizzSentence.Create(aSentence : string);
var
  i, p : integer;
begin
  inherited Create;
  fSentence := aSentence;

  // Using aSentence as work string
  while aSentence <> '' do begin
    p := pos(' ',aSentence);
    if p = 0 then begin
      Add(TQuizzWord.Create(aSentence));
      aSentence := ''; // Exit loop
    end
    else begin
      Add(TQuizzWord.Create(copy(aSentence,1,p-1));
      delete(aSentence,1,p);
    end;
  end;
end;
Avatar billede tuctoh Nybegynder
21. april 2004 - 17:38 #7
Hej HRC,

Ja, noob står for nybegynder... og dét er jeg, i Delphi.
Jeg har arbejdet en del med klasser i C++, men har endnu ikke fattet syntaxten i Delphi - jeg har dybest set ingen som helst anelse om hvor det skal pastes ind i min kode, hvor klassen skal erklæres... for slet ikke at nævne hvad de forskellige brugte funktioner betyder.
Men jo, jeg har kigget på din kode, og prøvet at implementere den... den giver et ton forskellige kompileringsfejl - om det er fordi jeg kun bruger Delphi 4, eller om det er fordi jeg ikke har fattet hvor det hele skal pastes ind ved jeg ikke :(
Avatar billede tuctoh Nybegynder
21. april 2004 - 18:15 #8
Ok, jeg har nu fået løst mit problem mere eller mindre... ved hjælp af HRC's sidste kode eksempel fandt jeg ud af hvordan jeg kunne loope igennem mine ord.
Min funktion er ikke perfekt, men jeg tror at jeg kan tweake den til at levere den type hints jeg har brug for i mit system.

Selv om jeg ikke brugte din metode HRC, hjalp den mig en del på vej... fik nogle idéer og metoder som jeg kunne bruge... koden det har resulteret i er sikker ikke af en høj standard, men man skal vel kravle før man kan gå - også i Delphi.

Jeg har først en funktion, som tager et ord og sætte _ ind i den:


function Taddquestion.WordToString(streng : string; level : integer) : string;
var temp : string;
var i : integer;
var j : integer;
MaxToInsert : integer;
begin
    level := 4-level;
    MaxToInsert := length(streng);
    for i := 1 to level do
    begin
          for j := 1 to Round(MaxToInsert/4) do
          begin
              temp := temp + '_'
          end;
    end;
    Result := copy(streng, 1, Length(Streng) - Length(Temp)) + temp;
end;



derefter har jeg den del der løber ordene igennem, og opdaterer mine EDIT-felter:


procedure Taddquestion.answerKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
  var
i : integer;
p : integer;
temp : string;
begin
  if Key = 13 then
  begin
  temp := answer.text;
  while Length(temp) > 0 do
  begin
    p := pos(' ',temp);
    if p = 0 then p:= Length(temp)+1;
    ShowMessage(temp);
    Hint1.text := Hint1.text + WordToString(copy(temp,1,p-1), 1) + ' ';
    Hint2.text := Hint2.text + WordToString(copy(temp,1,p-1), 2) + ' ';
    Hint3.text := Hint3.text + WordToString(copy(temp,1,p-1), 3) + ' ';
    temp := trim(copy(temp,p,(Length(temp))));
  end;
  Hint1.text := trim(Hint1.text);
  Hint2.text := trim(Hint2.text);
  Hint3.text := trim(Hint3.text);

  end;
end;




Det er som sagt nok ikke verdens smukkeste løsning, så jeg er da altid villigt til at høre kritik... vil bare gerne holde det på et niveau jeg kan forstå :p

Men tak for hjælpen HRC. Du får alle points; du har lagt et godt stykke arbejde i det!
Avatar billede tuctoh Nybegynder
21. april 2004 - 18:18 #9
Linien med ShowMessage skal der ses bort fra... var en linie til debugging.
Avatar billede hrc Mester
21. april 2004 - 21:12 #10
Havde vist fået det forkerte ben ud af sengen i dag. Hæ, hæ - kan lige forestille mig C++ mandens totale forvirring: hvor er mine h-filer???

I Delphi er kode-synligheden styret af "interface" og "implementation" i de enkelte filer og definitionerne kan placeres efter hinanden. Rækkefølgen er naturligvis styret af hvordan de enkelte klasser bruges - men så har man heldigvis "forward declaration":

interface // Synlig del

type
  TQuizzCard = class;
  TQuizzWord = class;

  TfrmMain = class(TForm)
  private
  ...
  end;

  // Her kommer så klassen (u. semikolon efter "class") - og nej, man kan ikke
  // lave multipel nedarving som i C++ - men man kan vist alt andet.
  TQuizWord = class
  private
  public
  end;
 
implementation // Usynlig del

I klasserne er synligheden styret som de plejer med vha. private, public, protected og published.

Mht. din kode (ikke meget at udsætte på det, men der er dog en programmeringsfejl - idet du bruger flydende decimal division på et heltal):

function Taddquestion.WordToString(streng : string; level : integer) : string;
var
  temp : string;
  i, j : integer;
  MaxToInsert : integer;
begin
  level := 4 - level;
  MaxToInsert := length(streng);

  for i := 1 to level do begin
    // Her går det galt. Konverterer til flydende decimaltal og tilbage igen
    // for j := 1 to Round(MaxToInsert/4) do

    // Det hedder (tilsvarende C++ "for(int j = 1; j < (MaxToInsert % 4); j++)")
    for j := 1 to (MaxToInsert div 4) do
      temp := temp + '_';
  end;
  Result := copy(streng, 1, Length(Streng) - Length(Temp)) + temp;
end;

procedure Taddquestion.answerKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  i, p : integer;
  temp : string;
begin
  if Key = VK_RETURN then begin // Bedre at bruge konstanten
    // Answer må være en TEdit. Giv den et navn som teknisk er mere beskrivende
    // Jeg sætter altid initialerne foran navnet: TEdit: eAnswer,
    // TComboBox: cbAnswer - et problem med TCheckBox, som jeg ikke har en
    // løsning på...
    temp := answer.text;

    while Length(temp) > 0 do begin
      p := pos(' ',temp);
      if p = 0 then p:= Length(temp)+1;

      ShowMessage(temp);
      Hint1.text := Hint1.text + WordToString(copy(temp,1,p-1), 1) + ' ';
      Hint2.text := Hint2.text + WordToString(copy(temp,1,p-1), 2) + ' ';
      Hint3.text := Hint3.text + WordToString(copy(temp,1,p-1), 3) + ' ';

      temp := trim(copy(temp,p,(Length(temp))));
    end;
    Hint1.text := trim(Hint1.text);
    Hint2.text := trim(Hint2.text);
    Hint3.text := trim(Hint3.text);
  end;
end;

At jeg bruger "begin" på måden ovenfor, er et epørgsmål om religion. Jeg synes jo at indrykket fortæller alt om, at nu er der en subsektion - men der er andre der skal have den rykket nedenfor (og spilder derved en masse linier). Sådan kender du det sikkert fra C++

Tak for points.
Avatar billede tuctoh Nybegynder
21. april 2004 - 21:22 #11
Du skal have tak for gennemgangen af min kode :)
Undrede mig godt nok over hvad "div" stod for - men det er bare modulus kan jeg forstå på det hele. Det undrer mig dog godt nok lidt at jeg ikke kan lave en ganske simpel division på en integer... men OK det kan jeg nok vende mig til.
Angående en teknisk beskrivelse for variable... jeg vil prøve at vende mig til at smide "e" foran Edit felter etc... det er nok en god idé; har til tider haft problemer med at finde ud af om det er caption eller text eller noget helt tredie jeg skal skrive til - med den beskrivelse burde jeg kunne huske det.

Og til VK_RETURN - jeg spurgte på experten i går og fik  den metode som er brugt i mit eksempel fortalt. Men det er da klart nemmere at huske en konstants navn.

Jeg vil nok også begynde at kigge lidt mere på klasser. Havde fået fortalt at Delphi skulle være ret dårlig til det, men hvis det kun er multiple nedarvning der "mangler" går det jo nok også... det er trods alt de færreste systemer hvor man bruger det.


Mange tak for hjælpen! Det virker ret godt; giver nogle gode hints.
Avatar billede hrc Mester
22. april 2004 - 21:34 #12
Grunden til jeg vil have dig til at bruge mod og div med heltal er, at koden eller bliver ineffektiv. Det sker ved alle sprog, også Delphi og C++. Der sker det, at compileren konverterer heltallene til flydende decimaltal, foretager operationen og konverterer tilbage igen (måske, men kun måske er den klog nok til at identificere situationen og optimere det til en heltalsoperation, men det tror jeg ikke. Det er vist kun Gnu-compileren der er intelligent nok der). Vi snakker clockcycles, men selve operationen har nok en faktor 50+ til forskel. Hvis "i div 4" tager 4 cycle, vil "i / 4" tage noget i retningen af 200.

Det ærger mig at måtte indrømme det, men du fangede mig i endnu en fejl. Operatoren "%" er rigtignok modulus. Mente at det stod for divisor, men her kan C++ åbenbart bruge "/" uden hensyntagen til heltal eller FD's.

VK_RETURN er en del af de Windows-konstanter som er defineret af Microsoft. I Delphi er de defineret i ... lige præcis ... Windows-unitten hvor også en masse andre konstanter er defineret.

Nu er jeg alt andet end objektiv når det gælder C++ kontra Delphi og har ikke leget med C++ nok til at sige om det er bedre til OOP. Til gengæld har jeg sjældent haft problemer med objekterne i Delphi - skyldes måske held, men jeg tror nu også på konsekvent brug af gennemprøvede rutiner. Bør nok begynde at interessere mig lidt for interfaces, men har ikke haft noget at bruge dem til endnu. Måske COM'er jeg i gang snart.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester