perjantai 8. toukokuuta 2020

Pisin yläriviltä kirjoitettava sana?

Vaihteeksi kevennys. Oletko huomannut, että englanninkielen sana "typewriter" voidaan kirjoittaa yksinomaan näppäimistön ylärivin merkeillä? Joku väitti, ettei se ole edes sattumaa, vaan että laitteen omat kirjaimet olisi tarkoituksella sijoitettu samaan riviin. Kymmenen merkin sana on englanninkieliseksi melko pitkä, kun eivät juuri yhdyssanoja harrasta. Typewriter on samalla myös pisin yläriviltä kirjoitettava sana.

Mikä mahtaa olla pisin suomenkielinen sana, joka voidaan kirjoittaa pelkillä ylärivin merkeillä? Oma nimi (petteri) tulee ensimmäisenä mieleen. Löytyisikö pidempiä?

Python-ohjelmointikieli on kuin tehty merkkijonojen käsittelyyn. Avataan siis suomen kielen sanoja sisältävä lista ja tehdään pieni ohjelma, joka tutkii merkit. Lopputulos riippuu tietenkin sanalistasta, tässä käytin vanhempaa versiota KOTUSin tiedostosta (http://kaino.kotus.fi/sanat/nykysuomi/). Siitä kuitenkin puuttuivat mm. henkilönimet.

Pascal- tai C-kieltä ohjelmoineelle Python tuntuu hämmentävältä, kun puolipisteitä ei ole eikä luuppeja tai ehtolauseita päätetä selkeillä avainsanoilla. Mutta tässä Python pääsee oikeuksiinsa, sillä sanalistoille on oma tietotyyppi ja sen lajittelu käänteiseen pituusjärjestykseen pituuden mukaan käy yhdellä komennolla sort (reverse=True, key=len). Teepä sama jollain toisella kielellä!

Sanalistassa on 91591 sanaa, joista ylärivin merkeillä voidaan kirjoittaa 221 kappaletta. Pisin on "prioriteetti", sitä seuraavat "irtopuuteri" ja "prototyyppi". Lyhyemmistä sanoista yleisiä ovat mm. reportteri, tuotetieto, portieeri, etupiiri, irtiotto, operetti, piirturi, rotuoppi, tiepiiri, eetteri, petturi, tippuri, torttu ja trippi.

Keskirivin sanoja on muuten 126, niistä pisin "laakajäkälä" ja seuraavina "hölkkääjä" sekä "sähkösaha".

Alarivin merkit z x c v b n m ovat kaikki konsonantteja eikä niillä voida kirjoittaa yhtään suomenkielistä sanaa.

Entä jos olisi toinen käsi paketissa? Millaisia sanoja voisin kirjoittaa pelkällä vasemmalla tai oikealla kädellä (käyttäen 10-sormijärjestelmän jakoa)?

Sanaston pisin on vasemman käden sana on "trasseerata", mutta ensimmäinen yleinen sana on "vastattava", sitten tulee mm. sarastaa, vastaava, vastedes, rasvata, asettaa, varasta, arvata, saaste, vasara ja aarre. Yhteensä sanoja on 164.

Oikealla kädellä voisi kirjoittaa 1199 sanaa, niistä pisimmät "äänihuulikyhmy", "oopiumipiippu" ja "hännänhuippu". Muita pitkiä ovat esim. ylipäällikkö, äijänkäppänä, hiilikoukku, huulipuikko ja joulukinkku.

Jos saa valita, kannattaa siis loukata vasen käsi!

23 kommenttia:

Anonyymi kirjoitti...

Alariviltä voi kirjoittaa roomalaisia numeroita, 2020 = MMXX

Anonyymi kirjoitti...

niin jos ei käytä yhdyssanoja

retrotietoperusprioriteettiteroiteoire


Anonyymi kirjoitti...

@Petteri

Mutta tässä Python pääsee oikeuksiinsa, sillä sanalistoille on oma tietotyyppi ja sen lajittelu käänteiseen pituusjärjestykseen pituuden mukaan käy yhdellä komennolla sort (reverse=True, key=len). Teepä sama jollain toisella kielellä!

Python ei ole oma suosikkin mm. juuri niistä mainitsemistasi syntaksiin liittyvistä seikoista ja muutamista muista mukaanlukien pythonin hitaus. Hyviäkin puolia pythonissa on, mutta ei tosiaan oma valintani useinkaan, ellei ole aivan pakko sitä käyttää niin jotain muuta käytän mieluummin.

Mutta pituuden mukaan lajittelu on näköjään tehty pythonissa varsin helpoksi. Ei se kovin vaikee tosin ole muissakaan kielissä ollut pitkään aikaan. Alla esimerkkejä (CL) lispillä ja perlillä, joissa käytetään vakiintunutta tapaa välittää lajittelufunktiolle vertailufunktio. Myös C:n quicksort kanssa käytetään samaa ideaa vertailufunktiosta.

Ensiksi merkkijonojen lajittelu nousevaan ja laskevaan järjestykseen löytyy valmiit funktiot (viimeinen argumentti)

CL-USER> (sort '("ääliö" "älä" "lyö" "ööliä" "läikkyy") #'string<)
("lyö" "läikkyy" "älä" "ääliö" "ööliä")

CL-USER> (sort '("ääliö" "älä" "lyö" "ööliä" "läikkyy") #'string>)
("ööliä" "ääliö" "älä" "läikkyy" "lyö")

Sitten merkkijonon pituuden mukaan ja koska ei ole valmista funktiota niin asia täytyy ratkaista vaikkapa anonyymin lambda-funktion avulla. Käytäntö on, että vertailufunktiolle välitetään a ja b parametrit, joita verrataan ja paluuarvolla kerrotaan mihin järjestykseen ne kuuluvat.

CL-USER> (sort '("ääliö" "älä" "lyö" "ööliä" "läikkyy") #'(lambda (a b) (> (length a) (length b))))
("läikkyy" "ääliö" "ööliä" "älä" "lyö")

Jos funktiota tarvitsee usein, siitä kannattaa tehdä erillinen nimetty vertailu-funktio ja käyttää sitä.

CL-USER> (defun string-len> (a b)
(> (length a) (length b)))

CL-USER> (sort '("ääliö" "älä" "lyö" "ööliä" "läikkyy") #'string-len>)
("läikkyy" "ääliö" "ööliä" "älä" "lyö")

Ja usein vielä helpompi on usein tehdä erillinen funktio koko lajittelusta

CL-USER> (defun sort-strlen> (list)
(sort list
(lambda (a b)
(> (length a) (length b)))))

CL-USER> (sort-strlen> '("ääliö" "älä" "lyö" "ööliä" "läikkyy"))
("läikkyy" "ääliö" "ööliä" "älä" "lyö")

Perlissä on pitkälle sama idea, voi tehdä anonyymin funktion tai sitten tehdä erillinen funktio ja viitata siihen tuon sort:n jälkeen.

$ perl -CSDA -de1
...
main::(-e:1): 1
DB<1> print join(' ',sort { length($b) <=> length($a) } qw(ääliö älä lyö ööliä läikkyy));
läikkyy ääliö ööliä älä lyö

DB<2> sub strlencmp() { my ($a,$b) = (@_); return $a <=> $b; }

DB<3> print join(' ',sort strlencmp qw(ääliö älä lyö ööliä läikkyy));
ääliö älä lyö ööliä läikkyy

DB<4> ^D
$

Yllä perl (5.30) REPL:nä on debuggeri ja komentorivinoptiolla kytketään UTF-8 automaattikonversio päälle, perl ei sitä oletuksena vielä muuten käytä.

ps. code tai pre tägejä olisi ollut hyvä käyttää, mutta niitä ei ole täällä näköjään sallittu.

Anonyymi kirjoitti...

Näköjään jäi tuohon viimeiseen perl esimerkkiin bugi, sen strlencmp funktion return lauseen jälkeen pitäisi olla sama mitä on edellisessä anonyymissä funktiossa sortin jälkeen, sen jälkeen pituusärjestykseen lajjittelu toimii oikein.

Anonyymi kirjoitti...

Suunnilleen samalla vaivalla kuin Pythonilla, olisi asian tehnyt myös IDL:llä, jonka WHERE-funktiolla olisi voinut vielä näppärästi tsekata sen, missä sanoissa on kolmantena kirjaimena "s" ja viidentenä "a" :) Joo, siinäkin on on mahdollisuus käyttää merkkijonotaulukoita, joiden alkioita on näppärä tarkastella. Eikä tämä kieli ole varsinaisesti merkkijonojen käsittelyyn tehty, vaan datan räpläämiseen. Varmaan joku Perl hoitaisi sen vielä kätevämmin, joskaan ei välttämättä selkolukuisemmin.

Anonyymi kirjoitti...

JavaScriptillä sorttaus pituuden mukaan olisi mennyt näin:

["aa", "bbbbbb", "ccc", "ddddddddd"].sort((a,b)=>b.length-a.length)

Anonyymi kirjoitti...

Komentorivillä:

>grep -v "[asdfghjklöäzxcvbnm\-]" Kotuslista.txt | awk '{print length($1), $1}' |sort -rn
13 prioriteetti
12 prototyyppi
12 irtopuuteri
11 tyyppioppi
11 tuotetieto
11 tuoretuote
11 tuorepuuro
11 territorio
11 reportteri
11 pitoteippi

Tuon grepin olis varmaankin saanut ympättyä awkiin, mutta en jaksanut pohtia enkä kokeilla pitempään.

Anonyymi kirjoitti...

@Anonyymi

Hyvä ratkaisu, mutta jätit pois sen osuuden miten teit sanalistan Kotuksen XML:stä.

Tässä yksi tapa tehdä se ja samalla grep -v tarpeettomana pois.

$ xmlstarlet sel -T -t -m /kotus-sanalista/st -s A:T:- "@s" -v "s" -n kotus-sanalista_v1.xml | awk '!/[asdfghjklöäzxcvbnm\-]/ {print length($1), $1}' | sort -nr | head -10

12 prioriteetti
11 prototyyppi
11 irtopuuteri
10 tyyppioppi
10 tuotetieto
10 tuoretuote
10 tuorepuuro
10 territorio
10 reportteri
10 pitoteippi

Yllä oleva olettaa, että käytössä on awk joka tukee UTF-8 merkistöä, monet vanhemmat GNU ja BSD versiot ei sitä tukeneet ja mm. mawk (linkattuna nimelle awk), joka on usein pienuutensa vuoksi asennusmedioilla ja sulautetuissa järjestelmissä ei tue kun korkeintaan yksitavuisia merkkejä. Merkkimuunnoksen voi tehdä helposti lisäämällä putkeen "iconv -f UTF-8 -t ISO-8859-15" ennen awk-ohjelmaa.

Mutta mistä johtuu, että laskettujen sanojen pituus sinun vastaaviin on yhtä pienempi. Ja jos lasken merkki merkiltä, niin prioriteetti sanassa on mielestäni 12 merkkiä, ei 13. XML-tiedostossa ei näytä olevan DOS:lle tyypillisiä rivinloppuja, jota arvelin ensin mutta ei niitä siinä minusta ole. Mistähän sinun laskemissasi tuo ero johtuu?

Anonyymi kirjoitti...

Hups, olis näköjään pitänyt tarkistaa omat höpinät.

Kotuslista oli mulla valmiina vuosien takaa ja DOSsin rivinvaihtohan se siellä ylimääräisenä. File-komentokin totesi tiedostosta ”with CRLF line terminators”. Ratkaisuksi riitti vimissä ”set fileformat=unix” ja tallennus (dos2unix-ohjelman olen näköjään poistanut koneeltani joskus muinoin). Nyt on mullakin vastaukset oikean pituiset. :)

