Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Java: Merkkijonojen laskeminen listasta

Markuster [14.10.2019 19:02:51]

#

Tämmönen ongelma olis... oon miettiny pään puhki mun en keksi vastausta... päästäkää joku mut pulasta...

eli tehtävän anto:
public static void toistuvatMerkkijonot(ArrayList<String> merkkijonot, int kertaa) - metodin kutsuminen tulostaa ne merkkijonot, jotka toistuvat parametrina annetun kertaa-muuttujan arvon verran parametrina annetussa listassa merkkijonot. Mikäli merkkijono toistuu useamman kerran tai alle annetun kertamäärän, ei merkkijonoa tule tulostaa. Metodi ei muokkaa parametrina annettavaa listaa.

Ja seuraavalla testipätkällä ja ohjelman yrityksella oon koittanut ratkaista:

public static void main(String[] args) {
        ArrayList<String> mjonot = new ArrayList<>();
        mjonot.add("hei");
        mjonot.add("hei");
        mjonot.add("taas");
        mjonot.add("maailma");
        mjonot.add("maailma");
        toistuvatMerkkijonot(mjonot, 2);
        System.out.println();
    }
    private static void toistuvatMerkkijonot(ArrayList<String> mjonot, int kertaa) {
        int indeksi = 0;
        while (indeksi < mjonot.size()) {
            String sana = mjonot.get(indeksi);
            indeksi++;
            int toistuvamaara = kertaa;
            if (mjonot.contains(sana) && toistuvamaara == kertaa) {
               System.out.print(sana);
            }
        }
    }

(Mod. huom: koeta muotoilla viestit järkevästi, ja merkitse koodit ohjeen mukaan.)

Metabolix [14.10.2019 19:17:52]

#

Mieti nyt hetki, mitä tuo koodisi tekee. Koodissa käyt listan sanoja läpi, ja jokaisen sanan kohdalla tarkastat vain ja ainoastaan sen, että kyseinen sana löytyy listasta (varmasti löytyy, kun juuri hait sen listasta) ja että muuttujassa toistuvamaara on sama luku kuin muuttujassa kertaa (varmasti on, kun edellisellä rivillä sijoitat juuri sen arvon).

Missään kohti koodia et yritä laskea listassa olevia sanoja, vaikka juuri sanojen laskeminen on tehtävän idea.

Jotta saisit tehtävän ratkaistua, sinun pitäisi käydä läpi listan sanat ja jokaisen sanan kohdalla laskea kyseisen sanan lukumäärä listassa. Jotta samaa sanaa ei tulisi tulostettua moneen kertaan, täytyy myös jotenkin pitää kirjaa (tai silmukassa tunnistaa), onko kyseinen sana selvitetty jo aikaisemmin.

Koodin rakenne olisi siis tällainen:

käy läpi sanat (s):

  jos s on jo käsitelty:
    ohita tämä sana

  käy läpi sanat (s2):
    jos s on s2:
      laske mukaan lukumäärään

  jos lukumäärä on pyydetty:
    tulosta

The Alchemist [15.10.2019 04:58:13]

#

Yllä annettu pseudokoodi on kyllä aika sekava. Muuttujaa "lukumäärä" ei alusteta missään.

Viimeisessä ehtolauseessa, jossa vertaillaan muuttujia "lukumäärä" ja "pyydetty", on oikeastaan bugi, sillä ehdon pitäisi kuulua "jos lukumäärä on pyydetty TAI SUUREMPI".

Ensimmäisessä ehtolauseessa olkia kohauttamalla vain todetaan, että tarkista, onko sana jo "käsitelty". Eli mitä? Mitä se tarkoittaa? Sanoisin, että tämä kohta on nyypälle jopa vaikeampi ymmärtää kuin alkuperäinen tehtävänanto.

Kyseisestä kohdasta myös näkee sen, että koska käsitellyt sanat on kerättävä johonkin talteen joka tapauksessa, niin koodia ei välttämättä kannata lähteä tekemään niin, että iteroidaan syötteenä annettua listaa kerta toisensa perään, vaan voi olla parempi turvautua toiseen tietorakenteeseen, johon kerätään löydettyjen sanojen lukumäärät talteen.

