2021-02-24, 19:10
  #1
Medlem
Hej. Sitter nu och tränar på rekursion på codingbat.com/
Dessvärre verkar det inte finnas lösningar att titta på. Klarar dock av att lösa problemen, men vet inte om min metod är den mest eleganta.

Som ett exempel till denna uppgiften:
https://codingbat.com/prob/p184029

Ser min lösning ut enligt följande:
Kod:
int iFound = 0;
public int countHi(String str) {
  
  int hiCount = 0;
  
  if(str.length() == 0){
    return 0;
  }
  
  
  if(str.substring(str.length()-1).equals("i")){
    iFound = 1;
  }
  
  else if(str.substring(str.length()-1).equals("h") && iFound == 1){
    hiCount = 1;
    iFound  = 0;
  }

  else{
    iFound = 0;  
  }
  
  return hiCount + countHi(str.substring(0, str.length()-1) );

}

Jag sparar alltså en markör för att se om det kommer ett "h" efter ett "i" (läser baklänges).
Därtill ett par if-satser för att filtrera resultatet till rätt output. Tänker att man hade kanske kunnat skicka in ett 2:a värde som argument till funktionen, men vill helst inte modifiera funktionen. Tror inte det är tänkt att man ska det heller.
Hur hade ni gjort?


Sedan blev jag lite konfunderad varför det översta uttrycket inte fungerar:
if( str.substring(str.length() - 1) == "x" ){ // fungerar inte. Jag jämför ju en sträng med en sträng, väl? Och operatorn "==" ska är väl definerad för String?
if( str.substring(str.length() - 1).equals("x") ){ //fungerar
if( str.charAt(str.length() - 1 ) == 'x' ){ // fungerar
Citera
2021-02-24, 19:34
  #2
Medlem
Rengardes avatar
Hej, operatorn == funkar inte för att jämföra strängar i java, du använder alltid equals() för det.
Citera
2021-02-24, 20:00
  #3
Medlem
poolos avatar
Som ovan skrev så jämför == referens (dvs samma sträng i minnet) i java och Equals jämför värdet.

Men angående din kod så bör du inte hårdkoda in data i koden. För imorgon kommer din chef och då ska din funktion helt plötsligt räkna "hello" i en sträng.

Skulle jag vart du skulle jag lagt en variabel ovanför typ String word = "hi".

Använda mig av indexOf funktionen för att hitta första index som ordet inträffat. Därefter substring för att ta bort längden av ordet och anropa metoden igen med det som kvarstår av strängen

Return 0 om strängen är tom eller indexOf inte hittar strängen, annars returnar du 1 + countHi(...)

Ta bort din iFound variabel.
Citera
2021-02-27, 15:03
  #4
Medlem
Citat:
Ursprungligen postat av Rengarde
Hej, operatorn == funkar inte för att jämföra strängar i java, du använder alltid equals() för det.
Tack! Det borde jag lärt mig!

Citat:
Ursprungligen postat av poolo
Som ovan skrev så jämför == referens (dvs samma sträng i minnet) i java och Equals jämför värdet.

Men angående din kod så bör du inte hårdkoda in data i koden. För imorgon kommer din chef och då ska din funktion helt plötsligt räkna "hello" i en sträng.

Skulle jag vart du skulle jag lagt en variabel ovanför typ String word = "hi".

Använda mig av indexOf funktionen för att hitta första index som ordet inträffat. Därefter substring för att ta bort längden av ordet och anropa metoden igen med det som kvarstår av strängen

Return 0 om strängen är tom eller indexOf inte hittar strängen, annars returnar du 1 + countHi(...)

Ta bort din iFound variabel.
Tack! mycket bra information. Men detta är endast enkla korta kodövningar, men naturligtvis bra att göra mer generella lösningar. Tack!
Citera
2021-03-26, 23:22
  #5
Medlem
Citat:
Ursprungligen postat av dagsvag
Hej. Sitter nu och tränar på rekursion på codingbat.com/
Dessvärre verkar det inte finnas lösningar att titta på. Klarar dock av att lösa problemen, men vet inte om min metod är den mest eleganta.

Som ett exempel till denna uppgiften:
https://codingbat.com/prob/p184029

Ser min lösning ut enligt följande:
Kod:
int iFound = 0;
public int countHi(String str) {
  
  int hiCount = 0;
  
  if(str.length() == 0){
    return 0;
  }
  
  
  if(str.substring(str.length()-1).equals("i")){
    iFound = 1;
  }
  
  else if(str.substring(str.length()-1).equals("h") && iFound == 1){
    hiCount = 1;
    iFound  = 0;
  }

  else{
    iFound = 0;  
  }
  
  return hiCount + countHi(str.substring(0, str.length()-1) );

}

Jag sparar alltså en markör för att se om det kommer ett "h" efter ett "i" (läser baklänges).
Därtill ett par if-satser för att filtrera resultatet till rätt output. Tänker att man hade kanske kunnat skicka in ett 2:a värde som argument till funktionen, men vill helst inte modifiera funktionen. Tror inte det är tänkt att man ska det heller.
Hur hade ni gjort?


Sedan blev jag lite konfunderad varför det översta uttrycket inte fungerar:
if( str.substring(str.length() - 1) == "x" ){ // fungerar inte. Jag jämför ju en sträng med en sträng, väl? Och operatorn "==" ska är väl definerad för String?
if( str.substring(str.length() - 1).equals("x") ){ //fungerar
if( str.charAt(str.length() - 1 ) == 'x' ){ // fungerar

Det är väl smak och behag hur man löser det, folk tänker ofta olika.
Jag är ett stort fan av läsbarhet så jag hadde löst det såhär då jag tycker det blir lite mera läsbart.

Kod:
int count 0;
public 
int countHi(String str) {
  
  
// base case that ends the recursion
  // otherwise it will run forever
  
if (str.length() < 2){
    
int result count;
    
count 0// let's reset the count for the next testcase
    
return result;
  }
  
  
String firstTwoChars str.substring(0,2);
  if (
firstTwoChars.equals("hi")){
    
count++;
    return 
countHi(str.substring(2)); // first two chars in remaining string is hi, so return the string without first two chars
  
}
  
  return 
countHi(str.substring(1)); // first two chars were not hi, so return string and remove on char from beginning

Citera
2021-04-19, 18:09
  #6
Medlem
kjellbrels avatar
Citat:
Ursprungligen postat av dagsvag
Tack! mycket bra information.
Lite sen på bollen (igen), men ville bara påtala att du har fått svar av lite blandad kvalitet.

Precis som poolo skriver tycker jag att du bör använda dig av indexOf för att lösa detta. Använd inte instansvariabler av något slag utanför metoden för att lösa detta. Inte som i föreslaget med att göra metoden generell (utan lägg i så fall till en parameter till metoden) och absolut inte en för att räkna antal med. Det sistnämnda bryter mot hela den rekursiva tanken. Det vore lika illa som att lägga en intern loop-variabel i någon metod som en instansvariabel istället. Precis som du i en loop eller annat slutet scope använder lokala variabler, som enbart existerar medans du utför beräkningen, så skall du under rekursion använda anropsstacken och returvärden för samma syfte. Alla variabler som är synliga utanför sitt tänkta syfte riskerar att bli nyttjade felaktigt och dessutom blir inte din metod trådsäker.

Nu kanske du har lämnat detta bakom dig, men ifall andra läser detta framöver, så är här lite kompletterande tankar runt rekursion som kanske är av intresse.

En gammal bra beskrivning för att lära sig förstå den rekursiva grundtanken tycker jag denna är (minns inte källan längre):

Att utföra en rekursiv operation på en samling element innebär att först utföra operationen på det första elementet och sen samma sak på resten, dvs alla element utom det första.

Komplettera detta med ett avbrottsvillkor, dvs hantera fallet när samlingen består av noll element, så är lösningen komplett.

Applicera nu denna tanke på ett minimalt konkret exempel, som att summera en samling heltal rekursivt. Ovanstående säger ju då att summan av alla element är första värdet i samlingen plus summan av alla återstående element. Avbrottsvillkoret blir att returnera 0 för en tom samling. Kodexempel (där ix är startposition för summering och som borde kompletteras med en överlagrad sum(int[]) som anropar denna med 0 som arg för ix, men skippat för att hålla exemplet litet):
Kod:
public static int sum(int[] array, int ix) {
    return ix >= array.length ? 0 : array[ix] + sum(array, ix + 1);
}
Denna följer den rekursiva tanken ovan: Om samlingen är tom (ix >= array.length) returnera 0 annars returnera första elemetet (array[ix]) + summan av resten (sum(array, ix + 1)).

Om man nu använder sig av indexOf för problemet i denna tråd så blir motsvarande rekursiva tanke denna: Hitta första förekomst av strängen "hi" och summera denna träff med antalet övriga träffar i återstående sträng (dvs resterande sträng efter första träff). Avbrottsvillkor blir att returnera 0 om vi inte hittar någon träff alls. Med mallen ovan för lösningen av sum() så kan man konstruera en rätt likt exempel (samt generalisera den, anropa med "hi" som andra argument för övningen i denna tråd):
Kod:
public static int count(String s, String match) {
    int ix = s.indexOf(match);
    return ix == -1 ? 0 : 1 + count(s.substring(ix + match.length()), match);
}

Eller som någon föreslog att det borde formuleras i ordböckerna:
Kod:
rekursion:
  se rekursion
Det dolda avbrottsvillkoret här är helt enkelt att läsaren till sist fattar.
Citera
2021-05-09, 12:40
  #7
Medlem
Citat:
Ursprungligen postat av kjellbrel
Lite sen på bollen (igen), men ville bara påtala att du har fått svar av lite blandad kvalitet.
Tusen tack! Ledsen för sent svar. Nä, jag insåg att jag tänkte helt fel från början. Tränade på hur man löser det på riktigt och efter ett tag gick det bättre. Fick godkänt på tentan.👍
Citera

Skapa ett konto eller logga in för att kommentera

Du måste vara medlem för att kunna kommentera

Skapa ett konto

Det är enkelt att registrera ett nytt konto

Bli medlem

Logga in

Har du redan ett konto? Logga in här

Logga in