Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: Suuremman numeron haku arraysta

Sivun loppuun

pistemies [14.06.2011 11:12:38]

#

Pikku selvitys enste

 $arr = array(0,4,8,12,16,20,24,28,32,36,40);

 $tulos = 25;
 #tässä pitäisi löytää arraysta eka-arvo joka on suurempi kuin $tulos eli arvo 28

Miten tuollainen etsintä tapahtuisi näppärimmin?

Teuro [14.06.2011 11:25:55]

#

Tässä oletetaan taulukon olevan aina järjestyksessä. Suuremmille taulukoille voisi harkita jotakin fiksumpaa hakua. Esimerkiksi puolitushakua vaikka pari kertaa, jolloin hakujoukko olisi hiukan pienempi. Tosin tämäkin taitaa olla ihan tarpeeksi nopea muutaman sadan alkion taulukoille?

<?php
$arr = array(0,4,8,12,16,20,24,28,32,36,40);

$tulos = 25;
#tässä pitäisi löytää arraysta eka-arvo joka on suurempi kuin $tulos eli arvo 28

for ($i = 0; $i < count($arr); $i++) {
	if ($arr[$i] > $tulos) {
		echo $arr[$i];
		break;
	}
}
?>

pistemies [14.06.2011 12:37:27]

#

Kiitos.
Ei tässä varmaa kauheita määriä kannata tauluun laittaa.
Ja tuo haku tarvitsee tehdä yhden ainoan kerran...

Chiman [14.06.2011 12:59:36]

#

Toimiva vastaus tuli jo yllä, mutta suosittelen käyttämään selkeämpää foreachia vanhanaikaisen forin tilalla:

<?php
$arr = array(0,4,8,12,16,20,24,28,32,36,40);
$tulos = 25;

foreach ($arr as $arvo) {
	if ($arvo > $tulos) {
		echo $arvo;
		break;
	}
}
?>

Triton [14.06.2011 14:55:22]

#

Tässä on oma versiona. Tämä funktio mahdollistaa vielä sen, ettei taulukon alkioiden tarvitse olla edes järjestyksessä, tosin jos alkiot ovat jokatapauksessa järjestyksessä, niin silloin on tehokkaampaa käyttää jotain noista ylläolevista.

function lahin_arvo( $etsittava, $taulukko ) {

	foreach ( $taulukko as $alkio ) {
		if ( $alkio > $etsittava && ( $alkio < $suurempi || is_null( $suurempi ) ) ) {
			$suurempi = $alkio;
		}
	}

	return $suurempi;

}

Chiman kirjoitti:

suosittelen käyttämään selkeämpää foreachia vanhanaikaisen forin tilalla

En kutsuisi for-rakennetta mitenkään vanhanaikaiseksi, sillä foreachin käyttäminen rajottuu vain tiettyihin erikoistapauksiin (taulukon läpikäyminen), tosin juuri tähän ongelmaan se käy mainiosti ratkaisuksi.

Chiman [14.06.2011 16:31:49]

#

Triton kirjoitti:

En kutsuisi for-rakennetta mitenkään vanhanaikaiseksi, sillä foreachin käyttäminen rajottuu vain tiettyihin erikoistapauksiin (taulukon läpikäyminen), tosin juuri tähän ongelmaan se käy mainiosti ratkaisuksi.

Ilmaisin mielipiteeni epäselvästi. Tarkoitin vanhanaikaisuudella for-silmukan käyttöä juuri taulukon läpikäynnissä. Jos halutaan käydä läpi taulukon alkiot, selkein keino siihen (mm. PHP:ssä) on foreach. Jos kohteena on sarja kokonaislukuja, for on hyvä vaihtoehto.

Koodissasi vaihtaisin iffin kaksi viimeistä ehtoa keskenään. Ensin null-tarkistus, sitten suuruus.

tsuriga [14.06.2011 18:22:25]

#

Chiman kirjoitti:

vaihtaisin iffin kaksi viimeistä ehtoa keskenään. Ensin null-tarkistus, sitten suuruus.