Javassa tällainen luokka olisi HashMap<String, Integer>, josta opettaja vaan tuskin on kertonut mitään.

Alla on auki purettu versio Metabolixin ideasta, mielestäni tämä on selkeämpi ja myös toimii oikein. Se on kirjoitettu javascriptillä, koska en osaa javaa enää riittävän hyvin, ja toisaalta js on hyvä korvike itse keksityille pseudokielille, joita kukaan muu ei osaa lukea.

// Sanan täytyy löytyä näin monta kertaa.
let threshold = 4

for (let i = 0; i < allWords.length; i++) {
  let word = allWords[i]
  let count = 0

  for (let j = 0; j < allWords.length; j++) {
    /**
     * HUOM! Java-kielessä merkkijonoja ei voi vertailla näin.
     * Käytä metodia String.equals()
     */
    if (allWords[j] == word) {

      // Jos haettava sana löytyy indeksiä "i" aiemmin, se on jo käsitelty, skip.
      if (j < i) {
        break
      }

      count++

      // Sana on löydetty riittävän monta kertaa: tulosta ja siirry seuraavaan sanaan.
      if (count == threshold) {
        console.log("WORD MATCHES: " + word)

        // Poistutaan sisemmästä silmukasta, koska sanaa ei tarvitse enää etsiä.
        break
      }
    }
  }
}

Tämä versio koodista iteroi pelkästään syötteenä annettua taulukkoa eikä tarvitse rinnalle toista tietorakennetta. Ideana on se, että ulommassa silmukassa rullataan kaikki sanat läpi ja sisemmässä lasketaan yksittäisen sanan lukumäärää.

Metabolixin esittelemä ongelma "tarkista onko sana jo käsitelty" on ratkaistu siten, että myös sisemmässä silmukassa iteroidaan koko taulukko läpi. Mikäli käsiteltävä sana löytyy indeksistä, joka on pienempi kuin "i", niin sana on silloin jo käsitelty ja voimme hypätä suoraan seuraavaan sanaan.

Markuster [15.10.2019 17:35:09]

#

Joo... kyl mä tosta javascriptistä selvää sain... toimii muuten mut toi ei ota huomioon tätä ehtoa:

Mikäli merkkijono toistuu USEAMMAN kerran tai ALLE annetun kertamäärän, ei merkkijonoa tule tulostaa.

Eli alla olevalla syötteellä pitäisi tulostua vain "hei", mutta tulostuu "hei", "maailma".

Maailmaa ei pitäisi tulostua koska se esiintyy 3 kertaa ja pyyntö oli 2 kertaa.

public static void main(String[] args) {
        ArrayList<String> mjonot = new ArrayList<>();
        mjonot.add("hei");
        mjonot.add("hei");
        mjonot.add("taas");
        mjonot.add("maailma");
        mjonot.add("maailma");
        mjonot.add("maailma");
        toistuvatMerkkijonot(mjonot, 2);
        System.out.println();
    }

Milläs se tosta rajaantuu pois???

Metabolix [15.10.2019 17:39:57]

#

The Alchemist kirjoitti:

Yllä annettu pseudokoodi on kyllä aika sekava. Muuttujaa "lukumäärä" ei alusteta missään.

Tämä algoritmin kuvaus oli kylläkin ihmisen luettavaksi suunniteltu eikä pyrkinyt esittämään mitään ohjelmointikieltä. Sisennyksen tarkoitus oli havainnollistaa rakenteiden sisäkkäisyyttä, joka on usein vaikeampi hahmottaa yhdelle riville kirjoitetusta kuvauksesta.

Yleensä ihminen ymmärtää implisiittisesti, missä kohti muuttuja nollautuu. Jos vaikka neuvoisit lapselle, että laske ensin siniset pallot ja sitten punaiset pallot, niin neuvoisitko lapselle, että ”muista nollata laskuri siinä välissä”? Luultavasti lapsi ymmärtää laskea ne eriväriset pallot erikseen, jos ei ole autistinen.