Petteri Järvinen kirjoitti...

Teillä oli mainioita esimerkkejä eri ohjelmointikielistä.

Uudet suosikkikielet painottuvat tekstin ja datan käsittelyyn, Pascalin ja C:n tiukat tyyppitarkistukset ja etukäteen allokoitavat merkkijonot jäävät pois, Fortranin matematiikkapainotteisuudesta puhumattakaan. Nykyään laskennassa kokonaisluku-tietotyypillä ei ole ylärajaa. Hienoa kehitystä, joka kertoo sovellusten muuttumisesta.

Vähän kyllä hirvittää millaisia virheitä koodaajat mahtavat nykyään tehdä, kun tyypitys on vapaata, proseduurikutsujen argumenttien määrä voi vaihdella ja sisennyksetkin vaikuttavat ohjelman toimintaan... onko tässä yksi syy tietoturvaongelmiin?

Anonyymi kirjoitti...

Hauska idea!

Ohjelmointikielten tyyppitarkistukset on hyvin mielenkiintoinen kokonaisuus. Kokemus C:n tiukasta tyyppitarkistuksesta tulee siitä, että se on käännösaikaan eli staattisesti tyypitetty. Mutta se on lisäksi kuitenkin heikosti tyypitetty, eli C luokitellaan luokkaan "staattinen, heikko".

Python on "dynaaminen, vahva". Tavallaan näppärä ohjelmointivaiheessa, mutta varsinkin jälkikäteen vanhaa koodia lukiessa ja muokatessa staattisesta tyypityksestä on paljon hyötyä.

Tietoturvaongelmia aiheuttaa etenkin C:n kaltainen heikko tyypitys, joka ei auta tai pakota ohjelmoijaa käsittelemään muistissa olevaa dataa aina oikein.

Esimerkin vuoksi alla on kyseinen ohjelma tehtynä Rustilla, joka on yhtä matalan tason kieli kuin C, mutta tyypitykseltään "staattinen, vahva", ja joka kielenä pitkästä aikaa tuo ohjelmointikentälle oikeasti uutta.

Nykytrendi suosituissa kielissä on nimenomaan staattisen ja vahvan tyypityksen suuntaan. Esimerkkejä tällaisista ovat mm. Rust, Swift, TypeScript, Go, Scala ja Kotlin. Kaikki suosittuja moderneja kieliä, sekä vahvasti ja staattisesti tyypitettyjä.

Jopa matalan tason Rustillakin sanojen järjestäminen pituuden mukaan onnistuu yhdellä rivillä: lines.sort_by_key(|line| !line.len()); Puolipisteetkin löytyvät! :) Järjestysfunktiossa huutomerkki saa aikaan saman kuin Pythonissa reverse=True.

Tämänkaltainen ergonomia on nykykielissä yleistä, ja hyvä niin!

use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
let matches: HashSet = "qwertyuiopå".chars().collect();

let file = File::open("sanat.txt").expect("Cannot open file");
let mut lines: Vec = BufReader::new(file)
.lines()
.map(|line| line.expect("Line parse failed").to_lowercase())
.collect();

lines.sort_by_key(|line| !line.len());

let matching_lines: Vec = lines
.into_iter()
.filter(|line| line.chars().all(|c| matches.contains(&c)))
.collect();

println!("Yhteensä {} sanaa, top 20:", matching_lines.len());
for line in matching_lines.into_iter().take(20) {
println!("{} {}", line.len(), line);
}
}

Kun ohjelman ajaa, se tulostaa:

Yhteensä 234 sanaa, top 20:
12 prioriteetti
11 irtopuuteri
11 prototyyppi
10 pitoteippi
10 reportteri
10 territorio
10 tuorepuuro
10 tuoretuote
10 tuotetieto
10 tyyppioppi
9 epiteetti
9 irtouitto
9 pieteetti
9 portieeri
9 preteriti
9 retriitti
9 tyyrpuuri
9 yrttiuute
8 etupiiri
8 irtiotto

Anonyymi kirjoitti...

@Petteri