Onks tolla jotain merkitystä? Mieluummin alustaa ton muuttujan funkkarin alussa ihan jo selkeyden takia, ja alustaa sen vaikka taulukon ekaan arvoon niin koko null-tarkistuksen voi jättää pois.

EDIT: Nii ja nykyäänhä voi käyttää kivoja lambda-funkkareita,

<?php

$greater = function($min) use($arr) {
    foreach ($arr as $v) {
        if ($v > $min) {
            return $v;
        }
    }
    return false;
};

?>

The Alchemist [14.06.2011 19:11:51]

#

No onhan sillä väliä, että yrittääkö käyttää olematonta / alustamatonta muuttujaa vai ei. Joissain toisissa ohjelmointikielissä se johtaisi koko ohjelman kaatumiseen, ja parhaassakin tapauksessa lähdekoodi ei kääntyisi. PHP:ssä se aiheuttaa notification-tason ilmoituksen ("Undefined variable"). Huonoa ohjelmointia.

For-silmukan stoppiehtona ei kannata käyttää count-funktiota vaan ottaa kyseinen arvo talteen muuttujaan, koska ainakaan oma PHP-tulkkini ei osaa kyseisen funktion arvoa cachettaa vaan taulukon koko lasketaan joka kierroksella uudestaan. Tuloksena siis melkolailla hitaampi suoritus.

Simppelin silmukkatestin mukaan for olisi kolmisen kertaa foreachia hitaampi. Sinällään ihan ymmärrettävää, kun foreach on helpompi optimoida sisäisesti.

tsuriga [14.06.2011 19:23:12]

#

The Alchemist kirjoitti:

No onhan sillä väliä, että yrittääkö käyttää olematonta / alustamatonta muuttujaa vai ei.

Ei noiden ehtojen järjestely siihen mitään auta, vaa se alustaminen, niin kuin jo suosittelinkin.

The Alchemist [14.06.2011 19:29:37]

#

PHP:ssä on pari funktiontapaista muuttujan olemassaolon tarkasteluun: isset ja empty. Is_null ei itse asiassa kelpaa, koska se vaatii olevaisen muuttujan.

tsuriga [14.06.2011 19:37:10]

#

Luepa nyt, mitä Chiman ehdotti

Chiman kirjoitti:

vaihtaisin iffin kaksi viimeistä ehtoa keskenään

<?php
if ($alkio > $etsittava && (is_null($suurempi) || $alkio < $suurempi))
?>

Ihan saman E_NOTICEn tuo heittää.

Totta kai on validi ratkaisu tutkia, että onko sitä muuttujaa olemassa, mutta järkevämpihän se on alustaa ettei joka kierroksella tarvitse tutkia.

pistemies [14.06.2011 19:47:25]

#

Joo... monissa tilanteissa on hyvä tietää, tarvitseeko edes etsiä jotakin asiaa jostakin...jos sitä ei siellä ole, miksi ihmeessä etsiä ollenkaan..? :)
No, tähän minun hommaan soveltuu tuo ekahomma (chiman) hyvin. Lisäsin vaan vielä tarkistuksen, että jos $arvo on sama kuin $tulos, niin silloinkin tehdään jotakin... lisätään $arvon lukua neljällä.
Kyseessä on simppeli taulukko, jossa kullakin rivillä on neljä solua, arrayn jokainen arvo on taulukossa olevan kunkin rivin ensimmäinen solu. Koska tässä saattaa pikkusoluja tulostua jopa sata ja niiden tarkka määrä on etukäteeen arvaamaton, niin siksi tällainen viritelmä.
Kiitoksia vielä!

Chiman [14.06.2011 20:23:42]

#

Hyvä pointti tuo noticen lentäminen is_nullista. Huolimattomuusvirhe minulta.

tsuriga kirjoitti:

Chiman kirjoitti:

vaihtaisin iffin kaksi viimeistä ehtoa keskenään. Ensin null-tarkistus, sitten suuruus.