The Alchemist kirjoitti:

Viimeisessä ehtolauseessa, jossa vertaillaan muuttujia "lukumäärä" ja "pyydetty", on oikeastaan bugi, sillä ehdon pitäisi kuulua "jos lukumäärä on pyydetty TAI SUUREMPI".

Tehtävänannossa lukee nimenomaan, että tulostetaan vain tasan pyydetyn verran esiintyvät, ei useammin esiintyviä.

The Alchemist kirjoitti:

Ensimmäisessä ehtolauseessa olkia kohauttamalla vain todetaan, että tarkista, onko sana jo "käsitelty". Eli mitä? Mitä se tarkoittaa? Sanoisin, että tämä kohta on nyypälle jopa vaikeampi ymmärtää kuin alkuperäinen tehtävänanto.

Mikä siinä on vaikea ymmärtää? Aloittajalla on jo käytettävissään ArrayList ja silmukka, ja näillä voi melko suoraviivaisesti tehdä kyseisen tarkastuksen.

Voi myös miettiä, onko opettavaisempaa opetella kääntämään JS-koodia Java-koodiksi vai onko parempi saada sanallisia ohjeita, mistä paloista ratkaisu voisi koostua.

Markuster kirjoitti:

Maailmaa ei pitäisi tulostua koska se esiintyy 3 kertaa ja pyyntö oli 2 kertaa.

Tulostus ja siihen liittyvä if-lause täytyisi sijoittaa vasta sisemmän for-silmukan jälkeen ja ilman break-riviä. Silloin tuloksena onkin algoritmi, joka on sama kuin minun kuvaamani, paitsi käsitellyn sanan tunnistaminen tapahtuu samassa silmukassa kuin sanojen laskeminen.

Muistahan edelleen käyttää kooditageja koodin ympärillä (tai ylipäänsä turha on laittaa viestiin koodia, joka ei varsinaisesti selvennä kysymystä).

Markuster [15.10.2019 18:14:26]

#

Okei... näillä ohjeilla sain toimii...
Kiitoksia kaikille...
Ja sori, tagit unohtu...

Täs nyt viel pätkä jolla sain toimii...
Jos jotain kiinnostaa...

 private static void toistuvatMerkkijonot(ArrayList<String> mjonot, int kertaa) {
        for (int i = 0; i < mjonot.size(); i++) {
            String sana = mjonot.get(i);
            int maara = 0;
            for (int j = 0; j < mjonot.size(); j++) {
                String sana2 = mjonot.get(j);
                if (sana.equals(sana2)) {
                    if (j < i) {
                        break;
                    }
                    maara++;
                }
            }
            if (maara == kertaa) {
                System.out.println(sana);
            }
        }
    }
}

The Alchemist [16.10.2019 02:40:47]

#

Joo, olin tosiaan saanut väärän käsityksen tehtävänannosta eli omassa koodissani oli se bugi. Enivei. Omasta kokemuksesta tiedän, että joskus on tarpeen myös nähdä suora vastaus pelkkien piilovihjeiden sijaan, koska jos jotain ei tajua, niin sitä ei vaan tajua. On turhaa kuluttaa tunteja jokaisen mitättömän ongelman ratkaisemiseen.

Omaan kouluaikaani luennoitsijat olivat usein huonoja opettajia. Tunneilla annetut esimerkit eivät olleet riittävän hyödyllisiä kotitehtävien ratkaisemiseen ja minulla oli usein vaikeuksia nähdä, miten ne edes liittyivät toisiinsa. Ei ohjelmoinnissa mutta joissakin toisissa aiheissa, koska ohjelmointi oli minulle aina helppoa.

Tuossa on nyt yksi selkeä koodinpätkä, jossa on sen verran asioita esillä, että niitä pitää jatkossa osata soveltaa itsekin. Sen ainakin tietää, että jos AP on täällä taas ensi viikolla kysymässä ratkaisua samanlaiseen ongelmaan, niin sitten on ihan turha vastata enää mitään.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta