20. april 2004 - 16:31Der 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:
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 :)
// 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:
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)!?
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;
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).
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;
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 :(
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!
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;
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++
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.
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.
Synes godt om
Ny brugerNybegynder
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.