Löysä/heikko (weak/loose) tyypitys liittyy yleensä kieliin, joissa on dynaaminen muistin varaaminen ja roskankeräys (garbage collection ie. GC), joten niillä välttää enimmät ongelmat puskurien ylivuodoista, muistivuoitoihin ja yrityksiltä käyttää jotain jo vapautetusta muistista, jotka ovat niitä C-kielellä tehtyjen ohjelmien yleisimpiä virheitä ja joista voi seurata tietoturvaongelmia riippuen siitä missä paikassa virhe sattuu olemaan. Jos niistä ei aiheudukaan aina tietoturvaongelmaa, niin ne tekevät ohjelmasta helposti syötteestä riippuen epävakaan ja arvaamattoman.

Et sinä näitä ongellmia dynaamista muistinvarausta tekevilä kielillä juuri löydä. Toki bugeja on ollut kielien toteutuksissa, mutta kaiken kaikkiaan em. ongelmat ovat olleet erittäin pieni ongelma koko sinä aikana kun kyseisiä kieliä on ollut olemassa.

Löysä/heikko tyypitys ei välttämättä myöskään johda tietoturvaongelmiin, juuri siksi että sitä käytetään yhdessä dynaamisen muistivaraamisen kanssa ja usein tehdään ajon aikana oletuksena automaattista konversiota tyypistä toiseen oletuksena ilman ohjelmoija nimenomaista tyypin toiseksi muuttamista.

Yksi esimerkki tästä on Ruby -kielen Duck Typing (If looks like a duck, walks like a duck and quacks like a duck, it must be duck). Rubyssä kaikki mitä on, on objekteja & olioita. Ne peritään yhdestä ylimmästä objektista. Näistä luodaan instansseja (muuttujia ym.) varattaessa muistia datalle. Instanssit perivät myös aina joukon metodeja. Metodien joukossa tyyppimuunnoksissa tarvittavia metodeja, joiden käyttö kuhunkin konversioon riippuu siitä mikä operaattori ja kohde operandin (kohde objektin) tyyppi on. Kaikki tämä on samalla tavalla kun mm. pythonissa, mutta rubyssä muunnokset tapahtuvat suurelta osin implisiittisesti, eli ei tarvitse tehdä itse explisiittistä type castia perustyypeille ja luokkiin voi lisätä ja muuttaa (overload) myös jo olemassa olevia muunnos-metodeja. Voit siten luoda omia tietotyyppejä ja lisätä niille konversio-metodit muihin jo olemassa oleviin ja omiin luomiisi tietotyyppeihin ja kieli käyttää niitä implisiittisesti sinun sitä tekemättä mitään sen jälkeen asian eteen.

Ruby yrittää tehdä parhaansa ja tehdä tarvittavat muunnokset niin, että lopputulos olisi aina järkevä. Tämä tekee nopeasta protoilusta helppoa. Mitä jos muuttaisin tuon tietorakenteen tyypistä toiseksi kokeilut helpottuvat ja on nopeita tehdä. Mutta isossa ohjelmassa ja jos muunnoksia on paljon se hidastaa suoritusta merkittävästi, ellei sitten huomioi myöhemmin ja kirjoita paremmin näitä rakenteita vastaamaan paremmin toisiaan tyyppimuutosten vähentämiseksi.

Anonyymi kirjoitti...

... jatko

Mutta heikolla tyypityksellä oli ne sitten tehty lähes kuinka fiksusti tahansa, niin ongelmat löytyvät myöhemmin käytäytännössä usein vasta ajon aikana tulkin suorittaessa tai JIT-kääntäjän huomatessa ongelman yhdessä ohjelman käyttämän datan kanssa. Tiukan tyypityksen kanssa nämä löytyvät varhaisemmassa vaiheessa, kääntäessä tai viimeistään JIT-kääntäjä huomaa, että tyypit eivät täsmää ja siitä seuraa ongelmia.

Tiukan tyypityksen kanssa joutuu tekemään enemmän työtä koko ajan, tietotyyppejä ja rakenteita suunnitellessa ja ohjelmoitaessa, mutta testaaminen on sitten suoraviivaisempaa ja helpompaa kun tietotyyppien kanssa ei tule ongelmia kun ohjelma kääntyy.

Löysän tyypityksen kanssa saa nopeasti varsin hyvin toimivaa koodia aikaan, mutta testaamiseen pitää panostaa enemmän. Molemmissa on etuja ja riippuu siitä mitä on tekemässä minkälainen kieli kannattaa siihen valita.

Mutta ns. tyyppien oikeellisuus (type-correctness), ei takaa ettei ohjelmoijalta jää ohjelmaan muita loogisia virheitä.

Yksi tapa ratkoa löysän tyypityksen ongelmia on hyvä esimerkki Typescript joka on tiukan tyypityksen esikääntäjä Javascriptille, joka kielenä edustaa löysää typitystä. Sen lisäksi, että virheet löytyvät nopeammin, niin testeissä myös Javascriptille käännetyn koodin suoritusnopeus on usein parempi.

Ohjelmointikieliä on kehitetty valtaisa määrä, mutta ei ole yhtään kieltä joka olisi täysin ylivoimainen kaikessa vaan se mitä on tarkoitus tehdä, mille alustalle, kenelle ja kuka ohjelmistoa jatkossa käyttää sekä ylläpitää merkitsee vähintään yhtä paljon kun se mikä kieli on ilmaisuvoimaltaan suurin, tuottaa tehokkaiman lopputuloksen tai mikä on sillä hetkellä helpoin ottaa käyttöön ja oppia.

Mielenkiintoista näiden aisoiden seuraaminen kuitenkin on ollut noin neljän vuosikymmenen ajan. Uuden kielen opettelu aina välillä ja sen käyttäminen pitää mielen virkeänä :)

Anonyymi kirjoitti...

@Anonyymi

Hyvä kirjoitus sinulta. olen samaa mieltä asiosta mitä toit esiin. Ja rust on hyvä esimerkki uusista kielistä joita on tullut viimeisen vuosikymmenen aikana, Go on muutaman vuoden vanhempi mutta erittäin hyvä sekin. Rustin osalta suurimmat omat varaumat, etten lähde heti sillä tekemään vielä mitään isompaa on sen kehittymisen nopeus, toiseksi sen build systeemi (cargo) vetää mukaan helposti tavaraa valtavia määriä riippuvuuksista johtuen ja sen lisäksi sen tuottamat binäärit ovat erittäin isoja (load/link työkaluti ei ole vielä niin fiksuja kun C/C++ puolella on).

Tietoturvan puolesta rust on pitkästä aikaa parasta mitä ohjelmointikielirintamalla nähty. Javan päädyttyä Oraclelle lisenssikustannukset jo kaupallisessa käytössä nousivat ellei käytä OpenJDK:ta. Mutta vaikka käyttäisi, niin kieleen tulevien muutosten myötä (RMI ja sen päälle rakennetut serialisointia käyttävät poistetaan koska niistä ei saada tietoturvallisia), Javan houkuttelevuus vähenee ja sen myötä myös käyttö vähenee jatkossa. RMI jonka päälle on tehty vaikka mitä (JAX ym) on ollut yksi keskeisistä asioista sen valintaan sen lisäksi, että Java helpompana kielenä kun C++ estää tehokkaammin olemaan tekemästä tyhmyyksiä.

petrip kirjoitti...

Python ja muut sen tyyliset kielet ovat enemmän käytössä työkaluohjelmoinnissa kuin tuotantokoodissa. siinä ohjelmoinnin nopeus on tärkeämpää kuin suorituskyky tai tyypityksen luoma yllpidettävyys. Toki sitä on tuotannossakin massivisia määriä kuten vaikka osat youtube:sta

Petteri Järvinen kirjoitti...

Haluaisinpa vaihtaa muutaman sanan Pythonin keksijän kanssa...

Miksi

for i=0 to 9

pitää Pythonissa kirjoittaa

for i in range (0,10):