Onks tolla jotain merkitystä? Mieluummin alustaa ton muuttujan funkkarin alussa ihan jo selkeyden takia, ja alustaa sen vaikka taulukon ekaan arvoon niin koko null-tarkistuksen voi jättää pois.

Tritonin koodin logiikka ei toimi, jos muuttuja alustetaan taulukon ekaan arvoon. Kyseinen koodi siis etsii koko taulukosta suuruudeltaan argumenttia seuraavan arvon, vaikka taulukon sisältö olisi järjestämätön. Alustaminen NULLiksi on kuitenkin paikallaan.

tsuriga [14.06.2011 22:40:58]

#

Aini :), unehtu hieman funkkarin käyttötarkoitus. Tonin koodin tapauksessa päästään ylimääräisestä tarkistuksesta silmukassa alustamalla $suurempi arvoon PHP_INT_MAX. Funktion lopussa pitää sitten miettiä, että verrataanko suurinta taas tuohon maksimiarvoon, vai käytetäänkö apumuuttujaa. Jälkimmäinen ehkä se semanttisempi vaihtoehto (TM). Jos halutaan toimia samoin kuin useimmat PHP:n omat funktiot (sanotaan nyt varauksella PHP:n ollessa kyseessä), voidaan etsinnän ollessa tulokseton palauttaa false.

Triton [14.06.2011 23:56:11]

#

Joo, no tuo funktio nyt oli aika lennossa tehty ja koska en jaksanut sen enempää miettiä, että miten alusta tuon $suurempi-muuttujan, päätin hoitaa sen tuossa if-lauseessa.

The Alchemist [15.06.2011 08:06:18]

#

tsuriga kirjoitti:

Aini :), unehtu hieman funkkarin käyttötarkoitus. Tonin koodin tapauksessa päästään ylimääräisestä tarkistuksesta silmukassa alustamalla $suurempi arvoon PHP_INT_MAX. Funktion lopussa pitää sitten miettiä, että verrataanko suurinta taas tuohon maksimiarvoon, vai käytetäänkö apumuuttujaa. Jälkimmäinen ehkä se semanttisempi vaihtoehto (TM). Jos halutaan toimia samoin kuin useimmat PHP:n omat funktiot (sanotaan nyt varauksella PHP:n ollessa kyseessä), voidaan etsinnän ollessa tulokseton palauttaa false.

Tarkoitus oli hakea taulukosta pienin annettua lukua suurempi luku. Loogisesti siis $suurempi kannattaisi alustaa vaikka arvoon $luku-1, koska se on automaattisesti mahdoton vastaus. Lopuksi tarkistaa, että onko $suurempi oikeasti suurempi, ja palauttaa sen mukaan.

tsuriga [15.06.2011 11:19:33]

#

Silloin alkiot ovat aina suurempia kuin kuin suurin, eikä suurinta koskaan uudelleenaseteta. Tai sitten joudutaan lisäämään taas jotain uutta sinne silmukan ehtolausekkeisiin.

Metabolix [15.06.2011 12:46:39]

#

Pekka Mansikka kirjoitti:

Kyseessä on simppeli taulukko, jossa kullakin rivillä on neljä solua, arrayn jokainen arvo on taulukossa olevan kunkin rivin ensimmäinen solu.

Siis onko taulukossa aina neljän kertotaulu? Miksi sitten kikkailet taulukon kanssa, kun kaiken voi helposti laskeakin? Luultavasti käytät suunnilleen seuraavia tietoja:

// Solun numero rivin alkukohdaksi:
// 0..3 => 0,  4..7 => 4,  8..11 => 8
function rivin_alku($solu, $pituus = 4) {
  return $solu - $solu % $pituus;
}
// Solujen määrä rivien määräksi:
// 0 => 0,  1..4 => 1,  5..8 => 2,  9..12 => 3
function riveja($soluja, $pituus = 4) {
  return intval(($soluja + $pituus - 1) / $pituus);
  // return rivin_alku($soluja - 1, $pituus) / $pituus + 1;
}

The Alchemist [15.06.2011 13:05:26]

#

tsuriga kirjoitti:

Silloin alkiot ovat aina suurempia kuin kuin suurin, eikä suurinta koskaan uudelleenaseteta. Tai sitten joudutaan lisäämään taas jotain uutta sinne silmukan ehtolausekkeisiin.

En ymmärrä yhtään, mitä yrität sanoa, mutta ratkaisuni on toimiva.

Metabolix [15.06.2011 13:09:03]

#

The Alchemist, jos $raja = 5 ja $suurempi = 4 ja $taulukko = array(6, 7), mitä silmukassa tapahtuu? Tähän mennessä ehdotetulla silmukalla tulokseksi tulisi 4, vaikkei sitä edes ole taulukossa.

The Alchemist [15.06.2011 13:28:06]

#

Eihän tule. Ei ole mitään järkeä palauttaa kokonaislukua vastaukseksi, jos vastausta ei ole. Esimerkiksi se ehdotettu false tai sitten null kelpaa hyvin. $suurempi-muuttujaa ei voi alustaa kummaksikaan noista, jos haluaa tehdä asiat oikein ja lisäksi välttää turhia tarkisteluita, koska vertailu nullin kanssa aiheuttaa noticen ja false taas evaluoituu nollaksi.

The Alchemist kirjoitti:

Lopuksi tarkistaa, että onko $suurempi oikeasti suurempi, ja palauttaa sen mukaan.

Metabolix [15.06.2011 13:30:10]

#

Miten niin vastausta ei ole? Piti hakea pienin rajaa suurempi luku, joka tuossa tapauksessa on aivan yksiselitteisesti 6. Ehkäpä menet nyt lukemaan sen alkuperäisen tehtävän uudestaan.

Muotoillaan kysymykseni selvemmin:

function haku($raja = 4, $taulukko = array(6, 7)) {
  // The Alchemistin ehdottama alustus:
  $suurempi = $raja - 1;

  // Tutkitaan taulukko.
  foreach ($taulukko as $x) {
    // KYSYMYS: Mitä tänne? if ($raja < $x && $x < $suurempi) ei sovi!
  }

  // Palautetaan tulos. Tästä ei ollut mitään väittelyä.
  if ($suurempi > $raja) return $suurempi;
  if ($suurempi == $raja) return $raja + 4;
  return false;
}

The Alchemist [15.06.2011 15:41:05]

#

Mikä siinä nyt on niin vaikeaa?

$raja < $x && ($suurempi < $raja || $x < $suurempi)

tsuriga [15.06.2011 16:20:16]

#

Tuon kun olisit laittanu vastauksees niin olis säästytty paljolta. Tulevaisuudessa olis hienoa, jos viestiin vastatessasi ottaisit oikeasti huomioon sen viestin koko sisällön, johon olet vastaamassa. Nyt tuli seittemän viestiä myöhemmin tarkennus, että "ai niin, koodin pitää tehdä myös tällainen muutos...". Tossahan $suurempi on sama alustaa nollaksi (positiivisilla luvuilla), säästytään siltäkin aritmetiikalta. Lisäksi tuo PHP_INT_MAXiin alustaminen on nopeampi vaatiessaan vähemmän vertailuja silmukassa.

Metabolix [15.06.2011 16:22:14]

#

Ei olekaan vaikeaa, mutta mitä hyötyä tuosta on verrattuna aiempaan is_null- tai vaikka is_bool-ratkaisuun? Silloin säästyisi silmukan jälkeenkin yksi tarkistus, kun ei tarvitsisi tarkistaa, löytyikö nyt jotain vai ei.

"Loogisesti" ei siis kannata käyttää tuollaista arvoa, joka vaatii lisätarkistuksen. Loogisesti kannattaa käyttää tsurigan ehdottamaa PHP_MAX_INT-arvoa, jolloin silmukassa riittää tarkistus yhteen suuntaan. Jos on pienikin epäilys, että kyseinen arvo voi esiintyä taulukossa luonnostaan, kannattaa vaihtaa nulliin tai falseen, joiden merkityksen satunnainen koodin lukija ymmärtää ilman kommenttejakin paremmin kuin tuollaisen hatusta vedetyn $raja-1:n.

Sinun logiikallasi olisi aika kätevää laittaa siihen alkuarvoksi "porkkana", joka on sekin ilmeisen mahdoton vastaus itse kysymykseen ja vaatii aivan yhtä paljon turhaa koodia. >_>

Triton [15.06.2011 22:51:36]

#

Tässä tulee taas vastaan PHP:n huonous. Minusta ei ainakaan ole kovin loogista, että kokonaislukumuuttujaan asetetaan (tai relevantimmin, voi asettaa) arvoksi null tai boolean arvo. Tuo on muutenkin huono tapa, sillä sellaisissa kielessä, jotka ovat vahvasti sekä staattisesti tyypitetty, ei voi noin edes tehdä. Hyvä esimerkki on vaikka Java. Tietenkin tilanne on siinä mielessä eri, että PHP:ssä muuttujilla ei ole kiinteesti määritettyjä tyyppejä, mutta itse ainakin pyrin aina asettamaan muuttujaan vain tietynlaisia arvoja.

Metabolix [15.06.2011 23:09:35]

#

Minusta taas ei voi sanoa, että jokin ratkaisu olisi huono vain siksi, että jossain muussa kielessä ei voi tehdä niin. Vaikka useimmissa tilanteissa olenkin samaa mieltä sekä heikon että dynaamisen tyypityksen ongelmista, yleispätevä null-arvo on usein käytännöllinen. Jos null-vaihtoehtoa ei ole, käytetään usein apumuuttujaa tai ehdotettua PHP_MAX_INT-tyyppistä ratkaisua tai – tällaisessa tapauksessa – parhaan löydetyn arvon indeksiä tai osoitinta. Null-arvon kanssa ei kuitenkaan tarvitse miettiä, oliko juuri tämän muuttujan kohdalla tyhjän merkkinä 0, -1 vai MAX_INT tai tuliko se apumuuttujakin nyt asetettua oikein.

Mutta kun nyt mainitsit, niin näytänpä vielä ihan ilkeyttäni, että kyllähän se Javakin toimii aivan samalla tavalla:

int[] luvut = {1, 2, 3, 4};
int raja = 5;
Integer suurempi = null;
for (int i = 0; i < luvut.length; ++i) {
    if (luvut[i] >= raja && (suurempi == null || luvut[i] < suurempi)) {
        suurempi = luvut[i];
    }
}
if (suurempi == null) {
    System.out.println("Ei tulosta: " + suurempi);
} else {
    System.out.println("Tulos: " + suurempi);
}

Grez [15.06.2011 23:09:45]

#

Triton kirjoitti:

Tässä tulee taas vastaan PHP:n huonous. Minusta ei ainakaan ole kovin loogista, että kokonaislukumuuttujaan asetetaan (tai relevantimmin, voi asettaa) arvoksi null tai boolean arvo.

Anteeksi mihin? Ei PHP:ssä ole kokonaislukumuuttujia, vaan ihan vaan muuttujia.

Triton [15.06.2011 23:24:29]

#

Metabolix: Olen kyllä tietoinen, että Javassa voi käyttää olioversioita primitiivisistä tietotyypeistäkin. Näitä nk. wrappereitähän joutuu käyttämään myös mm. geneeristen luokkien yhteydessä. ;)

Itse pidän siitä, että ohjelmointikielet ovat keskenään mahdollisimman yhdenmukaisia, etenkin kun kyseessä on saman sukuiset kielet (ja näinhän PHP:n ja Javan tapauksessa myöskin on).

Grez: Niin, kuten sanoin, pyrin itse käyttämään PHP:tä siten, että käytän muuttujia niin, etten aseta yhteen muuttujaan kuin kokonaislukuja ja toiseen vain liukulukuja jne... Vaikkei PHP:ssä teknisesti ottaen olekaan kuin vain muuttujia, niin oman ajattelu tapani takia kirjoitin mitä kirjoitin. Tosin olen siitä huolimatta melko varma, että ymmärsit tasan tarkkaan sen, mitä kirjoitin.

Grez [15.06.2011 23:51:09]

#

Kyllä minun mielestä olet ymmärtänyt hieman nurinkurisesti, kun sanot että PHP:n tapa toimia ei olisi kovin loogista.

Minusta olisi monta kertaluokkaa epäloogisempaa, jos tyypitön muuttuja, eli muuttuja mihin voi asettaa mitä tahansa, muuttuisi yhtäkkiä tyypitetyksi muuttujaksi esimerkiksi, kun siihen sijoitetaan kokonaisluku.

Se, että PHP:ssä muuttujat ei ole vahvasti tyypitettyjä ei ole mikään epäloogisuus vaan suunnittelupäätös. On monia muitakin kieliä, joissa muuttujat ei ole vahvasti tyypitettyjä. Toki voidaan olla sitä mieltä että suunnittelupäätös on huono, mutta ihan loogisesti ja johdonmukaisesti sitä suunnittelupäätöstä PHP:ssä noudatetaan.

Toki muuttujien käyttö PHP-koodissa voi olla epälogista, mutta sekään nyt taas ei ole mitenkään PHP:n huonous vaan koodarin huonous. Muuttujia voi käyttää monilla tavoilla epäloogisesti myös vahvasti tyypitetyissä kielissä. Jos koodari ei osaa koodata loogisesti tyypittämättömässä kielessä, niin koodarin kannattanee käyttää jotain muuta kieltä.

Jos kaikki tykkäisivät tehdä kaikki asiat samalla tavalla, niin meillä ei varmaan olisi kuin yksi ohjelmointikieli.

Ja itsehän en myöskään pidä PHP:stä.

Antti Laaksonen [15.06.2011 23:51:13]

#

Triton kirjoitti:

Itse pidän siitä, että ohjelmointikielet ovat keskenään mahdollisimman yhdenmukaisia, etenkin kun kyseessä on saman sukuiset kielet (ja näinhän PHP:n ja Javan tapauksessa myöskin on).

Toisaalta on rikkaus, että ohjelmointikielet ovat erilaisia, ja joka ratkaisussa on omat hyvät ja huonot puolensa. Kysymys ei ole siitä, että PHP:n tekijät ovat yrittäneet tehdä Javan kopion mutta epäonnistuneet siinä tyhmyyttään.

PHP on siinä mielessä monipuolisempi, että se tarjoaa molemmat vaihtoehdot: joko voi käyttää samaa muuttujaa aina samantyyppisenä (kuten itse teet) tai sitten vaihdella muuttujan tyyppiä tarpeen mukaan.

Grez [16.06.2011 00:00:16]

#

Antti Laaksonen kirjoitti:

PHP on siinä mielessä monipuolisempi, että se tarjoaa molemmat vaihtoehdot: joko voi käyttää samaa muuttujaa aina samantyyppisenä (kuten itse teet) tai sitten vaihdella muuttujan tyyppiä tarpeen mukaan.

Omasta mielestäni PHP:ssä ei voi johdonmukaisesti käyttää vaihtoehtoa, että muuttuja olisi aina samantyyppinen, koska silloin ei voisi käyttää PHP:hen sisältyviä funktioita jotka palauttavat milloin mitäkin.

Tai no tokihan funktioiden kanssa voisi käyttää aina tilapäismuuttujia joihin ja joista arvot sitten kopioitaisiin vakiotyyppisiin muuttujiin. Tämä tapa olisi minusta kuitenkin huonompi kuin vaan pelata PHP:n säännöillä ja tarkistaa muuttujan tyyppi tarpeen mukaan ja/tai käyttää tyypin huomioivia vertailuja yms.


Sivun alkuun

Vastaus

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

Tietoa sivustosta