Kirjoittaja Aihe: Ohjeita shell-skriptaukseen (bash)  (Luettu 376438 kertaa)

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #260 : 09.10.19 - klo:08.50 »

Ensin rajoittamattoman monen rajoittamattoman moninumeroisten luvun yhteenlaskin jossa on pari funktiota ja neljä looppia. Tätä ei ole suoranaisesti tarkoitettu käytettäväksi vaan se on tehty funktiokutsujen ja looppien nopeuden varmistamiseksi. Lopussa kuvatulla laskulla skriptin läpikäyminen vaatii noin 150:n monipuolisen käskyn suorittamisen elikä noin 3ms/150=20 mikrosekuntia per käsky -> siis kaikki sen käskyt ovat nopeita.
Koodia: [Valitse]
function radd () { 

function desimaalisumma () { for((j=1;j<=$2;j++)); do summa=$((summa+${desimaaliosa[j]:$1:1})); done; [[ $muistinumero ]] || muistinumero=0;summa=$((summa+$muistinumero)); muistinumero=0 ;}

function kokonaissumma  () { for((j=1;j<=$2;j++)); do summa=$((summa+${kokonaisosa[j]:$1:1})); done; [[ $muistinumero ]] || muistinumero=0; summa=$((summa+$muistinumero)); muistinumero=0 ;}

i=1; maxdesimaalit=0; maxkokonaiset=0; muistinumero=0; for n in $@; do desimaaliosa[i]=${n##*.}; desimaaliosanpituus[i]=${#desimaaliosa[i]}; (( ${desimaaliosanpituus[i]} >= $maxdesimaalit )) && maxdesimaalit=${desimaaliosanpituus[i]}; kokonaisosa[i]=${n%%.*};  kokonaisosanpituus[i]=${#kokonaisosa[i]}; (( ${kokonaisosanpituus[i]} >= $maxkokonaiset )) && maxkokonaiset=${kokonaisosanpituus[i]}; (( i++ )); done

i=1; for n in $@; do desimaaliosa[i]=${n##*.}"000000000000000000"; desimaaliosa[i]=${desimaaliosa[i]:0:$maxdesimaalit};
kokonaisosa[i]="000000000000"${kokonaisosa[i]}; kokonaisosa[i]=${kokonaisosa[i]: -$maxkokonaiset};
echo ${kokonaisosa[i]}.${desimaaliosa[i]}; (( i++ )); done 

desimaalit=''; for((i=maxdesimaalit-1;i>=0;i--)); do summa=0; desimaalisumma i $# ; desimaalit=${summa: -1}$desimaalit; muistinumero=${summa:0: -1}; done; # echo $desimaalit' '$muistinumero; read

kokonaiset=''; for((i=maxkokonaiset-1;i>=0;i--)); do summa=0; kokonaissumma i $# ; kokonaiset=${summa: -1}$kokonaiset; muistinumero=${summa:0: -1}; done; echo $muistinumero$kokonaiset.$desimaalit
}

time radd 1.17 22.222 3.33 4.4


petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #261 : 11.10.19 - klo:19.27 »
Skriptin toiminnan tutkimiseksi siihen kannattaa skriptiä tehdessä laittaa tulostus lähes kaikkelle mahdolliselle. Mutta ennenkaikkea niitä tulostuskäskyjä ripotellaan koodiin jonka toimintaa ei omalla logiikallaan hallitse ja skripitin toimintaa korjataan kokoajan. Toiminta on tällaista kaikilla kielillä.

Mutta välillä nuo välitulosteet sotkevat joten ne poistetaan ja hetkenkuluttua taas palautetaan. Melkoinen homma jos ei tee noista tulostamisista ehdollista joten yhdellä muuttujalla voi säätää tulostetaanko kaikissa miljoonassa paikassa vai ei. Mutta ne tulostusehdot ovat kookkaita ja skriptin rakenne vääristyy.   

Mutta BASH:issa ei tarvita ehtoa. Esimerkki kertonee parhaiten kuinka tehdään: skriptin alkuun kirjoitetaan:  tulosta=echo. Ja loppuskriptin echo:t korvataan muuttujalla $tulosta. Esimerkiksi:
tulosta=echo; $tulosta apua -> tulostaa: apua.

- skriptin merkityksellisin echo kyllä jätetään.
- kun ei enää haluta välitulosteita niin muuttujalle: tulosta annetaan arvo : (siis kaksoispiste)
- voi muuttujalle: tulosta  yhtähyvin antaa arvon: 'printf %s\n'
- bacup tai mikä muu hyvänsä hoituu samallatavoin.
- se siitä ensimmäisenluokan funktiosta jota BASH:issa ei ole.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #262 : 13.10.19 - klo:09.42 »
Windows alkoi muutamavuosisiten liittämään BASH:ia koodiinsa. Olettekos ihmetelleet miksi tehdä hyvin kallis lisäys toiminnasta jota Linux-yhteisössä pidetään surkimuksena?

Johdatteleva kysymys: millä saralla Linux on ehdoton kunkku? Haluaisivatkohan muut osille noissa super-tietokoneissa? - ei se raha vaan maine. Valitettavasti taitavat onnistua kun ottaa huomioon kuinka Linux-virtuoosit BASH:ia kohtelee. Sillä BASH on surkimus ja samalla kuningaspultti.

BASH-skriptit ovat koneen hoitamisessa erinomaisia mutta harva enää nykyään konettaan hoitaa - se on noidankehä joka kokoajan kiihtyy ja päätyy orjan elämään kun kaikessa täytyy luottaa toisiin. Siis nimenomaan täytyy, ei voi valita sitä että tekisi jotain itse.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #263 : 20.10.19 - klo:17.23 »

BASH:issa on useampiakin keinoja laskea e (se luonnollisen järjestelmän kantaluku joka on arvoltaan 2.71828....). Tässä annetuilla työkaluilla sen arvosta saa 16 ensimmäistä numeroa. Ei tällä tietenkään ole muuta käyttötarkoitusta kuin että käyhän tämäkin leikkikalusta ja sainhan tätä tehdessäni hihitellä BASH:ista esitetyille väitteille. Itseasiassa vain tuo viimeinen rivi on uutta - funktiothan on esitetty jo aikaisemmin. Aikaa kuluu e:n arvoa etsittäessä noin 70ms

e:n arvo saadaan laskulla: 1/0!+1/1!+1/2!+1/3!+1/4! ...  Ja sen laskemistahan tuo viimeinen lause ohjaa.

- ja nimenomaan se pännii että tämä olisi toiminut aina, jo silloin aikoinaan kun BASH:ia alettiin kelvottomaksi väittää.

Koodia: [Valitse]
function jaa () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"0000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"0000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}

luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliosa1 #; echo $desimaaliosa1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliosa2 #; echo $desimaaliosa2
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
kokonaiset=$kokonaiset.${desimaalit: -9}
# echo $jakojaannos
jakojaannos=$(((100000000*$jakojaannos)%$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=0000000000000000$desimaalit
echo $kokonaiset${desimaalit: -8} ;}     

function yhteenlasku () { # voi käyttää vähennyslaskuunkin
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=$1
luku2=$2
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
(( $m2 +1 )) && luku=$((10#$kokoluku1+10#$kokoluku2)) || luku=$((10#$kokoluku1-10#$kokoluku2))
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}

summa=2; for n in {2..18}; do summa=$(yhteenlasku $summa $(jaa 1 $(($(seq -s* $n))))); done; echo $summa

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #264 : 26.10.19 - klo:09.44 »
Desimaalimatematiikan suorittaminen BASH:in omilla käskyillä vaatii runsaasti aputoimintoja. Useat aputoiminnot ovat niin nopeita että kun niistä kasaa funktion niin se ei hidasta havaittavasti. Joten niitä funktioita kannattaa tehdä - jos ei muusta syystä niin virtuooseja kurmuuttaakseeni sillä eihän näillä funktioilla toistaiseksi muuta merkitystä ole. Esimerkiksi:
Koodia: [Valitse]
function desimaaliosanloppunollatpois () {
kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; [ ${#desimaaliosa} -eq ${#1} ] && { echo $1; return ;}; desimaaliosa=.$desimaaliosa; while [[ ${#desimaaliosa} && ${desimaaliosa: -1} == [.0] ]]; do desimaaliosa=${desimaaliosa:0: -1}; done; echo $kokonaisosa$desimaaliosa ;}

luku=10.0102000; desimaaliosanloppunollatpois $luku


petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #265 : 29.10.19 - klo:06.30 »
Päivitin neliöjuuren laskemis-skriptin, vaikkei sillä toistaiseksi olekaan muuta tarkoitusta kuin osoitaa että BASH:in matematiikkaa haukutaan osittain väärin perustein. Ja kukatietää mihin päädytään jatkuvien oleellisten parannuksien kanssa - nämäkin parannukset nopeuttivat kaksinkertaisesti. Ehkä BASH:in matematiikastakin tulee edes siedettävää?

Skripti laskee neliöjuuresta vain 8-9 numeroa mutta sen se tekee parhaimmillaan nopeammin kuin jos laskettaisiin neliöjuuri bc:ssä - elikä laskenta kestää 2ms:a.

Mutta neliöjuuren laskemiseen tätä ei ole tehty vaan virtuoosien toimien ihmettelyyn. Sillä nyt kun skriptillä on sujuvampi toiminta on varmaa että ihmettelyyn on aihetta. On mahdotonta uskoa että maailmanluokan virtuoosit eivät olisi tunteneet desimaalilaskennan saloja ikuisesti.

Jossain vaiheessa kiinnitän huomioni logaritmeihin. Niihinkin BASH ilmanmuuta kykenee olkoonkin etteivät BASH:in toistaiseksi selvinneet kyvyt kovin pitkälle riitä.
Koodia: [Valitse]
function sqrt () {
declare -i seed 
declare -i delta # nämä declare-määritelyt mahdollistivat koodimuutoksia ja normaalin-näköisen matematiikan. Koodi nopeutuikin melkein puolella.

function desimaalipiste2oikealle () { # juurrettava on <0
desimaaliosa=${1##*.}; # kokonaisosaahan ei voi ollakaan jos tänne on tultu.
(( ${desimaaliosa:0:1} )) && echo ${desimaaliosa:0:2}.${desimaaliosa:2} || echo ${desimaaliosa:1:1}.${desimaaliosa:2} ;}               

function  desimaalipiste2vasemmalle () { # juurettava on >100
kokonaisosa=${1%%.*}; desimaaliosa=${1##*.}
echo ${kokonaisosa:0: -2}.${kokonaisosa: -2}$desimaaliosa ;}
 
luku=$1 # funktio sqrt:n koodi alkaa
[[ $luku == 1 ]] && echo 1 && return
[[ ${luku:0:1} == . ]] && luku=0$luku
[[ ${luku//./} == $luku ]] && luku=$luku".0" || luku=$luku'0' # luvussa täytyy olla desimaalipiste
desimaalisiirto=0
while (( ${luku%%.*} <= 0 )); do luku=$( desimaalipiste2oikealle $luku) && (( desimaalisiirto-- )); done
# echo $desimaalisiirto' '$luku
while (( ${luku%%.*} >= 100 )); do luku=$(desimaalipiste2vasemmalle $luku) && (( desimaalisiirto++ )) ; done #; echo $desimaalisiirto' '$luku # toiminnantarkastamisessa hyvä vihje
in=${luku//./}"0000000000000000000"
in=${in:0:17}
seed=2010000000  # maksimi seed olisi 4294967295
delta=1005000000
apu=${luku%%.*}
(( ${#apu} == 1)) && {
for (( n=delta; n>=1; n=n/2 )); do [[ $seed*$seed -gt $in ]] && seed=seed-n || seed=seed+n; done ;} || { for (( n=delta; n>=1; n=n/2 )); do [[ $seed*${seed::-1} -gt $in ]] && seed=seed-n || seed=seed+n; done ;}
case  1:${desimaalisiirto:--} in # tulostus
($((desimaalisiirto<0))*) echo ${seed:0:1}.${seed:1}e$desimaalisiirto ;;
($((desimaalisiirto>8))*) echo ${seed:0:1}.${seed:1}e$desimaalisiirto ;;
                       *) echo ${seed:0:desimaalisiirto+1}.${seed:desimaalisiirto+1} ;;
esac ;}


# tarkistukset: sqrt 2.3456   # pitää tulla: 1.53153519
#               sqrt 23.456   # pitää tulla: 4.84313949
#               sqrt 234.56   # pitää tulla: 15.3153519
#               sqrt 0.023456 # pitää tulla: 1.53153519e-1
#               sqrt 1234567800000000000  # pitää tulla: 1.11111105e9
#               sqrt .000000000012345678  # pitää tulla: 3.51364171e-6

***

Desimaalimatematiikan skripteissä on usein monta funktiota. Kun joku funktio höpertää menee koko lasku metsään. Olisi helppo selvittää mikä funktio on syypää mikäli funktiot kirjoittaisivat näytölle välituloksiaan.

Mutta koska parametrit normaalisti siirretään näytön kautta menee vastaanottaja sekaisin kun ei erota mikä on välitulosta ja mikä parametria. Juuri tästä syystä haukutaan ettei BASH osaa parametrejaan palauttaa - mutta haukutaan syyttä kun ei haluta käsittää että parametrien palautustapoja on useampia.

Tässä parametrit siirretää suurinpiirtein samallatavoin kuin muissakin kielissä:
Koodia: [Valitse]
function koe () { apu=$(declare | grep ^$1=); muuttuja=${apu##*=}; muuttuja=$((2*muuttuja)); echo höh; read $1 < <(echo $muuttuja) ;}

# funktiokutsu:
luku=2; koe luku; echo $luku
selvitystä muuttujista:
apu=$(declare | grep ^$1=)  hakee BASH:in muutujalistasta muuttujaa vastaavan declare-lauseen.
                            Muuttujalista on nimittäin sama oltiinpa funktiossa tai pääohjelmassa.
                            Tällätavoin funktioon voidaan toimittaa haluttu määrä parametreja, myös
                            matriiseja. Samanaikaisesti voidaan käyttää sitä vanhaakin tapaa.
muuttuja=${apu##*=}         ottaa muuttujan arvon declare-lauseesta yhtäkuin-merkin perästä.
muuttuja=$((2*muuttuja))    kerrotaan muutuja kahdella jotta pääohjelmassa näkisi muuttujan muuttuneen.
read $1 < <(echo $muuttuja) muutetaan muuttujalistaa. Read, let tai readarray on pakollinen.
echo höh                    osoitus siitä ettei näytölle kirjoittaminen sotke laskua.
apu                         apumuuttuja jolla ei ole myöhemmin mitään merkitystä.
 
- parametri on tyypiltään nimiparametri eikä arvoparametri jollaisia BASH:in funktioissa on totuttu käyttämään. Muutujalle ei pidä laittaa funktiossa local-määrittelyä.
- palautettava parametri saa olla mitä tyyppiä hyvänsä, vaikka matriisi.
- voidaan palauttaakin niin monta parametria kuin halutaan ja mitä tyyppiä halutaan, myös matriiseja.

Hankalaahan tuo on, mutta yleensä tätä parametrien palautustapaa ei tarvitse käyttää.
- tuon declare-kompleksin sijaan voi kirjoittaa: eval apu=\$$parametrinumero. Silloin ei tarvita lausetta: apu=${apu##*=}
- jos pilkkua viilataan niin parametria ei palauteta vaan määritetään sen arvo globaalialueella.
- asia on esitetty aikaisemminkin mutta nyt se on esitetty täysin uudesta näkökulmasta.
« Viimeksi muokattu: 02.11.19 - klo:15.35 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #266 : 12.11.19 - klo:20.33 »
BASH:issa on aivan varmaa että kun joku tekee jostain asiasta hitaan satarivisen skripti-hirviön niin joku tekee samasta asiasta muutaman rivinpituisen nopeasti toimivan toteutuksen ja hirviön tekijä saa trauman loppuiäkseen. Mutta jos tätä ei hyväksytä niin BASH:ia ei kukaan uskalla kehittää - sillä niitä lyhyitä ja nopeita versioita ei tehdä ensiksi sillä vain mikäli tuo hirviöskripti toimii uskotaan etteivät puheet BASH:in kyvyttömyydestä pidä paikkaansa ja kannattaa yrittää - tai eihän nykyään enää yritetä sittenkään koska väitteet BASH:in kyvyttömyydestä on omaksuttu liiankin hyvin.

***

Valmistautumista logaritmin laskemiseen - tämä on se hidas hirviö jonka täytyy toimia ennenkuin on mahdollista tehdä se nopea versio - vaikka eihän BASH:ista saa edes kohtuunopeaa matemaattisissa operaatioissa.

Kymmenkantaista logaritmia laskettaessa suurin haaste on logarotmoitavan desimaaliluvun korottaminen potenssiin kymmenen. BASH:issa kymmenpotenssiinkorotus täytyy toteuttaa kertomalla luku itsellään kymmenenkertaa sillä merkkiluku eksponentiaatiossa on paljon pienempi. BASH:in oman kertolaskunkaan numeromäärä ei ole riittävä vaan tarvitsee käyttää kaksinkertaisen numeromäärän desimaalilukujen kertolaskuskriptiä. Kymmenpotenssiin korottaminen tällätavoin kestää noin 65ms ja lopputuloksessa on noin 14 oikeaa numeroa:
Koodia: [Valitse]
#!/bin/bash
# Kertolaskussa kumpikin jäsen saa olla 17numeroinen ja tulokseen voi tulla 34numeroa. Myös etumerkki
# hyväksytään niinkuin myös desimaalit ja silti toimintanopeus on 2ms. Onhan se pitkä aika yhdelle
# kertolaskulle, mutta onnistuu sentään. Tätä versiota on korjailtu runsaasti.

function kerro18 () {
tulosta=: # vaihtoehdot: tulosta=echo ja tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
summa2=$(($m1*$m2*10#$summa2))
(( ! summa2 )) && summa1=$(($m1*$m2*10#$summa1))
apu=$summa2$summa1; (( $desimaaleja )) && echo $((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} > /tmp/joo || { echo $(( 10#$summa2 ))$summa1 ;} ;}

# Potenssiin korotus on helpointa testata seuraavalla skriptillä: se tulostaa jokaisella kierroksella kertolaskusta saadun tuloksen ja seuraavalla rivillä bc:n varmasti oikean tuloksen. Muista laittaa potenssiin korotettava täysin samanlaisena kahteen paikkaan:
time { echo 1 > /tmp/joo; for n in {1..10}; do kerro18 9.87654321 $(cat /tmp/joo); cat /tmp/joo;
bc -l <<< "9.87654321^$n"; echo; done ;}

kamara

  • Käyttäjä
  • Viestejä: 3028
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #267 : 13.11.19 - klo:08.46 »
petteriIII on ominut tämän säikeen lähes kokonaan, niin laitetaan nyt itsellenikin tarpeellinen scriptin pätkä. En tosin ole sitä testannut kaikilla (ääri)arvoilla.

Tämä tasaa horisontin kuvasta imageMagick:n ja bc:n avulla määrittelemällä kaksi pistettä, jotka ovat horisontin koordinaatissa.
Koodia: [Valitse]
$ cat fixhorizon.sh
#!/bin/bash

if [ "$#" -ne 6 ]
then
echo "use: ./fixhorizon.sh fromImg x1 y1 x2 y2 toImg"
exit
fi
pi=`echo "h=20;4*a(1)" | bc -l`
echo $pi
x=`echo "h=20;$4-$2" |bc -l`
echo x
y=`echo "h=20;$5-$3" |bc -l`
echo y
if [ $x -eq 0 ]
then
if [ $y -gt 0 ]
then
angle=90
else
angle=-90
fi
else
angle=`echo "h=20;-1*a($y/$x)*180/$pi"|bc -l`
fi
echo $angle

convert -rotate "$angle" $1 $6

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #268 : 15.11.19 - klo:13.11 »
Sanallinen esitys kymmenkantaisen logaritmin laskemisesta:
- tässä skriptissä alkuoletus on että logaritmoitava on yli 1.
- kun puhutaan numerosta niin tarkoitetaan yhtä merkkiä: 1, 2, 3, 4, 5, 6, 7, 8, 9 tai 0.
- toiminta ei muutu mitenkään vaikka logaritmoitava olisin kokonaisluku ilman desimaaleja. 
- aluksi logaritmi nollataan. Sitten logaritmin ensimmäiseksi numeroksi tulee "logaritmoitavan kokonaisosan numeroiden lukumäärä -1" ja perään tulee desimaaipiste.

Sitten logaritmiin lisätään numeroita yksi kerrallaan niin monta kertaa kuin tulokseen halutaan desimaaleja: logaritmin seuraava numeron selvittämiseksi lasketaan uusi logaritmoitava siirtämällä desimaalipiste  vanhassa logaritmoitavassa ensimmäisen numeron perään ja korottamalla saatu luku potenssiin kymmenen. Logaritmin seuaava numero on "logaritmoitavan kokonaisosan numeroiden määrä -1". Uudesta logaritmoitavasta tulee vanha logaritmoitava. Palataan kohtaan: Sitten logaritmiin lisätään ...

Koodina tämä on:
Koodia: [Valitse]
#!/bin/bash
function kerro18 () {
tulosta=: # vaihtoehdot: tulosta=echo ja tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
summa2=$(($m1*$m2*10#$summa2))
(( ! summa2 )) && summa1=$(($m1*$m2*10#$summa1))
apu=$summa2$summa1; (( $desimaaleja )) && echo $((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || { echo $(( 10#$summa2 ))$summa1 ;} ;}

function getlog ()  { # varsinainen logaritmointi
apu=${1//,/.}
[[ ${apu//[^.]/} ]] && vanhalogaritmoitava=$1 || vanhalogaritmoitava=$1".0"
logaritmoitavankokonaisosa=${vanhalogaritmoitava%%.*}
logaritmi=0; logaritmi=$((${#logaritmoitavankokonaisosa}-1)).
# sitten logaritmiin lisätään numeroita yksi kerrallaan mikäli siinä ei vielä ole tarpeeksimontaa numeroa:
until (( ${#logaritmi} >= 19 )); do
  apu=${vanhalogaritmoitava//./}; apu=${apu:0:1}.${apu:1}; # echo apu:$apu
  uusilogaritmoitava=1.0;
  for n in {1..10}; do uusilogaritmoitava=$(kerro18 $apu $uusilogaritmoitava); done
  # echo uusilogaritmoitava:$uusilogaritmoitava; read
  uudenlogaritmoitavankokonaisosa=${uusilogaritmoitava%%.*}
  logaritmi=$logaritmi$((${#uudenlogaritmoitavankokonaisosa}-1))
  vanhalogaritmoitava=$uusilogaritmoitava
done
echo luvun: $1 '  logaritmi on: '$logaritmi ;}

getlog 2.0000001234


Pitäisi tulla: luvun: 2.0000001234   logaritmi on: 0.301030022459
#                                                           tulee: 0.301030022459 


Onhan tämä suunnattoman hidasta eikä tarkkuuskaan riitä kuin tusinaan numeroita. Mutta se on osoitus siitä mihin BASH kykenee 19-numeron kokonaislukukertolaskullaan ja ennenkaikkea osoitus ettei pidä sanoa: BASH ei osaa .....

Jokaisen skriptin kykyjen huonouteen pätee: kun joku esittää jotakin niin täysin varmasti jokutoinen pistää paremmaksi. Tai nykyäänhän se ei enää pidä paikkaansa koska virtuoosit ovat saaneet kaikki uskomaan että BASH on paitsi kelvoton niin myös kyvytön.
« Viimeksi muokattu: 15.11.19 - klo:13.35 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #269 : 19.11.19 - klo:09.11 »
Vaikka juuri koskaan ei saa aikaiseksi BASH-skriptiä joka ei alussa hölmöilisi niin ne hölmöilyt saa aina loppumaan. Olenkin aina kuvaillut tulevaa skriptiä ennenkuin oleellisimpia funktioita on tehty - ja jokakerran BASH saa asiat toimimaan. Toivottoman hidashan BASH on mutta kyllä BASH:illakin koodaten hautaan kerkiää - sitäpaitsi olen huomannut että kun teen uudestaan skriptin joka aikoinaan kesti sekunteja niin nyt se kestää millisekunteja. 

***   

Nyt antilogaritmin kimppuun:
 
"antilogaritmin peukalosääntö": luvun 10 logaritmi on 1; kääntäen: luvun 1 antilogaritmi on 10
- antilogaritmia tarvitaan esimerkiksi kun lasketaan: luku^murtoluku:
esimerkiksi 2^2.1  lasketaan: antilogaritmi $( 2.1*$(logaritmi 2))
- mutta varsinainen syy tämän skriptin tekemiseen on testata desimaalilaskentaa ja oppia sen sääntöjä.

Antilogaritmin laskemisessa erikoisinta on siinä oleva funktio luvun puolittamiseen sillä sen täytyy ottaa nopeasti huomioon:
- puolitettava on aina joko kokonaisluku tai desimaaliluku typiltään: x.xxxxxxxxxxxxxxxxxxxxxx jossa x kuuluu joukkoon 0-9 tai tyhjä.
- joten siinä tulee eteen kummankin tyypin oktaaliansa: kokonaisluku saattaa olla nolla ja oktaaliansa laukeaisi jos sitä ei väistelisi; laskettaessa poistaisi ja tulostettaessa palauttaisi.
- mutta mikäli desimaaliosankin alussa on nollia niin tätä käänteistä oktaaliansaa ei edes voi poistaa. Jotta ei jouduttaisi oktaaliansaan lisätään desimaaliosan eteen luku 2 ja tulostettaessa se poistetaan - silloinhan se on edelleen tulostettavan alussa ja sen arvo on 1.

puolittamisen toteuttava funktio:
Koodia: [Valitse]
#!/bin/bash
function puolita () {
[[ ${1//[^.]/} ]] && luku=${1:0:18} || luku=${1:0:18}".0"
luku=${luku//0./.}
desimaaliosa=${luku##*.}
kokonaisosa=${luku%%.*}
kokonaisosanpituus=${#kokonaisosa}
(( $kokonaisosa )) && etunolla='' || etunolla='0'
kokonaisosa='2'$kokonaisosa # desimaaliosasta on tullut kokonaisosa
lukuilmandesimaalipistetta=$kokonaisosa$desimaaliosa
 
kokonaiset=$(($lukuilmandesimaalipistetta/2))
jakojaannos=$(($lukuilmandesimaalipistetta%2))
desimaalit=$(((1000000000*$jakojaannos)/2))
desimaalit=000000000000000$desimaalit
kokonaiset=$etunolla${kokonaiset:1}${desimaalit: -9}
 
echo $(( 10#${kokonaiset:0:$kokonaisosanpituus} )).${kokonaiset:$kokonaisosanpituus} ;}
« Viimeksi muokattu: 03.01.23 - klo:14.53 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #270 : 22.11.19 - klo:08.33 »
Puolitus-funktiosta on tullut jo kaksi uutta versiota sillä vanhat versiot toimivat joissain erikoistilanteissa väärin. Desimaalilaskennan osalta en edes yritä laskea versioita. Jokaisessa skriptissä on varaa parantaa elikä tehdä siitä uusi versio. Joskus uusi versio on hitaampi mutta joskus myös nopeampi kuin vanha versio - ja koodi yksinkertaistuu melkein aina.

BASH:in desimaalilaskenta toimi jo ennenkuin bc:tä awk:ia oli. Mutta koska virtuoosit uskottelivat ettei BASH desimaalilaskentaan kykene niin BASH:ia ei enää edes kehitetty "koska matematiikka toimii kelvottoman huonosti". Ja kun kieltä ei kehitetä niin käytännössä se taantuu. Todellisuudessa BASH on "vähänumeroisissa nelilaskintyyppisissä desimaalilaskuissa"  huomattavasti nopeampi kuin mikään ulkoinen ohjelma kutem esimerkiksi bc tai awk.

Kehittyneemmissä laskuissa ja varsinkin "antilogaritmin laskemis-tyyppisissä" BASH on laskennallisesti erittäin hidas. Silti noita laskuhirviöitä kannattaa tehdä sillä ne eivät toimisi jos missään olisi ongelmia: jokaisen funktion ja funktioiden välisen tiedonsiirron tulee toimia moitteetta eivätkä funktiot saa häiritä toisiaan. Siis jos nuo laskuhirviöt toimivat niin se takaa laadun.

Sekin alkaa näkyä että vaikka BASH:in merkinnät ovat tyystin erilaisia kuin muissa kielissä niin ajatukset ovat samoja elikä ongelmat on kyllä tunnettu jo aikoinaan.

***

Antilogaritmin laskemiseksi ei ole samantapaista menetelmää kuin logaritmin laskemiseksi, joten antilogaritmin arvo on etsittävä binäärihaulla vertaamalla lukuarvion logaritmia annettuun lukuun.

Selvitettävää numeroa kohti binäärihaku täytyy suorittaa noin kolmesti joten pyrittäessä tarkkuuteen 14 numeroa binäärihakuja tulee noin 47 elikä homma kestää peruskoneella noin 6-16 sekuntia. Tajuttoman pitkään siis, mutta verrattuna siihen ettei se toimisi ollenkaan on ero valtava. Sitäpaitsi mikäli BASH:iin saisi pieniä kielellisiä parannuksia toisivat ne nopeutta tuhatkertaisesti.

Esimerkkinä antilogaritmista lasketaan minkä luvun logaritmi on: 0.30102999566398119:
Koodia: [Valitse]
  #!/bin/bash
function puolita () {
[[ ${1//[^-]/} ]] && merkki=- || merkki=''
luku=${1//-/}
[[ ${luku//[^.]/} ]] && luku=${luku:0:18} || luku=${luku:0:18}"."
[[ ${luku:0:1} == '0' ]] && luku=${luku:1}
desimaaliosa=${luku##*.}
kokonaisosa=${luku%%.*}
kokonaisosanpituus=${#kokonaisosa}
kokonaisosa='2'$kokonaisosa
lukuilmandesimaalipistetta=$kokonaisosa$desimaaliosa
kokonaiset=$(($lukuilmandesimaalipistetta/2))
jakojaannos=$(($lukuilmandesimaalipistetta%2))
kokonaiset=${kokonaiset:1}$(((10*$jakojaannos)/2))
echo $merkki$(( ${kokonaiset:0:$kokonaisosanpituus} )).${kokonaiset:$kokonaisosanpituus} ;} 

function kerro18 () {
tulosta=: # vaihtoehdot: tulosta=echo ja tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1
luku2=${luku2: -36} ; $tulosta $luku2
luku3=${luku3: -36} ; $tulosta $luku3
luku4=${luku4: -36} ; $tulosta $luku4; $tulosta
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42;$tulosta
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1
summa2=$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); $tulosta summa2:$summa2
summa2=$(($m1*$m2*10#$summa2))
(( ! summa2 )) && summa1=$(($m1*$m2*10#$summa1))
apu=$summa2$summa1; (( $desimaaleja )) && echo $((10#${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || { echo $(( 10#$summa2 ))$summa1 ;} ;}

function getlog ()  { # varsinainen logaritmointi
apu=${1//,/.}
[[ ${apu//[^.]/} ]] && vanhalogaritmoitava=$1 || vanhalogaritmoitava=$1".0"
logaritmoitavankokonaisosa=${vanhalogaritmoitava%%.*}
logaritmi=0; logaritmi=$((${#logaritmoitavankokonaisosa}-1)).
# sitten logaritmiin lisätään numeroita yksi kerrallaan mikäli siinä ei vielä ole tarpeeksimontaa numeroa:
until (( ${#logaritmi} >= 14 )); do
  apu=${vanhalogaritmoitava//./}; apu=${apu:0:1}.${apu:1}; # echo apu:$apu
  uusilogaritmoitava=1.0;
  for n in {1..10}; do uusilogaritmoitava=$(kerro18 $apu $uusilogaritmoitava); done
  # echo uusilogaritmoitava:$uusilogaritmoitava; read
  uudenlogaritmoitavankokonaisosa=${uusilogaritmoitava%%.*}
  logaritmi=$logaritmi$((${#uudenlogaritmoitavankokonaisosa}-1))
  vanhalogaritmoitava=$uusilogaritmoitava
done
echo $logaritmi ;}

function onkoekasuurempi  () {
[[ ${1//[^-]/} ]] && m1=a || m1=b; [[ ${2//[^-]/} ]] && m2=a || m2=b
luku1=${1//[-+]/}; luku2=${2//[-+]/} # etumerkki omaan muuttujaan ja vertailtavista pois

while [[ ${luku1:0:1} == 0 ]]; do luku1=${luku1:1}; done ; while [[ ${luku2:0:1} == 0 ]]; do luku2=${luku2:1}; done # etunollien poistaminen
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellaien mikäli ei jo ole
(( $# )) && luku1=${luku1//[^0-9]/} && luku2=${luku2//[^0-9]/}
kokonaisosa1=${luku1%%[,.]*}; desimaaliosa1=${1##*[,.]}
kokonaisosa2=${luku2%%[,.]*}; desimaaliosa2=${2##*[,.]}
case $m1$m2 in 
  ba )  echo 1 ;;
  ab )  echo 0 ;;
  bb )  [[ $kokonaisosa1 > $kokonaisosa2 ]] && { echo 1 || echo 0 ;return ;} || [[ $desimaaliosa1 > $desimaaliosa2 ]] && echo 1 || echo 0 ;;
  aa )  [[ $kokonaisosa1 > $kokonaisosa2 ]] && { echo 0 || echo 1 ;return ;} || [[ $desimaaliosa1 > $desimaaliosa2 ]] && echo 0 || echo 1 ;;
esac ;}

function yhteenlasku () { # voi käyttää vähennyslaskuunkin
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=$1
luku2=$2
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellainen mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
(( $m2 +1 )) && luku=$((10#$kokoluku1+10#$kokoluku2)) || luku=$((10#$kokoluku1-10#$kokoluku2))
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}

# laskennan määrittelyt:
annettuluku=0.30102999566398119 # Kirjoita tähän mistä antilogaritmi lasketaan - mitään muuta ei muuteta
tulosarvio=$((10**$((${annettuluku%%.*}+1))/5))
delta=$(puolita $tulosarvio)

# sitten binäärihaku:
time for n in {1..45}; do
echo 'kierros: '$n'  $tulosarvio ' $(getlog $tulosarvio)'  delta: '$delta       
(( $(onkoekasuurempi $(getlog $tulosarvio) $annettuluku ))) && tulosarvio=$(yhteenlasku $tulosarvio -$delta) || tulosarvio=$(yhteenlasku $tulosarvio $delta)
  delta=$(puolita $delta)
  delta=${delta:0:18}  # onko oikea paikka?   
done
echo $annettuluku on luvun: ${tulosarvio:0:12}  logaritmi 
« Viimeksi muokattu: 03.01.23 - klo:20.12 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #271 : 10.12.19 - klo:12.40 »
Useimmille matematiikan funktioille on monia erilaisia sarjakehitelmiä. Ja luonnollista logaritmia ei BASH:issa voi laskea samallatavalla kuin 10-kantainen logaritmi laskettiin vaan se täytyy laskea sarjakehitelmällä koska BASH:issa korottaminen potenssiin 2.71... on vaikeaa. Tässäkäytetty sarjakehitelmä toimii tosin vain kun logaritmoitava on suurempi kuin .5 . Vaikka teoriassa ylärajaa ei ole niin aikaa alkaa kulua kun logaritmoitava on suurempi kuin 100.

Tähänmennessä tehdyillä työkaluilla saa luonnolliseen-logaritmiin vain tusinan oikeita numeroita ja sekin vaati että työkaluihin tehtiin koodiparannuksia - sillä uudentyyppiset tehtävät tuovat näin alussa esiin uusia ongelmia. Koodiparanneltujen työkalujen tulee toimia myös vanhoissa tehtävissä.

Tämä on sellaista numeroleikkiä jota harrastettiin vuosikymmeniä sitten samaanaikaan kun tietoisena BASH:in matemaattisista kyvyistä vakuutettiin silmät sinisinä ettei BASH osaa. Ja koskei alkeita selvitetty silloin aikoinaan täytyy ne yrittää selvittää nyt - tai eihän se enää kannata vaikka mielenkiintoista onkin.

Käytetyn matemaattisen sarjan "sanallinen" kuvaus:
apu=(logaritmoitava-1)/logaritmoitava; lnx=apu+(1/2)*apu^2+(1/3)*apu^3+(1/4)*apu^4 ...
- siis tässä tarvitsee laskea luvun korottamista potenssiin luokkaa sata.

Sanallisesta kuvauksesta tehty koodi: ensiksi lasketaan apu muuttujaan ja apu:n potenssien arvot matriisiin. Sarja suppenee eri luvuilla kovin erinopeasti joten lasketaan matriisiin uusia jäseniä niin kauan että laskuista saadut merkitsevät numerot ovat kaikki nollia - matriisin koosta ei tarvitse välittää sillä BASH:in matriisit kasvavat automaattisesti jäseniä lisättäessä:
Koodia: [Valitse]
function kerro18 () {
tulosta=: # jos halutaan tulostaa niin merkitään: tulosta='echo -e' ja jos ei niin tulosta=:
[[ ${1//[^.]/} ]] && luku1=${1:0:17} || luku1=${1:0:17}".0"
[[ ${2//[^.]/} ]] && luku2=${2:0:17} || luku2=${2:0:17}".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.};
desimaaliosa2=${luku2##*.}; desimaaleja=$((${#desimaaliosa1}+${#desimaaliosa2}))
luku1=000000000000000000${luku1//./}
luku2=000000000000000000${luku2//./}
a=${luku1: -18:9}; b=${luku1: -9}
c=${luku2: -18:9}; d=${luku2: -9}; $tulosta $a' '$b; $tulosta $c' '$d
luku1=00000000000000000000000000000000000000$((10#$b*10#$d))
luku2=00000000000000000000000000000000000000$((10#$d*10#$a))"000000000"
luku3=00000000000000000000000000000000000000$((10#$c*10#$b))"000000000"
luku4=00000000000000000000000000000000000000$((10#$a*10#$c))"000000000000000000"
luku1=${luku1: -36} ; $tulosta $luku1'\r\v'
luku2=${luku2: -36} ; $tulosta $luku2'\r\v'
luku3=${luku3: -36} ; $tulosta $luku3'\r\v'
luku4=${luku4: -36} ; $tulosta $luku4'\r\v'
luku11=${luku1:0:18} # tämänjälkeen 18->17
luku12=${luku1:18}; $tulosta a$luku11' 'b$luku12'\r\v'
luku21=${luku2:0:18}
luku22=${luku2:18}; $tulosta c$luku21' 'd$luku22'\r\v'
luku31=${luku3:0:18}
luku32=${luku3:18}; $tulosta a$luku31' 'b$luku32'\r\v'
luku41=${luku4:0:18}
luku42=${luku4:18}; $tulosta c$luku41' 'd$luku42'\r\v'
summa1=$((10#$luku12+10#$luku22+10#$luku32+10#$luku42)); $tulosta summa1:$summa1'\r\n'
summa1pituus=${#summa1}; ylivuoto=0; (( $summa1pituus >= 19 )) && ylivuoto=${summa1:0: -18} && summa1=${summa1:1}
summa1=000000000000000000$summa1; summa1=${summa1: -18} ;$tulosta ylivuoto:$ylivuoto' summa1:'$summa1'\r\n'
summa2=000000000000000000$((10#$luku11+10#$luku21+10#$luku31+10#$luku41+$ylivuoto)); summa2=${summa2: -18}; $tulosta summa2:$summa2'\r\n'
#summa2=$(($m1*$m2*10#$summa2))
(( $m1 != $m2 )) && echo -n '-' 
apu=$summa2$summa1; (( $desimaaleja )) && echo $((${apu:0: -$desimaaleja})).${apu: -$desimaaleja} || { echo $(( 10#$summa2 ))$summa1 ;} ;}

function jaa () {
[[ ${1//[^.]/} ]] && luku1=$1 || luku1=$1".0"
[[ ${2//[^.]/} ]] && luku2=$2 || luku2=$2".0"
[[ ${1//[^-]/} ]] && m1=-1 || m1=+1; [[ ${2//[^-]/} ]] && m2=-1 || m2=+1
luku1=${luku1//-/}; luku2=${luku2//-/}
desimaaliosa1=${luku1##*.}
desimaaliosa2=${luku2##*.}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"0000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"0000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
kokonaisosa1=${luku1%%.*}
kokonaisosa2=${luku2%%.*}
luku1ilmandesimaalipistetta=$kokonaisosa1$desimaaliosa1 #; echo $desimaaliosa1
luku2ilmandesimaalipistetta=$kokonaisosa2$desimaaliosa2 #; echo $desimaaliosa2
kokonaiset=$((10#$luku1ilmandesimaalipistetta/10#$luku2ilmandesimaalipistetta))
jakojaannos=$((10#$luku1ilmandesimaalipistetta%10#$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=000000000000000$desimaalit
kokonaiset=$kokonaiset.${desimaalit: -9}
# echo $jakojaannos
jakojaannos=$(((100000000*$jakojaannos)%$luku2ilmandesimaalipistetta))
desimaalit=$(((1000000000*$jakojaannos)/$luku2ilmandesimaalipistetta))
desimaalit=0000000000000000$desimaalit
(( $m1 != $m2 )) && echo -n '-'
echo $kokonaiset${desimaalit: -8} ;}

function yhteenlasku () { # voi käyttää vähennyslaskuunkin merkki!
[[ ${1//[^-]/} ]] && m1=- || m1=+; [[ ${2//[^-]/} ]] && m2=- || m2=+
luku1=${1:0:18}
luku2=${2:0:18}
luku1=${luku1//[-+]/}; luku2=${luku2//[-+]/}
luku1=${luku1//-./-0.}; luku2=${luku2//-./-0.}
[[ ${luku1//./} == $luku1 ]] && luku1=$luku1".0" # on tarpeen että luvussa on yksi desimaalipiste
[[ ${luku2//./} == $luku2 ]] && luku2=$luku2".0" # joten lisätään sellainen mikäli ei jo ole
desimaaliosa1=${luku1##*.}; desimaaliosa1pituus=${#desimaaliosa1}
desimaaliosa2=${luku2##*.}; desimaaliosa2pituus=${#desimaaliosa2}
(( ${#desimaaliosa2} >= ${#desimaaliosa1} )) &&
{ apu=$desimaaliosa1"00000000000000000000"; desimaaliosa1=${apu:0:${#desimaaliosa2}} ;} || { apu=$desimaaliosa2"00000000000000000000"; desimaaliosa2=${apu:0:${#desimaaliosa1}} ;}
#echo a$desimaaliosa2' 'b$desimaaliosa1 ; read # testatessa tämä on tarpeen
desimaaleja=${#desimaaliosa1} #; echo $desimaaleja
kokonaisluku1=${luku1%%.*}
kokonaisluku2=${luku2%%.*}
kokoluku1=$kokonaisluku1$desimaaliosa1 #; echo $kokoluku1
kokoluku2=$kokonaisluku2$desimaaliosa2 #; echo $kokoluku2
case $m1$m2 in 
  +- )  luku=$((10#$kokoluku1-10#$kokoluku2))  ;;
  -+ )  luku=$((-10#$kokoluku1+10#$kokoluku2)) ;;
  ++ )  luku=$((10#$kokoluku1+10#$kokoluku2))  ;;
  -- )  luku=$((-10#$kokoluku1-10#$kokoluku2)) ;;
esac ;
echo ${luku:0: -$desimaaleja}.${luku: -$desimaaleja} ;}

unset matriisi
logaritmoitava=4.214 # kirjoita tähän se luku josta haluat luonnollisen logaritmin.
matriisi[1]=$(jaa $(yhteenlasku $logaritmoitava -1) $logaritmoitava); apu=${matriisi[1]}; time for n in {2..10000}; do matriisi[$n]=$(kerro18 $apu ${matriisi[$n-1]}); (( 10#${matriisi[$n]:3:16} == 0 )) && echo $n && break; printf "%s\n" "${matriisi[@]}"; done

# sitten matriisista lasketaan luonnollinen logaritmi:
lnx=0; for (( n=1; n<=${#matriisi[@]}; n++ )); do
apu=$(kerro18 $(jaa 1 $n) ${matriisi[n]})
lnx=$(yhteenlasku $lnx $apu); echo $lnx; done

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #272 : 15.01.20 - klo:09.07 »
Muuttujan julistaminen (=declare) kokonaisluvuksi kannattaa, sillä koodi yksinkertaistuu ja nopeutuukin aikapaljon. Käskyt näiden toteamiseksi:
 
time { declare -i x; x=0; for n in {1..100000}; do x=x+1; done; echo $x ;}
time { unset x; x=0; for n in {1..100000}; do x=$((x+1)); done; echo $x ;}
time { for n in {1..100000}; do : ; done ;} # tällä saa sen ajan minkä itse looppi vie

- käsky: x=x+1 tallettaa muistipaikkaan x tekstijonon x+1 ja tuo "declare -i x" määrää BASH-tulkin tulkkaamaan sen kokonaisluvuksi. Näet tämän kun kirjoitat:
Koodia: [Valitse]
unset x; for n in {1..100000}; do x=x+1; done; echo $x
- se että käskyrivin eteen täytyy kirjoittaa "unset x" johtuu siitä että BASH-tulkki noudattaa samassa päätteessä aikaisemmin annettua määräystä ellei uutta määräystä ole annettu. Käsky "unset x" poistaa määräyksen.
- tämä vanhojen asetuksien muistaminen saattaa joskus olla paha asia silloin kun yrittää opiskella BASH:in saloja. Tämän takia omat skriptit saattavat toimia silloinkin kun ei pitäisi ja toisten tekemät skriptit eivät toimi vaikka pitäisi.

petteriIII

  • Käyttäjä
  • Viestejä: 693
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #273 : 23.01.20 - klo:21.59 »
BASH:issa ei ole desimaalilukuja - on vain tekstijonoja joista jotkut voi tulkita desimaaliluvuiksi. Tekstijonoa täytyy usein käsitellä jotenkin ennen tulostamista. Olisi houkuttelevaa käyttää tulostamiseen printf-komentoa sillä se kykenee käsittelemään tulostamisen yhteydessä ja se tulostaa desimaaliluvutkin oikein. Mutta se toimii vain kun kokonaisosan ja desimaaliosan yhteenlaskettu numeromäärä on BASH:issa  alle 19, se vaatii joskus desimaalieroittimeksi sen mikä koodisivulla on määrätty (pilkku tai piste) - ja lisäksi se on pikkuisen hidas. Ei voi olla ajattelematta että prntf on heitetty BASH:in rattaisiin jottei desimaalilaskentaa saisi toimimaan hyvin.

Käytettäessä echo:a tulostettavissa luvuissa saa olla moninkertainen määrä numeroita eikä desimaalieroittimen kanssa joudu tappelemaan. Mutta echo ei kykene käsittelemään tulostettavaa mitenkään vaan se tarvitsee apua BASH-skripteiltä ja nämä ovat usein erittäin kookkaita - mutta senlaatuiset skriptit ovat myös hyvin nopeita.

Desimaalilaskennan skriptit ovat vikaherkkiä senlisäksi että ne ovat runsaskoodisia. Ja sellaisissa tilanteissa käytetään normaalisti kirjastoja koodinteossa mutta virtuoosit ovat suhmuroineet BASH:in kirjastoja niin paljon ettei BASH:in kirjastoja juuri kukaan enää käytä. Sentakia on helppoa uskotella että esimerkiksi BASH ei kykene juuri ollenkaan numeronmurskaukseen ilman ulkoisten ohjelmien apua, joten esimerkiksi desimaalilaskentaa pidetään mahdottomana.

Kirjasto on yksinkertaisimmassa muodossaan tiedosto johon on kirjoitettu funktioita. BASH:issa jonkun toisen tiedoston funktiot voi liittää tehtävän skriptin koodiin käskyllä: . tiedoston nimi. Liitoskäsky on tosiaankin pelkkä piste. Aikoinaan BASH:illa oli moniakin kirjastoja - netistä löytyy vieläkin aikamonta (mutta niiden löytäminen on hankalaa). Mutta tapa liittää kirjasto  tehtävän skriptin koodiin oli alussa toisenlainen kuin nykyään. Kun liitäntätapaa muutettiin niin kirjastoja lakattiin käyttämästä - vaikka ne toimisivat edelleenkin.

Kirjastofunktion testaaminen on huomattavasti isompi työ kuin tavallisen funktion vaikka niillä ei teoriassa juuri eroa ole - mutta kirjastofunktion täytyy varautua paljon paremmin niihin tapauksiin joita pidetään puolimahdottomina. Jokatapauksessa jokaisessa kirjastofunktiossa on alkuunsa useimmiten naurettaviakin puutteita ja virheitäkin - ja läheskaikkien korjaaminen olisi vuosien urakka komppanialta koodaajia.

Esimerkiksi seuraava tosinopeatoiminen funktio jota kehoitetaan katkaisemaan annettu desimaaliluku määrättävän desimaalimäärän jälkeen pyöristäen oikein. Funktion koodi on nopea huolimatta pituudestaan - eikä tällaista funktiota varmaankaan kukaan viitsisi tehdä jos sitä ei löydy kirjastosta:
Koodia: [Valitse]
function pyöristä_desimaaleissa () { (( ${1//./} > 0 )) && { (( $# == 2 )) && desimaalienluku=$2 || desimaalienluku=0; kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; (( $desimaalienluku == 0 )) && echo $(($kokonaisosa+$((${desimaaliosa:0} >= 50)))) && return; desimaaliosa=$desimaaliosa"0000000000000000000000";echo $kokonaisosa.$(( ${desimaaliosa:0:$desimaalienluku}+$(( ${desimaaliosa:$desimaalienluku+1} >= 50)) )) ;} || { (( $# == 2 )) && desimaalienluku=$2 || desimaalienluku=0; kokonaisosa=${1%%[,.]*}; desimaaliosa=${1##*[,.]}; (( $desimaalienluku == 0 )) && echo $(($kokonaisosa+$((${desimaaliosa:0} <= 50)))) && return; desimaaliosa=$desimaaliosa"0000000000000000000000";echo $kokonaisosa.$(( ${desimaaliosa:0:$desimaalienluku}-$(( ${desimaaliosa:$desimaalienluku+1} <= 50)) )) ;} ;}

# kutsuesimerkit testaamiseen (kokeillessa leikkaa-liimaaa kaikki päätteeseen yhtenä palasena):
pyöristä_desimaaleissa 1.51515773 0   # pitäisi tulostaa 2
pyöristä_desimaaleissa 1.51515773     # pitäisi tulostaa 2
pyöristä_desimaaleissa 1.51515773 2   # pitäisi tulostaa 1.52
pyöristä_desimaaleissa 12345678901234567890123456789.51515773 2 # pitäisi tulostaa
                                                                #12345678901234567890123456789.52
pyöristä_desimaaleissa 1.51515773 3   # pitäisi tulostaa 1.515
pyöristä_desimaaleissa 1.51515773 4   # pitäisi tulostaa 1.5152
pyöristä_desimaaleissa 1.5151577311111151 14 # pitäisi tulostaa 1.51515773111112
pyöristä_desimaaleissa -1.51515773 0  # pitäisi tulostaa -1
pyöristä_desimaaleissa -1.51515773 2  # pitäisi tulostaa -1.51
pyöristä_desimaaleissa -1.51515773 3  # pitäisi tulostaa -1.514
pyöristä_desimaaleissa -1.51515773 4  # pitäisi tulostaa -1.5151
pyöristä_desimaaleissa 1.1 9          # pitäisi tulostaa  1.100000000
:

Jere Sumell

  • Käyttäjä
  • Viestejä: 742
  • Talous, Hallinto ja Markkinointi (AMK, 2017),B.B.A
    • Profiili
    • Tietokone-blogi
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #274 : 04.04.20 - klo:08.55 »
Mikä olisi hyvä kirja opiskella bash-skriptausta? Varmaan suomeksi ei ole kirjoitettu, mutta kieli ei ole este.

Pitäisi ohjelmoida skripti, joka automatisoi käyttäjälogi-tiedostoista tiedoston poistoa, kun se on vanhenutunt tietyn aikaa, eli aika pitäisi tarkistaa, ja sitä mukaa skripti poistaa käyttäjälogi-tiedostosta rivin tai tietueen paremminkin virallista termiä käyttäen, kun ensin tarkistaa sen syntyajankohdan.

Olisi aikaa ja halukkuutta ottaa Bash-skriptaus tehokäyttöön haltuun, ja perinteinen kirja on paras tietolähteeni.
Free Internet and  people for humans all over the globe!

(Profiilikuvassa oma valokuvani GIMPissä editoituna Disney Classic-väripaletin väreihin ja muunnettuna bittikartta-tiedostosta vektorigrafiikaksi.)

nm

  • Käyttäjä
  • Viestejä: 16425
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #275 : 04.04.20 - klo:09.23 »
Pitäisi ohjelmoida skripti, joka automatisoi käyttäjälogi-tiedostoista tiedoston poistoa, kun se on vanhenutunt tietyn aikaa, eli aika pitäisi tarkistaa, ja sitä mukaa skripti poistaa käyttäjälogi-tiedostosta rivin tai tietueen paremminkin virallista termiä käyttäen, kun ensin tarkistaa sen syntyajankohdan.

Tuohon kannattaa käyttää logrotatea

jekku

  • Käyttäjä
  • Viestejä: 2624
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #276 : 04.04.20 - klo:09.45 »
Minun suosikkini on ollut jo vuosia
"Learning the bash"
O'Reilly & Associates, Inc

ISBN1-56592-147-x

Aapisen viivakoodista tulostuu tuollainen:
9781565921474 90000

http://shop.oreilly.com/product/9780596009656.do

Jere Sumell

  • Käyttäjä
  • Viestejä: 742
  • Talous, Hallinto ja Markkinointi (AMK, 2017),B.B.A
    • Profiili
    • Tietokone-blogi
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #277 : 04.04.20 - klo:10.00 »
Minun suosikkini on ollut jo vuosia
"Learning the bash"
O'Reilly & Associates, Inc

ISBN1-56592-147-x

Aapisen viivakoodista tulostuu tuollainen:
9781565921474 90000

http://shop.oreilly.com/product/9780596009656.do

Kiitos vinkistä! Katsoin hakukoneella tuon kirjan hintaa, niin voisi harkita omaan kotikirjastoon tuohon kirjahyllylle muiden tietotekniikka - kirjojen jatkeeksi hommata tuon, kun hinta ei ole paha, kun Suomestakin tilattuna Adlibriksessä näyttäisi olevän hinta alle 40 euroa. Halpa tietotekniikka-kirjaksi, ja Shell - skriptaus kannattaisi ottaa haltuun, kun se on ajatonta tietokoneistusta liennyt jo sitä 1970-luvulta?
Free Internet and  people for humans all over the globe!

(Profiilikuvassa oma valokuvani GIMPissä editoituna Disney Classic-väripaletin väreihin ja muunnettuna bittikartta-tiedostosta vektorigrafiikaksi.)

Whig

  • Käyttäjä
  • Viestejä: 353
  • puppu-generaattori
    • Profiili
    • localhost
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #278 : 14.04.20 - klo:11.35 »
Minun suosikkini on ollut jo vuosia
"Learning the bash"
O'Reilly & Associates, Inc

ISBN1-56592-147-x

Aapisen viivakoodista tulostuu tuollainen:
9781565921474 90000

http://shop.oreilly.com/product/9780596009656.do

Kiitos vinkistä! Katsoin hakukoneella tuon kirjan hintaa, niin voisi harkita omaan kotikirjastoon tuohon kirjahyllylle muiden tietotekniikka - kirjojen jatkeeksi hommata tuon, kun hinta ei ole paha, kun Suomestakin tilattuna Adlibriksessä näyttäisi olevän hinta alle 40 euroa. Halpa tietotekniikka-kirjaksi, ja Shell - skriptaus kannattaisi ottaa haltuun, kun se on ajatonta tietokoneistusta liennyt jo sitä 1970-luvulta?

Näyttäisi olevan myös ilmaiseksi ladattavissa PDF-tiedostona: Poistettu Github-linkki. (--Tomin)
« Viimeksi muokattu: 14.04.20 - klo:13.07 kirjoittanut Tomin »

Tomin

  • Palvelimen ylläpitäjä
  • Käyttäjä / moderaattori+
  • Viestejä: 11481
    • Profiili
    • Tomin kotisivut
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #279 : 14.04.20 - klo:13.09 »
Näyttäisi olevan myös ilmaiseksi ladattavissa PDF-tiedostona: Poistettu Github-linkki. (--Tomin)

Se, että netistä löytää kirjan PDF-tiedostona, vaikka sitten GitHub-repostakin ei vielä takaa, että se olisi ilmaiseksi jaossa. Tässä tapauksessa en löydä yhteyttä kirjoittajan tai kustantajan ja tuon GitHub käyttäjän tai repon väliltä, joten poistin linkin. Tuo kirja näkyy löytyvän netistä monestakin paikkaa, mutta kirjassa tai muussa löytämässäni lähteessä ei sanota, että se olisi vapaasti jaossa.
Automaattinen allekirjoitus:
Lisäisitkö [RATKAISTU] ketjun ensimmäisen viestin aiheeseen ongelman ratkettua, kiitos.