Saattaa tuottaa muutaman vaikeasti havaittavan virheen aiempiin ohjelmointikieliin tottuneille koodareille.

petrip kirjoitti...

Toi on äärimmäisen looginen rakenne koska se on geneerinen. Siinä ei loopat jostakin johonkin vaan iteroitavan objektin kaikki elementit läpi vaikka näin
In [1]: for i in "this is test": print i
t
h
i
s

i
s

t
e
s
t


Petteri Järvinen kirjoitti...

Jos i ei ole esitelty kokonaisluvuksi, miksi sen pitää olla kokonaisluku? Yhdenmukaisuuden vuoksi for i in range (0.1, 0.5) pitäisi toimia, mutta ei kelpaa.

Ja miksi range (0,10) sisältää alarajan (0) mutta ei ylärajaa (10)?

Anonyymi kirjoitti...

"Ja miksi range (0,10) sisältää alarajan (0) mutta ei ylärajaa (10)?"

Python on aika hyvin suunniteltu, joten olettaisin että se mikä on valittu on yleensä loogisin ja selkein vaihtoehto.

Tyhjä range on esim. range(0,0) tai range(1,1).

Ykkösestä alkava kolmen pituinen range on range(1,1+3).

Muitakin syitä voi olla.


"Yhdenmukaisuuden vuoksi for i in range (0.1, 0.5) pitäisi toimia, mutta ei kelpaa."

Tämä ainakin näyttää miten Python tekee ajoaikaisen (dynamic) tyyppitarkistuksen verrattuna C-kielen käännösaikaiseen (static) tyyppitarkistukseen. Python ei muuten mainosta, että tyypitys olisi löysä/heikko (kuten eivät tietääkseni muutkaan ohjelmointikielet).

Anonyymi kirjoitti...

@Petteri

Jos i ei ole esitelty kokonaisluvuksi, miksi sen pitää olla kokonaisluku? Yhdenmukaisuuden vuoksi for i in range (0.1, 0.5) pitäisi toimia, mutta ei kelpaa.

Arvelen, että Guido (van Rossum) päätyi siihen sen vuoksi kun kokonaisluvuilla on helppo valistuneesti arvata mikä voisi olla hyvä step arvo oletuksena ja tarve tuon kaltaiselle toiminnolle on varsin yleinen.

Mutta kielten suunnittelija joutuu myös aina puntaroimaan, mitkä ovat niitä keskeisiä kieleen tarvittavia ominaisuuksia ja mitkä kannattaa toteuttaa kirjastoilla tai ovat niin vähän tarvittuja, mutta helposti tehtävissä että ne voi jättää tarvitsevan itse tehtäväksi. Liukuluvuilla

Löysin helposti web-sivun, jossa range toimintaa on selitetty ja siellä lopussa on myös esimerkki miten voi tehdä liukuluvuilla toimivan funktion varsin helposti.

https://www.pythoncentral.io/pythons-range-function-explained/

@petrip tuossa ylempänä viittaa iteraattoreihin. Ne ovat erittäin tarpeellisia ja kannattaa hetki miettiä miksi nykyään aina kun mahdollista pyritään käyttämään generisiä kielen iteraattoreita.

For lauseita ja niiden kanssa Indeksejä käyttäessä syntyy helposti virheitä, jossa jommasta kummasta päästä tietorakennetta jää helposti jotain pois tai yritetään vahingossa käsitellä yli rakenteen viimeisen elementin, jolloin ohelma käyttäytyy omituisesti, päättyy virheeseen tai aiheutuu jopa tietoturvaongelma. Alkupään kanssa virheitä syntyy siksi, että joissakin kielissä indeksit alkavat nollasta ja toisissa ykkösestä, lopussa virheitä syntyy taas siksi että ohjelmoija epähuomiossa kirjoitaa lopetuksen ehtolauseen väärin jostain syystä.

Edellisiä virheitä kutsutaan ns. aidanpylväs-virheiksi (fence-post errors). Nimitys johtuu siitä, että suljetussa aidassa on aina yhtä monta pylvästä kun on aidan väliosia. Mutta avoimessa aidassa on aina yksi pylväs enemmän kun väliosia. Jos rakennat aitaa ja ostat osia, tämä on hyvä pitää mielessä tavaroita tilatessa.

