Elämässäni ensimmäinen kerta kun en uskalla jotain testata kovin nopeasti. Mutta kaiken kieron mitä olen uudelle yhteen- ja vähennyslaskulle keksinytkin on se ratkaissut sen oikein. Toisaalta myös lasku 1+1 onnistui. Mutta jossain muussa helpossa se tietty mokaa.
***
Desimaali-jakolaskuun löytyi uusi menetelmä - se on aina nopea mutta vaikka joissakin laskuissa saa 48 oikeaa desimaalia niin joissakin ei saa kuin muutaman. Ja muutenkin se on toistaitoinen menetelmä. Pitääpä katsoa saako primadonnan tanssimaan.
Esitänpä laskemisen periaatteen:
pitäisi laskea mitä on: 1233457890123.23/.123456
kokonaisosa: $((1233457890123230000/123456)) -> 9991072852864
aletaan laskea desimaaleja: $((1233457890123230000%123456)) -> 52016
desimaalit ovat: $((5201600000000000/123456)) -> 42133229652
siis vastaus on näin alkuunsa:
9991072852864.42133229652
haetaanpa oikea vastaus: bc -l<<<"1233457890123230000/123456" ->
9991072852864.42133229652669777086
***
Edellisestä periaatteesta tehty skripti - joka samantien laskee pari lasku-kierrosta lisää:
# tämä on uusi ja korjattu versio joka on vielä aika raakile ja muuttuu vielä paljon. Keskeneräisyyden
# osoitus on runsas välitulosten tulostaminen
function siisti () { apu=$1; merkki='';[[ ${apu//[^-]/} ]] && merkki=- && apu=${apu:1}; [[ ${apu//[^.]/} ]] || apu=$apu"." ; apu=${apu%00000000000000};apu=${apu%0000};apu=${apu%00};apu=${apu%0};apu=${apu#00000000000000};apu=${apu#0000};apu=${apu#00};apu=${apu#0};apu=${apu%.}; echo $merkki$apu ;}
function jaa () { # muutos
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1"."
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2"."
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
kokonaisosa1=${luku1%%.*}; echo x$kokonaisosa1
kokonaisosa2=${luku2%%.*}; echo y$kokonaisosa2
# apu=$((${kokonaisosa1:0:18}/${kokonaisosa2:0:18})).
kokonaisiatulosteessa=0
nolliatulosteessa=''
apu=$((${#kokonaisosa2}-${#kokonaisosa1}-1));echo $apu
case $apu in
-1) kokonaisiatulosteessa=1 ;;
0) kokonaisiatulosteessa=0 ;; # apu=85; printf "%${apu}s" | tr " " 0
1) nolliatulosteessa='' ;;
2) nolliatulosteessa=0;;
3) nolliatulosteessa=00 ;;
4) nolliatulosteessa=000 ;;
5) nolliatulosteessa=00000 ;;
6) nolliatulosteessa=000000 ;;
7) nolliatulosteessa=0000000 ;;
8) nolliatulosteessa=00000000 ;;
9) nolliatulosteessa=000000000 ;;
10)nolliatulosteessa=0000000000 ;;
*) kokonaisiatulosteessa=$((-1*$apu-1 )) ;;
esac
echo nolliatulosteessa:$nolliatulosteessa' kokonaisiatulosteessa:'$kokonaisiatulosteessa
luku1=$kokonaisosa1$desimaaliosa1
luku2=$kokonaisosa2$desimaaliosa2
echo xxx$luku1' '$luku2
unset tulos # vain varmistus että kaikki on tuloksessa tämänjälkeen uutta
for n in {1..6}; do # muodostetaan tulos-palasia 9 merkkiä kerrallaan
apu=$((10#$luku1/10#$luku2)); (( ${#apu} ==8 )) && apu=$apu'0'; tulos[$n]=${apu} ;echo a$luku1' '$luku2' '$apu
luku1=$(($luku1%$luku2))'0000000000000000000'; luku1=${luku1:0:18} ;echo z$luku1
done
for n in {1..6}; do # kootaan tulosta matriisin palasista
tulos=$tulos${tulos[$n]}
done
# tulos=$( siisti $tulos)
echo "oikea tulos 54 desimaalilla esitetynä on päällä ja alla tulos tästä laskusta:"
bc<<<"scale=54; $1/$2" | tr -d '\\\n'; echo ' tämä rivi on bc:stä'
[[ $nolliatulosteessa ]] && echo .$nolliatulosteessa${tulos:0} || echo ${tulos:0:$kokonaisiatulosteessa}.${tulos:$kokonaisiatulosteessa} ;}
jaa 1233457890123.23 .1234567
----skripti tulostaa:
oikea tulos 54 desimaalilla esitetynä on päällä ja alla tulos tästä laskusta:
9991016203439.991511193803171476315177710079728358201701487242085686 tämä rivi on bc:stä
9991016203439.991511193803171476315177710079728358201701487242085686
- tämä osoittaa ainakin että periaate on oikea - ongelmana näyttää olevan muunmuassa desimaalipisteen paikan laskeminen ja laskujen etunollat. Kai ne aikanaan saa korjatua.
- miinus-merkkiset ei vielä toimi
- tämäkin skripti on niin kasattu että sen voi helposti ajaa uudestaan - paina vai nappin nuoli-ylös jolloin funktiokutsu palaa näytölle edtoitavaksi ja kun painaa enter niin se ajetaan uudestaan editoiduilla parametreilla.
- kaikki tämän tyyppiset skriptit ovat nopeita ja sillä on vain pieni merkitys kuinka kookkaita nämä tällaiset skriptit ovat - ja esimrkiksi voi laskea niin monta desimaalia kuin sielu sietää - nopeus on aina siellä millisekunnin nurkilla.
- muuten nuo kummalliset käskyt ovat tekstinkäsittely-käskyjä eivätkä matematiikka-käskyjä - BASH on yksittäisten sanojen käsittelyssä ziljoonakertaa parempi kuin sed - ja sed on ehdottomasti paras isojen tekstien käsittelemisessä. Tai enpä ole tuosta ihan varma - sed on pajon parempi kuin "korkeantason" käskyillä invalidisoitu BASH mutta en tiedä kuinka nuo matalan tason käskyt isoissatekstinkäsittelyssä toimivat, en ole kokeillut. Paitsi silloin kerran kun puhuttiin Pythonin ylivertaisuudesta ja todettiin että kyllä sed-kin jää toiseksi ja naureskeltiin BASH:in kustannuksella - mutta itseasiassa eräs BASH:in matalan tason käsky oli melkein yhtähyvä - mutta luulin silloin että ihan yksittäinen tapaus se oli, vaan eipä tainnut ollakaan.
***
Oli pahantahtoinen teko viedä BASH:ilta kirjasto-osoitin - kirjastojen toimintaan se ei vaikuta mutta tekee kirjastojen käyttämisestä hankalaa.
Tästä hommasta on väännetty ikuisesti - esimerkiksi väittämällä että kirjastot ovat tietoturvariski. Mutta jokaisessa Ubuntussa on 85 funktion kirjasto joten jos ne ovat tietoturvariski niin se tietoturva on mennyt jo ja se osoittimen vieminen oli pelkkää kiusantekoa.
***
Aivan kaikesta voi ja myös pitää tehdä funktio kirjastoon sillä kukaan ei voi muistaa kaikkia pieniä kikkoja - tai ainakaan viitsi kirjoittaa. Esimerkiksi käsky joka etsii jostakin jotakin - tekstinpalasia lauseista tai tiedostoista, numeroita ja vaikka mitä. Pienissä hommissa se on paljon parempi kuin grep:
function onkoosa () { [ -z "$1" ] || { [ -z "${2##*$1*}" ] && [ -n "$2" ] && echo joo || echo ei ;};}
- kutsu etsittäessä tiedostosta: onkoosa BEGIN "$(cat /boot/grub/grub.cfg)"
- kutsu etsittäessa tekstijonosta:onkoosa öp <muutuujan nimi>
matriisista haku sitten toisella tavalla:
function onkomatriisissa () { [[ " ${@:2} " =~ $1 ]] && echo joo || echo ei ;};
esimerkiksi: matriisi=({1..100000}); onkomatriisissa 55555 ${matriisi[*]}
Nyt sain ajatuspähkinän että toimiiko tämä aina:
function onkomatriisissa () { [[ $(declare -p $2 ) =~ \"$1\" ]] && echo joo || echo ei ;}; matriisi=({1..100000}); onkomatriisissa 55555 matriisi
- siis matriisi passataan nimiparametrina - siis semmoisena jota BASH ei muka tunne.
***
Kun tuommoisia alkaa kerätä kirjastoonsa niin samalla voi nimetä ne uudestaan että funktiolla on sellainen nimi jonka muistat - ja voi niitä muutenkin hieman räätälöidä.
Kyllä niitä funktioita googlaamalla löytää. Ongelmana on ettei yksittäisestä funktiosta ole mitään iloa vaan niitä täytyy löytyä tuhansia - lajiteltuina ryhmiin, kaikkien toiminta pitäisi olla santapaista kuin muillakin samassa ryhmässä - ja ennenkaikkea omituiset omassa ryhmässään silla kaikessa on kyllä jotakin eikä niitä omituisia roskikseen pidä laittaa.
Olisikin tarve siihen että olisi paikka jossa olisi funktioita todella paljon. Ja kyllä niitä onkin - mutta nuo varastot on kaikki pilattu. Joko käyttäminen on ihan liian monimutkaista, vaikeaa ja tarkoitettu virtuooseille - tai funktioissa on muutamia hyviä mutta käsittämättömän huonoja niin runsaasti ettei niitä kultajyviä tahdo millään löytää.
******
Tietokoneen omienkin matematiikka-kirjastojen kehittäminen on vaatinut lukemattomia mies-työvuosia kymmeniltä loistavilta matemaatikoilta - nuo kehitystiedot on sijoitettu prosessorien matematiikka-yksikköiden langoitukseen ja mikrokoodiin, käyttöjärjestelmien kirjastoihin ja kielijärjestelmien kirjastoihin. BASH on ihan tietoisesti hankkinut vain rajoitetun pääsyn integer-kirjastoihin.
Sillä kaikki laskenta on jossain vaiheessa kokonaisluku laskentaa ja kirjaston tekeminen kokonaisluku-laskentana ihan mahdollista - ja koska BASH on opetuskieli niin oli opetuksen takia tarkoitus pistää käyttäjät tekemään itse desimaalikirjastonsa. Ja senaikaisilla käskyillä noista kirjastoista olisi tullut kohtuullisen nopeitakin - siellä millisekunnin nopeus-luokassa isotkin matalantason käskyistä tehdyt skriptit useimmiten ovat. Lisäksi noiden skriptien nopeus laskee vain vähän koodimäärän kasvaessa.
Mutta jostainsyystä kukaan ei silloin kauan sitten noita kirjastoja tehnyt ja kun BASH:in käskykantaa "parannettiin" olisi kirjastoista tullut uuslla käskyillä niin hitaita ettei niiden tekemisessä ollut mieltä - toimivathan ne vanhat käskyt edelleen mutta tietoisuus niiden olemassaolosta hiipui nopeasti.
Nyt on tilanne BASH:in kannalta niin toivoton ettei noiden kirjastojen kehittäminen enää paljoa kannata - BASH:in kyvyt kasvavat paljon mutta käyttäjäthän ovat jo kaikonneet eivätkä takaisin tule. Mutta saihan näistä yritelmistä ainakin sen tiedon että tässäkin asiassa BASH on telottu hengiltä levittämällä epätotuuksia - kovin on kirjastojen poissaolo omituista - niitä muuten oli aikoinaan mutta ne tuhottiin jollain ilveellä - jäljellä on tosin muutama omituinen. Ja on syytä muistaa se jokaisessa Ubuntussa oleva 85 funktion kirjasto jota vain käyttöjärjestelmä tietää käyttää. Varmaankin tuo kirjasto muistakin Linukseista löytyy käskyllä: declare -f. Sieltä muuten näkee senkin etteivät kehittäjät vierasta käskyä: eval.
***
Teoriassa laskut desimaaliluvuilla ovat yksinkertaisia silloinkin kun voi laskea ainoastaan kokonaisluvuilla: ennen laskua poistetaan se demaalipiste, sitten lasketaan ihan normaalisti jäljelle jääneellä kokonaisluvulla ja lopuksi se desimaalipiste palautetaan. Mutta vaikka tämä toimii käytännössäkin niin eteen tulee monia vaikeuksia:
Esimerkiksi desimaalipisteen palauttaminen oikeaan paikkaan on todellinen ongelma - lisäksi se on vielä pientä jos laskusi heittää kymmenertaisesti. Seuraavankaltaisissa pikkuhommassakin desimaalipisteen paauttaminen oikealle paikalle aiheuttaa päänsärkyä:
function tuplaa () {
luku1=$1"." # jos luvussa on tässävaiheessa kaksi desimaalipistettä ei se haitaa
kokonaisosa1=${luku1%%.*}
kokonaisosanpituus=${#kokonaisosa1}
luku1=${luku1//./};luku1=${luku1:0:18} # poistetaan ne desimaalipisteet olipa niitä 1 tai 2
tulo=$(($luku1*2))
[[ ${#tulo} -gt ${#luku1} ]] && kokonaisosanpituus=$(($kokonaisosanpituus+1))
tuloste=${tulo:0:$kokonaisosanpituus}.${tulo:$kokonaisosanpituus}
echo ${tuloste%*.} # ei tulosteta desimaalipistettä jos se on tuloksen viimeinen merkki
}
tuplaa 9.65438761111111112
Desimaalipisteen paikan kanssa joutuu kamppailemaan vaikka tekisi tuplaamisem tai puolittanisen bittisiirtoina (tuo: let "luku <<=1"):
function tuplaa () { [[ ${1//[^.]/} ]] && luku=$1"0"|| luku=$1".0"; desimaaliosa=${luku##*.}; desimaaliosanpituus=${#desimaaliosa}; luku=${luku//./}; let "luku <<=1"; apu=${luku: -$desimaaliosanpituus:$desimaaliosanpituus-1}; (( $apu )) && echo ${luku:0: -$desimaaliosanpituus}.$apu || echo ${luku:0: -$desimaaliosanpituus} ;}
function puolita () { [[ ${1//[^.]/} ]] && luku=$1"0"|| luku=$1".0"; desimaaliosa=${luku##*.}; desimaaliosanpituus=${#desimaaliosa}; luku=${luku//./}; let "luku >>=1"; echo ${luku:0: -$desimaaliosanpituus}.${luku: -$desimaaliosanpituus:desimaaliosanpituus-$((${luku: -1} == 0)) } ;}
# Tässä testaus:
puolita $(tuplaa 5555555555555.5555)
***
Mutta mikäli laskettavien joukkoon tulee toinenkin luku niin eteen tulee lukemattomia ongelmia lisää - esimerkiksi yhteenlasku onnistuu näin vain mikäli lukujen desimaaliosat ovat yhtäpitkät - jos ne eivät ole niin lyhyempään täytyy lisätä perään nollia niin monta että ne ovat yhtäpitkiä - sillä desimaalien perässä nollat eivät muuta mitään. Samallatavoin täytyyy tehdä jos desimaalilukuja vertailee. On yli- ja alivuodot - ja ongelmat niiden kanssa laajenevat paljon pyrittäessä suurempaan numeromäärään jolloin lukuja aletaan jakaa osiin ja niiden laskentatuloksia aletaan koota. Silloin on myös pahemmat merkkiongelmat ...
BASH tosiaan opettaa olemaan riemuitsematta kovin nopeasti - usein myöhemmin selviää ettei skripti ihan niin hyvä ollutkaan kuin oli luulo. Aina on parantamisen varaa - toisaalta se on katkeraa ja toisaalta elämän suola.
- kaikkein katkerinta on tieto siitä että jotkut ovat tienneet ikuisesti desimaalilaskujen onnnistuvan BASH:issakin oikein hyvin sillä kokonaisluvuilla desimaalilaskut aina lasketaan - siellä näyttämön takana jonne käytännössä harva kurkkii - mutta matematiikan teoreetikot ovat tienneet tämän aina - se on asiaan kuulumatonta että se on todellinen miinakenttä. BASH:issa kyky laskea desimaaleilla on nykyään merkityksetöntä mutta aikoinaan se olisi ollut erittäin merkittävää ja ihan yhtä mahdollista kuin tänäänkin.
***
Desimaalilukujen kanssa toimiessa olisi usein tarpeen poistaa merkityksettömät etunollat kokonaisosasta ja takanollat desimaaliosasta - ja poistaa myös desimaalipiste mikäli se jäisi luvun viimeiseksi merkiksi. Yksi kammottava funktio tekee sen nopeasti ja varmasti:
function siisti () { apu=$1; merkki='';[[ ${apu//[^-]/} ]] && merkki=- && apu=${apu:1}; apu=${apu%00000000000000};apu=${apu%0000};apu=${apu%00};apu=${apu%0};apu=${apu#00000000000000};apu=${apu#0000};apu=${apu#00};apu=${apu#0};apu=${apu%.}; echo $merkki$apu ;}
# käsky kokeilemiseksi:
apu="-000120340.0400";echo -n "Luku alunperin: $apu . Ja käsittelyn jälkeen: "; siisti $apu
***
Aloin tutkia Taylorin sarjoja ja kestää tovi ennenkuin tuloksia tulee - teorioiden toimivuus on kyllä tarkistettu jo muttaa kerkiänkö remontin takia tekemään toimivaa skriptiä jää nähtäväksi. Silläaikaa täytyy puhua pehmoisia:
BASH:issa on kyllä samantapaiset muuttujien määrittelyt kuin muissakin kielissä mutta eipä niitä näissä pikkuhommissa tarvita - miksi suotta sotkea koodia? Mutta joidenkin mieletä ne päinvastoin selkeyttävät koodia ja ovathan ne joskus ehdottoman tarpeellisiakin. Joten on syytä tietää että niitäkin on:
1. Funktiossa olevat muuttujat näkyvät pääohjelmassakin. Joskus funktio silloin muuttaa tarkoittamattaan pääohjelman muuttujia. Tällöin funktiossa niille härikkö-muuttujille määrätään: local muuttujan_nimi
- joskus täytyy peräti siirtyä funktiossa omaan prosessiin jolloin mikään ei varmasti vaikuta pääohjelmaan ellei nimenomaan käsketä. Tämä tapahtuu muuttamalla funktiokutsun aaltosulut kaarisuluiksi.
2. Joskus halutaan painottaa sitä että muuttuja on kokonaisluku. Tällöin määrätään:
declare -i muuttujan_nimi (=integer).
3. joskus on tarpeen määrätä että muuttuja onkin vakio: declare -r muuttujan_nimi=arvo (=readonly)
- siis arvo täytyy antaa määrittelyn yhteydessä.
4. joskus halutaan painottaa sitä että muutuja on matriisi-> declare -a muuttujan_nimi
5. jos matriisi on assosiatiivinen jolloin se täytyy määritellä: declare -A muuttujan_nimi (Assosiatiivisen matriisin osoite on tekstiä - jos siinä on numeroita niin tekstiksi nekin käsitetään).
. ja on määreitä muitakin:
https://linuxcommand.org/lc3_man_pages/declareh.htmlOnhan BASH:in muuttujat mukavia käyttää kun ainoastaan assosiaiatiivinen matriisi täytyy määritellä. Vaan on sillä varjopuolensakin - esimerkiksi muuttuja on aina myös saman-nimisen matriisin ensimmäinen jäsen ja usein kun luulet toimivasi numeroilla matriisin kanssa toimitkin itseasiassa tekstijono-muuttujan kanssa.
Voit toki määritellä interger-matriisinkin. Silloin ongelmat ovat toisenlaisia - käskepä:
declare -ai apu; apu[5]=kattokassinen; echo ${apu[5]} -> tulostuu 0 - ellei sattumalta muualla skriptissä ole muuttujaa nimeltä kattokassinen ja sillä numero-arvo.
Samoin mukavaahan se on kun ei tarvitse välittää onko muuttuja tekstiä vai numero - lukuunottamatta tilannetta jossa nimenomaan määräätään muuttujan olevan numero. Noilla integer- muutujilla on muuten automaattinen laskenta - käskepä: declare -i apu; apu=1+2; echo $apu -> tulostuu 3 - mutta RAM-muistissa se silti on:1+2
Ja mukavaa on sekin ettei välttämättä tarvitse antaa muutujalle arvoa ennenkuin sitä käyttää - sen arvo on silloin 0. Mutta toisaalta tämä tekee sen että jos esimerkiksi toisessa skriptissä onkin annettu saman-nimiselle muuttujalle jokin arvo pysyy määrittely voimassa ja yhtäkkiä tämänhetkisen skriptin muuttujalla onkin joku kummallinen alkuarvo. Tämän takia skriptin alkuun kehoiteaan laittamaan: set -u
***
BASH olettaa että asiat on määritelty siinävaiheessa kuin niitä yritetään käyttää - elikä eteenpäin viittauksia ei sallita. Tämän kiertämiseksi on lukemattomia konsteja mutta koodi pysyy yksinkertaisempana kun ei konsteja suotta harrasta - konsteista täytyy saada jotain pätevää hyötyä - ja sitä ei voi sanoa hyödyksi että haluaa BASH:in toimivan kuin C.
BASH on tuomittu ohjelmointikielenä vaika eihän BASH:ia ole ohjelmointiin tarkoitettukaan vaan ulkoisten ohjelmien yhteen-nivomiseen - BASH:in toimiminen opetuskielenäkin on tavallaan sivujuonne sillä kyllä me kaikki oppia kaipaamme. On BASH:illa ohjelmoiminen silti mielestäni mukava harraste ja nuo desimaalilaskuni olivat vain yksi osoitus siitä että BASH:ista puhutaan suupielet ruskeina kaikenlaista.
Enkä usko etteivät virtuoosit ole tienneet että BASH osaa kaikenlaista - esimerkiksi funktiot ovat tosiaan BASH:in toimimisen perusta ja kun funktioita ei käytetä ja peräti väitetään ettei funktio-nimiprametreja tunneta niin kyllä siinä kieli hiljokseen tikahtuu. Joten ei BASH toisten puheisiin ja luuloihin tainnut kuolla vaan kyllä syy oli sisäsyntyinen - virtuoosit tappoivat oman kielensä.
Sillä on aika rakentaa ja on aika rikkoa. Se ei ole meidän oma valintamme vaan meidän jokaisen elämä tapahtuu noin halusimme tai emme. Nuorena alamme jossain vaiheessa rakentaa ja jossain vaiheessa vanhuutta alamme taas rikkoa sitä mitä olemme rakentaneet - rikkomistamme yleensä edes tajuamatta.
***
Yksi asia mitä virtuoosit eivät painota enkä minäkään ole huomannut painottaa vaikka se kuuluisi kertoa jo ennen alkeita: BASH:in tietorakenteet ovat dynaamisia elikä niiden koko määräytyy vasta käytönaikana - esimerkiksi matriisia ei tarvitse edes ilmoittaa ja silti voidaan määritellä ensin matriisin miljoonas jäsen ja seuraavaksi jäsen 222 ja jättää kaikki muut määrittelemättä ja silti se on täysin kelvollinen matriisi. Sama on tekstijonon kanssa - sen koolla ei ole ylärajaa jaa se voikin kasvaa ikuisesti vaikka sen olemassaolosta ole koskaaan ede kerrottu - se vain on olemassa ja alussa sen arvo on tyhjä.
Voit siis aivan rauhassa tulostaa jotakin josta ei ole koskaan puhuttu - sillä ei vaan ole arvoa joten tulostuu tyhjää. Samoin jos käytät määrittelemätönta niin se ei ole varsinaisesti virhe vaan tuon määrittelemän matemattiseksi arvoksi oletetaan nolla ja tekstuaaliseksi arvoksi tyhjä. Tottakai tuommoisella on myös varjopuolensa ja sen virtuoosit kyllä muistavat kertoa.