Ohjelmoitaessa kun käytät toistorakenteita, joissa joudut itse kirjoittamaan toiston lopettamisen ehtolauseen on syytä olla tarkkana ja muistaa edellinen.

Ohjelmointikielet kehittyvät ja kun joku keksii jonkun hyvän ratkaisun yleiseen ongelmaan, niin sitä ratkaisua voidaan sitten hyödyntää uusien kielten kanssa ja joskus niitä myös toteutetaan vanhempien kielten uusiin versioihin.

Yksi näistä hyvistä ideosita on jo mainittu iteraattori. Niiden idea on toteuttaa sisäisesti kielen tasolla toistorakenteita, joita käyttämällä ohjelma automaattisesti askeltaa lävitse koko rakenteen jäsenet, ensimmäisestä viimeiseen ohjelmoijan miettimättä sitä tuliko nyt käsiteltyä varmasti kaikki tietorakenteen jäsenet vai ei.

Iteratttoreita kannataa ehdottomasti opetella käyttämään aina kun mahdollista ja vaihtaa pois aiemmin opitusta tavasta askeltaa perinteisellä tavalla yksi kerrallaan indeksillä osoittamalla tietorakenteita. Koska sillä välttyy helposti yhdeltä aiemmin varsin yleiseltä loogiselta virheeltä, joita ohjelmiin helposti jäi.

Anonyymi kirjoitti...

Hmm... jäi näköjään lause kesken, edellisessä. Liukuluvuilla iterointi on paljon vähemmän tarvittu kun kokonaisluvuilla, joten se on jätetty tarvitsevan ohjelmoijan itse tehtäväksi.

Anonyymi kirjoitti...

""Ja miksi range (0,10) sisältää alarajan (0) mutta ei ylärajaa (10)?"

Python on aika hyvin suunniteltu, joten olettaisin että se mikä on valittu on yleensä loogisin ja selkein vaihtoehto.

Tyhjä range on esim. range(0,0) tai range(1,1).

Ykkösestä alkava kolmen pituinen range on range(1,1+3).

Muitakin syitä voi olla."

Tähän oli muistaakseni ns. muu syy, joka oli osastoa "kun näin tehdään, niin se on tietyissä tilanteessa näppärää". Tällöin kun siitä on seurauksena se, että useista muista kielistä poiketen viitatessa taulukon alkioihin tyyliin a(2:5) viittaus kohdistuukin alkioihin a(2),a(3) ja a(4), mutta ei enää alkioon a(5), niin kyseessä on aivopieru. Näppäryys ei saa ikinä mennä loogisuuden edelle, se on vaarallista. Toinen vastaava, monen mielestä näppärä ominaisuus on seuraava:
a=3
b=2
c=4
c=a/b=1.5
Ei saakeli, ei todellakaan näin, kahden kokonaisluvun jakolasku ei saa muuttujan tyyppiä lennosta. Ja tämä oli sentään oikein kakkossarjan Pythonissa.

Luin viime talvena yhden Pandasia ja Matplotlibia esittelevän kirjan, sen perusteella ei herännyt isompaa innostusta kumpaakaan kohtaan. Hyvää niissä oli lähinnä ilmaisuus, mutta ne tuntuivat tekevän enimmäkseen asioita, jotka esim. IDL ja Matlab tekivät jo 15 vuotta sitten. Ja etenkin Matplotlibin dokumentaatio tuntuu olevan osastoa suppea, mutta puutteellinen, onneksi kuitenkin epälooginen.

petrip kirjoitti...

toi 2/3 = 1 aiheutti kakkossasrjasa melkoisesti hämmästystä ja siksi se muutettiin. skripti kielent on harvemmin tarkoitettu kokonaislukujen käsittelyy ja 2//3 toimiin niissä harvoissa tilanteissa vanhalla tavalla.