Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++, PL/I: Kalenteriviikon laskeminen

Sivun loppuun

jalski [11.06.2012 17:36:32]

#

Tervehdys,

Tuleeko kenellekään mieleen nopeampaa/suoraviivaisempaa tapaa laskea kalenteri viikkonumero annetulle päivämäärälle, kuin käyttämäni tapa?

Oma toteutukseni on kirjoiteltu PL/I:llä ja käyttää ratkaisuun bittimerkkijonoja:

Annetun päivämäärän pohjalta muodostetaan riittävän pituinen bittimerkkijono tallentamaan päivät bitteinä tammikuun alusta kyseiseen päivämäärään asti.

Bittimerkkijono täytetään oikeanlaisella viikon bittirotaatiolla riippuen tammikuun ensimmäisen päivän viikonpäivästä. Viikkoa kuvataan siis bittimerkkijonolla jossa maanantai on ykkönen ja muut päivät nollia.

Tekemällä yksinkertainen merkkijonohaku ja laskemalla ykkösien lukumäärä bittimerkkijonosta saadaan maanantaipäivien lukumäärä selville kyseisenä aikavälinä.

Tammikuun ensimmäinen viikko voi olla osittainen viikko ja jos siihen kuuluu torstai niin lisätään kyseinen viikko tulokseen. Tämä voidaan tarkistaa yksinkertaisesti viikkoa kuvaavalla oikeanlaisella bittirotaatiolla.

Tässä vaiheessa jos tulos on nolla niin tiedetään, että viikkonumero kuuluu edellisen vuoden puolelle. Tällöin tehdään ensimmäisenä selostamani asia edellisen vuoden alusta -> edellisen vuoden loppuun.

Sama koodina:

*PROCESS MARGINS(1, 140);

 test: proc options (main);
   dcl 1 date_type based,
         2 day   pic'99',
         2 month pic'99',
         2 year  pic'9999';

   dcl d like date_type;

   /* fill date structure */
   d.day   = '01';
   d.month = '01';
   d.year =  '2012';

   /* test our proggy */
   put skip list (trim(week(d)));

 /****************************************************************************************************************/
   (STRINGRANGE, SUBSCRIPTRANGE):
   week: proc (day) returns (fixed bin (31));
     dcl day like date_type;

     dcl (from, to) like date_type;
     dcl (days_from, days_to) fixed bin (31);
     dcl total_days fixed bin (31);
     dcl week_index fixed bin (31);
     dcl weeknro fixed bin (31);
     dcl result bit (*) aligned controlled;
     dcl weeks (7) bit (7) aligned nonasgn
         init ('0100000'b, '0010000'b, '0001000'b, '0000100'b, '0000100'b, '0000010'b, '0000001'b);
     dcl masks (7) bit (7) aligned nonasgn
         init ('0111100'b, '1111000'b, '1110001'b, '1100011'b, '1000111'b, '0001111'b, '0011110'b);
     dcl bool bit (1);

     dcl (days, weekday, tally, substr, lbound, hbound, copy, omitted) builtin;

     /* we start from first of january, naturally */
     from.day   = '01';
     from.month = '01';
     from.year  = day.year;

     to = day;

     days_from = days(string(from), 'DDMMYYYY');
     days_to   = days(string(to), 'DDMMYYYY');

     /* calculate required length for the bit string */
     total_days = days_to - days_from + 1;
     if total_days <= 0 then return (0);

     /* allocate bit string, holding date range */
     allocate result bit (total_days);

     /* fill bit string with correct rotation of the week pattern */
     week_index = weekday(days_from);
     result = copy(weeks(week_index), total_days / 7) || substr(weeks(week_index), 1, mod(total_days, 7));

     /* find the number of weeks starting on monday */
     weeknro = tally(result, '1'b);

     /* free allocated storage */
     free result;

     /* first week can be partial and if it includes thursday, then add it to the result */
     bool = substr(masks(week_index), 1, 1);
     If bool then weeknro += 1;

     /* maybe first days of the year belong to week numbering of the previous year? */
     if weeknro <= 0 then
       do;
         from.year -= 1;
         to = from;
         to.day   = '31';
         to.month = '12';

         days_from = days(string(from), 'DDMMYYYY');
         days_to   = days(string(to), 'DDMMYYYY');

         total_days = days_to - days_from + 1;
         if total_days <= 0 then return (0);

         allocate result bit (total_days);

         week_index = weekday(days_from);
         result = copy(weeks(week_index), total_days / 7) || substr(weeks(week_index), 1, mod(total_days, 7));

         weeknro = tally(result, '1'b);
         free result;
       end;
     return (weeknro);
   end week;
 /****************************************************************************************************************/
 end test;

Grez [11.06.2012 17:50:08]

#

Suoraviivaisempi ja luultavasti nopeampikin tapa olisi ihan vaan jakolaskulla tehdä.

Nykyisissä ohjelmointiympäristöissähän niihin sisältyvä kalenteri yleensä tarjoaa viikkonumeron suoraan. Jos kuitenkin leikitään että käytettävissä on vain ne toiminnot, joita tuossa PL/I koodissakin on käytetty, niin C#:lla tekisin näin:

static int GetWeek(DateTime value) {
    var yearsFirstDay = new DateTime(value.Year, 1, 1);

    //Vuoden 1. päivän viikonpäivä ma=0 .. su=6 (DayOfWeekissä su=0, joten siirrämme yhdellä)
    var wd = ((int)yearsFirstDay.DayOfWeek) + 6 % 7;

    //Jos 1. päivä on pe-su, on viikko 0, muuten 1
    var weekZeroMonday = yearsFirstDay.AddDays(((wd > 3) ? 0 : -7) - wd);
    return (value - weekZeroMonday).Days / 7;  //Kokonaislukujako
}

Tämä siis antaa tulokseksi numeron välillä 0-53

En edes jaksa arpoa mihin olet saanut loput noin 70 riviä koodia hukattua. Tuskin ylimääräisen koodin lisääminen ainakaan nopeuttaa tai tekee suoraviivaisemmaksi.

jalski [11.06.2012 19:27:26]

#

Grez kirjoitti:

En edes jaksa arpoa mihin olet saanut noin paljon koodia hukattua.

Tuosta koodista puolet voi ottaa pois, jos ei haluta palauttaa edellisen vuoden kalenteriviikon numeroa mikäli vuoden ensimmäiset päivät oikeasti siihen kuuluvat. Omasta koodistasi muuten puuttuu tuo. En itse ainakaan kalenterissa ikinä ole nähnyt nollaviikkoa. ;-)

Lyhyempään tilaan tuo versiosi toki PL/I:lläkin menisi kuin omani, mutta vaatisi silti useamman rivin enemmän tekstiä johtuen ihan ohjelmointikielen syntaksin eroista. Pelkät muuttujien määrittelyt vaativat tuossa PL/I:llä yhtäpaljon tekstiä kuin koko C# versiosi. C# ohjelmassa päivämäärä on objekti metodeineen, PL/I:ssä pelkkä merkkijono.

Omassa päässäni varsinainen merkkijonopohjainen ratkaisu oli helpoin hahmottaa:

weeknro = tally(result, '1'b);
bool = substr(masks(week_index), 1, 1);
If bool then weeknro += 1;

Grez [11.06.2012 20:05:07]

#

jalski kirjoitti:

Tuosta koodista puolet voi ottaa pois, jos ei haluta palauttaa edellisen vuoden kalenteriviikon numeroa mikäli vuoden ensimmäiset päivät oikeasti siihen kuuluvat. Omasta koodistasi muuten puuttuu tuo. En itse ainakaan kalenterissa ikinä ole nähnyt nollaviikkoa. ;-)

Niin, no jos funktio palauttaa pelkästään viikkonumeron (eli käytetään funktiolle annetun päivän vuosinumeroa) niin on pakko palauttaa 0 ettei tule "off by a year" virhettä. Voin toki tehdä version joka palauttaa noissa tilanteissa edellisen vuoden viimeisen viikon numeron, ei tule juurikaan pidempi:

static int GetWeek(DateTime value) {
    var yearsFirstDay = new DateTime(value.Year, 1, 1);

    //Vuoden 1. päivän viikonpäivä ma=0 .. su=6 (DayOfWeekissä su=0, joten siirrämme yhdellä)
    var wd = ((int)yearsFirstDay.DayOfWeek) + 6 % 7;

    //Jos 1. päivä on pe-su, on viikko 0, muuten 1
    var weekZeroMonday = yearsFirstDay.AddDays(((wd > 3) ? 0 : -7) - wd);
    var week = (value - weekZeroMonday).Days / 7;  //Kokonaislukujako
    return (week < 1) ? GetWeek(yearsFirstDay.AddDays(-1)) : week;
}

jalski kirjoitti:

Pelkät muuttujien määrittelyt vaativat tuossa PL/I:llä yhtäpaljon tekstiä kuin koko C# versiosi. C# ohjelmassa päivämäärä on objekti metodeineen, PL/I:ssä pelkkä merkkijono.

Niin no miksi niitä määrittelyjä pitää olla niin julmetusti? Väittäisin ettei sen paljonkaan pidempi tarvitse olla PL/I:nä.

Ja koska en tee väitteitä kevyesti... Tässä sama PL/I:nä. Toivottavasti on edes sinne päin koska en ole koskaan koodannut PL/I:llä enkä myöskään voinut testata.

week: proc (day) returns (fixed bin (31));
	dcl (day, yearsFirstDay, wd, weekZeroMonday, weeknro) fixed bin (31);
	yearsFirstDay = days(daystodate(day,'YYYY') || '0101', 'YYYYMMDD');
	wd = mod((weekday(yearsFirstDay) + 5), 7);
	weekZeroMonday = yearsFirstDay - wd;
	if wd<4 then weekZeroMonday -= 7;
	weeknro = ((value - weekZeroMonday) / 7);
	if week>0 then return (weeknro);
	return (week(yearsFirstDay-1));
end week;

(Edit: korjasin jakojäännöksen) 

jalski kirjoitti:

Omassa päässäni varsinainen merkkijonopohjainen ratkaisu oli helpoin hahmottaa

Toisaalta tietokoneet on tehokkaita laskemaan ihan numeroilla. Tuollainen merkkijonon tekeminen ja sitten merkkien laskeminen sieltä on jokseenkin turhaa työtä koneelle. Ohjelmoinnissa on usein järkevämpää miettiä miten on matemaattisesti järkevää tehdä joku juttu kuin miten sen itse tekisi sormilla laskemalla. Kone on aika hyvä "laskemaan päässä" vaikeitakin laskuja.

Uskon myös, että keskimääräiselle koodarille tuo merkkijonopohjainen ratkaisusi on keskimäärin hankalampi hahmottaa ja sen oikeellisuudesta on vaikeampi varmistua.

jalski [11.06.2012 21:24:44]

#

Tarkoitin aiemmin, että sillä on eroa käsitelläänkö päiväystä DateTime objektina vai pelkkänä merkkijonona.

esim. PL/I:llä vaihtoehdot ovat jokseenkin seuraavanlaiset:

dcl date   char (8) date ('DDMMYYYY') init ('01012012');
dcl day    pic'99';
dcl month  pic'99';
dcl year   pic'9999';

/* Osiin */
day   = substr(date, 1, 2);
month = substr(date, 3, 2);
year  = substr(date, 5, 4);

/* takaisin */
date = day || month || year;

Tai sitten:

dcl date_string char (8) date ('DDMMYYYY');

dcl 1 date_type based,
      2 day   pic'99',
      2 month pic'99',
      2 year  pic'9999';

dcl date like date_type;

date.day   = '01';
date.month = '01';
date.year  = '2012';

/* merkkijonoksi */
date_string = string(date);

Näistä alemman etuna on se, että struktuurin osia voidaan suoraan käsitellä kuin numeroita.

C#:llä DateTime objekti tarjoaa kyseisen toiminnallisuuden ohjelmoijan kannalta lähes ilmaiseksi.

EDIT:
Tuo laittamasi pätkä pääsi kyllä aika lähelle. :-) PL/I:ssä ei ole modulo operaattoria, vaan homma hoituu sisäänrakennetulla MOD funktiolla.

Grez [11.06.2012 21:36:48]

#

jalski kirjoitti:

PL/I:ssä ei ole modulo operaattoria, vaan homma hoituu sisäänrakennetulla MOD funktiolla.

Joo, näköjään olisi pitänyt lukea tuon sivun alkukin kun hain PL/I -dokumentaatiosta modulus hakusanalla ja tämän sivun keskivaiheilla näytti hyvältä: http://pic.dhe.ibm.com/infocenter/ratdevz/v7r5/topic/com.ibm.etools.pl1.win.doc/topics/onmdir-yyy.htm?resultof="modulus" "modulu"

jalski kirjoitti:

tarjoaa kyseisen toiminnallisuuden ohjelmoijan kannalta lähes ilmaiseksi.

Juuri siksi en ihan älyä miksi kukaan haluaa kiusata itseään esim. PL/I:llä, kun löytyy paljon kieliä, jotka tarjoaa kaikenlaista ohjelmoijan kannalta "ilmaiseksi". Tai siinä tapauksessa ymmärrän jos maksetaan hyvin jonkin vanhan järjestelmän ylläpitämisestä. :D

Ja tuo C# esimerkkikoodinikin oli kirjoitettu sillä ajatuksella, että ei käytetä kuin PL/I:ssä tarjolla olevia keinoja. Toki C#:ssa saa viikon suoraankin:

static int GetWeek(DateTime value) {
    return System.Globalization.CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(
        time: value,
        rule: System.Globalization.CalendarWeekRule.FirstFourDayWeek,
        firstDayOfWeek: DayOfWeek.Monday);
}

jalski [12.06.2012 10:31:53]

#

Grez kirjoitti:

Juuri siksi en ihan älyä miksi kukaan haluaa kiusata itseään esim. PL/I:llä, kun löytyy paljon kieliä, jotka tarjoaa kaikenlaista ohjelmoijan kannalta "ilmaiseksi". Tai siinä tapauksessa ymmärrän jos maksetaan hyvin jonkin vanhan järjestelmän ylläpitämisestä. :D

Kyllähän noissa "vanhan liiton" ohjelmointikielissäkin on hyviä ominaisuuksia joita ei välttämättä löydy vielä kovin monesta uudemmastakaan kielestä.

On PL/I yli 40-vuotiaaksi ohjelmointikieleksi edelleen ihan käytettävä ja tarjoaa ohjelmointikielitasolla muun muassa:

- monipuoliset IO-ominaisuudet (stream IO, record IO (sequential, keyed))
- virheiden hallinta ja käsittely
- monipuoliset merkkijonojen käsittelyominaisuudet
- vahva tyypitys (type, handle)
- debuggaus ominaisuudet kuuluvat ohjelmointikieleen (plidump, put data)
- desimaali -ja kompleksiluvuilla laskenta
- moniajo
- taulukoilla indeksialue vapaasti määriteltävissä
- taulukoilla ja structeilla voi laskea suoraan (matriisi -ja vektorilaskenta)
- osoittimen käyttö toteutettu fiksusti based muuttujan kautta

Grez [12.06.2012 11:32:57]

#

jalski kirjoitti:

Kyllähän noissa "vanhan liiton" ohjelmointikielissäkin on hyviä ominaisuuksia joita ei välttämättä löydy vielä kovin monesta uudemmastakaan kielestä.

Oliko esim. noissa PL/I:n ominaisuuksissa jotain, mitä ei mielestäsi löydy esim. Javasta tai C#:sta?

Toki jotkin ominaisuudet ei välttämättä sisälly suoraan kieleen, mutta nähdäkseni kaikki puuttuvat saa helposti ottamalla käyttöön luokan joita on valmiina.

Ja jos nyt 1 tai 2 löytyy joita ei ole valmiiksi, niin toisin päin (juttuja joita on vaikka Javassa valmiina, mutta ei PL/I:ssä) on sitten tuhansittain.

jalski [13.06.2012 00:42:49]

#

Grez kirjoitti:

Toki jotkin ominaisuudet ei välttämättä sisälly suoraan kieleen, mutta nähdäkseni kaikki puuttuvat saa helposti ottamalla käyttöön luokan joita on valmiina.

Kyllä ohjelmointikielen natiivi datatyyppi luokan tai ulkopuolisen kirjaston käytettävyydessä voittaa ja yleensä aina myös tehokkuudessa.


Itse ainakin kirjoitan esim. kompleksiluvut mielummin suoraan sen sijaan, että näpistelisin jonkun luokan objektien kanssa:

dcl alpha  float;
dcl (p1, p2, p3) float complex;

/* näillä voi laskea suoraan mitä haluaa ja sisäänrakennetut */
/* matemaattiset funktiot tukevat kompleksilukuja            */
p1 = 3+4i;
p2 = 2+3i;
p3 = p1*p2;

/* tulosta ympyrän pisteet */
do alpha = 0 to 359;
  put skip list (p3 * complex(cosd(alpha), sind(alpha)));
end;

Kuinkas Javalla tai C#:llä perustaulukot taipuvat vektori -ja matriisilaskentaan?

PL/I laskee taulukoilla ja strukteilla, joten useimmat matriisilaskennassa tarvittavat operaatiot onnistuvat lähes suoraan.

All pari esimerkkiä:


Matriisitulo:

dcl A (2,3) float;
dcl B (3,5) float;
dcl C (2,5) float;
dcl (i, j) fixed bin;

dcl sum builtin;

put skip list ('Syötä 2x3 ja 3x5 matriisit:');
get list (A, B);

do i = 1 to 2;
   do j = 1 to 5;
      C(i,j) = sum(A(i,*) * B(*,j));
   end;
end;

put skip list ('Matriisitulon tulos on:');
put skip list (C);

Matriisin transpoosi:

    dcl m (4,4) float init ( 1,  2,  3,  4,
                             5,  6,  7,  8,
                             9,  10, 11, 12,
                             13, 14, 15, 16 );

    dcl mT (4,4) float defined m (2sub, 1sub);

	/* tulosta alkuperäinen matriisi */
	put skip list (m);
	/* tulosta matriisin transpoosi  */
    put skip (2)  list (mT);

groovyb [14.06.2012 23:02:22]

#

Itse en kyllä näe noilla "Kuolleilla" kielillä mitään muuta iloa kuin omaksi iloksi vääntäminen. Minun on paha kuvitella että tekisin mitään kaupallista tai edes tarjoaisin yrityksille softaa tehtynä PL/I:llä. Mukavahan noita on opetella kun ei muutakaan keksi, ja iltojen iloa riittää loppuiäksi kun vanhoja menetelmiä alkaa opettelemaan. Toki jos joku erikoinen tilanne tulee, jossa tarvitsee esim. vanhaa PL/I -softaa alkaa muokkaamaan tai vastaavaa, voi olla kaupallistakin hyötyä. Mutta eiköhän se ole aika marginaalista. Moni meistä tekee kuitenkin ohjelmointiä päätyökseen, niin minä en ainakaan jaksa innostua kovin vanhoista asioista, uusienkin menetelmien ja ohjelmointitekniikoiden opettelussa saa vietettyä tarpeeksi iltoja joka kuukausi. Ja kyllä, voihan sillä vaikka win32:sta vääntää. Mutta ei taida monikaan jaksaa edes c++:lla enää MFC:tä tai windows apia kirjoitella, kun tuohon tarkoitukseen on omat ja tehokkaammat (jos ei muuta niin ainakin kustannustehokkaammat ja mielenterveyttä säästävät) menetelmät olemassa.

Mutta matriiseista, tässä C#:n tapoja

Deffi [15.06.2012 08:10:54]

#

Hmmh, taisin ihan vasta nähdä että jossain haettiin PL/1-ohjelmoijaa. Tässä:
http://www.talentway.fi/pages2/save/PL---1-ohjelmoija-249.html

edit. oho, oli 5v vanha ilmotus.

jalski [15.06.2012 10:07:51]

#

Deffi kirjoitti:

edit. oho, oli 5v vanha ilmotus.

:-)
Eiköhän nuo mahdolliset PL/I ohjelmointityöpaikat löydy mainframe puolelta. Varmasti ovat myös harvemmassa kuin Java ja .NET pohjaisten kielten työjarjonta. Tosin kaverit, jotka ovat päteviä järjestelmäohjelmoijan työtä mainframe järjestelmien parissa tekemään ovat myös nykypäivänä harvinaisuuksia. Kuka vaan 13-vuotias nörtti osaa asentaa toimivan unix-järjestelmän, mutta harva osaa generoida toimivan asiakkaan vaatimukset täyttävän MVS-järjestelmän.

groovyb kirjoitti:

Mutta matriiseista, tässä C#:n tapoja

En nähnyt noissa mitään sellaista, mikä tekisi matriisien kanssa touhuamisesta C#:n kanssa mukavampaa puuhaa kuin PL/I:n kanssa. Taulukoita käsiteltiin alkio kerrallaan loopeissa ja suorituskyky oli riippuvainen looppien järjestyksestä. Miksei vain jättäisi moista kääntäjän huoleksi? Kyllä näiden parissa touhutessa edelleen "vanhan liiton" ohjelmointikielet, kuten Fortran ja PL/I vievät voiton.

Grez [15.06.2012 11:04:37]

#

jalski kirjoitti:

Miksei vain jättäisi moista kääntäjän huoleksi?

No eikös se käytännössä niin mene kun viittaa matriisiluokkaan ja vaan käyttää sitä?

Toki jos tarkoitus on puhtaasti pyöritellä matriiseja, niin C# ei ole siihen tarkoitukseen paras työkalu. En tiedä onko PL/I:kään.

Pekka Karjalainen [15.06.2012 19:32:00]

#

Tällaiselta näyttää C++ ja Armadillo-kirjasto, jonka saattoi asentaa Linuxin paketinhallinnan kautta. PL/I-kääntäjää sieltä ei löytynyt, joten vertailu jäi toispuoleiseksi.

#include <armadillo>
#include <iostream>

using namespace arma;

int main() {
    std::cout << "Syötä matriisit Arma-formaatissa:" << std::endl;

    mat A;
    mat B;

    bool ok = true;
    ok &= A.load(std::cin, arma_ascii);
    ok &= B.load(std::cin, arma_ascii);

    if (!ok) {
        std::cerr << "Ohjelma ei voinut lukea matriisejasi" << std::endl;
        return 1;
    }

    std::cout << "Matriisien koot ovat: "
              << A.n_rows << " × " << A.n_cols << " ja "
              << B.n_rows << " × " << B.n_cols << std::endl;

    mat C;
    try {
        C = A * B;
    } catch (std::logic_error e) {
        std::cerr << "Taisipa olla yhteensopimattomat matriisien koot"
                  << std::endl;
        return 1;
    }


    std::cout << "Matriisitulo on: " << std::endl;
    std::cout << C;

    return 0;
}

Tässä on esimerkkisyöte. Tämä on erityinen formaatti, jota kirjasto ymmärtää, mutta sille kyllä kelpaavat muutkin formaatit. Ks. dokumentaatio kirjaston sivulla*.

ARMA_MAT_TXT_FN008
2 3
  1.2 2.3 3.4
  0.1 0.2 0.3

ARMA_MAT_TXT_FN008
3 5
  0.1 0.2 0.3 0.4 0.5
  0.1 0.1 0.1 0.1 0.1
  0.5 0.4 0.3 0.2 0.1

Ja esimerkki sen käytöstä. Matriisit tulevat input.txt-tiedostosta.

$ ./example < input.txt
Syötä matriisit Arma-formaatissa:
Matriisien koot ovat: 2 × 3 ja 3 × 5
Matriisitulo on:
   2.0500   1.8300   1.6100   1.3900   1.1700
   0.1800   0.1600   0.1400   0.1200   0.1000

Tämä toimii minkä tahansa kokoisille matriiseille, mutta yhteensopimattomat koot tuottavat matriisituloa laskiessa tietenkin poikkeuksen. Laitoin hieman virheenkäsittelyäkin mukaan, vaikka se on näin lyhyessä ohjelmassa aika turhaa. Esimerkin vuoksi :)

*: http://arma.sourceforge.net/

jalski [16.06.2012 01:11:02]

#

Pekka Karjalainen kirjoitti:

PL/I-kääntäjää sieltä ei löytynyt, joten vertailu jäi toispuoleiseksi.

Tässä simppeli ohjelmapätkä vertailua varten:

*PROCESS DEFAULT(REORDER) OPTIMIZE(2);

 matrixmul: proc options (main);

   dcl (A, B, C) (*,*) float controlled;
   dcl (i, j, s) fixed bin;
   dcl seed float bin (53) static init (0);
   dcl (start, finish) float (18);

   dcl (sum, random, time, secs)  builtin;

   put skip list ('Matrix multiply test');
   put skip list ('Enter matrix size:');
   get list (s);

   allocate A(s,s);
   allocate B(s,s);

   if seed = 0 then seed = random(time());

   do i = 1 to s;
     do j = 1 to s;
       A(i,j) = 9999 * random();
       B(i,j) = 9999 * random();
     end;
   end;

   allocate C(s,s);

   start = secs();

   do i = 1 to s;
     do j = 1 to s;
       C(i,j) = sum(A(i,*) * B(*,j));
     end;
   end;

   finish = secs();

   put skip list ('Finished...');
   put skip edit ('elapsed time =', finish - start, ' seconds')
     (A, F(10,3), A);

   /*put skip list (C);*/

   free A, B, C;

 end matrixmul;

Ajettava Windows binääri

Omalla hitaahkolla koneellani matriisitulo 500x500 neliömatriiseilla kestää hiukan alle 0.9 sekuntia.

Pekka Karjalainen [16.06.2012 13:06:36]

#

Tässä vastaava ohjelma käyttäen Armadilloa sekä Boostia hieman avuksi. Vieläkään minulla ei ole PL/I-kääntäjää, enkä aio ajaa netistä ladattuja binaareja millään koneellani. En tiedä mikä tuo float controlled on, joten laitoin C++-tyypiksi doublen.

Oikeassa ohjelmassa voisi tarvita edistyineitä operaatioita (esim. erilaisia matriisihajotelmia), jotka Armadillo tarjoaa LAPACK- ja BLAS-kirjastojen kautta. Voit ihan itse testata omien algoritmiesi nopeutta näistä kirjastoista löytyvien kanssa, jos haluat :)

#include <armadillo>
#include <boost/progress.hpp>
#include <iostream>

int main() {
    using arma::Mat;
    using arma::randu;

    std::size_t msize = 0;
    std::cout << "Matrix multiplication test. "
              << "Enter matrix size:\n";
    std::cin >> msize;

    Mat<double> A = randu<Mat<double> >(msize, msize) * 9999;
    Mat<double> B = randu<Mat<double> >(msize, msize) * 9999;
    Mat<double> C(msize, msize);

    std::cout << "Elapsed time: ";
    { // scope for progress_timer
        boost::progress_timer timer;
        C = A * B;
    }
}

Omalla hitaahkolla koneellani matriisitulo 500×500 neliömatriiseilla kestää hiukan alle 0.6 sekuntia.

jalski [16.06.2012 15:19:55]

#

Pekka Karjalainen kirjoitti:

Voit ihan itse testata omien algoritmiesi nopeutta näistä kirjastoista löytyvien kanssa, jos haluat :)

Varmasti löytyy kirjastoista tiettyä tarkoitusta varten optimoidut, nopeammat ja paremmat rutiinit, kuin käyttämäni. Koko juttuhan lähti oikeastaan siitä, että tekeekö näillä 40-50 vuotta sitten alkunsa saaneilla vanhoilla ohjelmointikielillä enää nykypäivänä mitään. Omasta mielestäni näistä löytyy edelleen ominaisuuksia, joita ei välttämättä löydy uusista ohjelmointikielistä.

Laittamani pätkähän ei siis ole mitenkään optimoitu matriisilaskentaan, vaan käyttää PL/I:n perusominaisuutta laskea suoraan taulukoilla.

Mainitsin aiemmin myös kompleksiluvut, joille löytyy tuki suoraan ohjelmointikielestä ilman mitään lisäkirjastoja. Näitä ei myöskään ole sidottu mihinkään tiettyyn tietotyyppiin, kuten C++:lla taitaa olla?

Ohjelmointikielitasolla tuetun record IO:n avulla on helppo toteuttaa tekstipohjainen tietokanta, josta avaimen avulla voidaan hakea tietoa nopeasti.

Omasta mielestäni monet ohjelmointikielet tarjoavat paljon mielenkiintoisia ominaisuuksia, vaikka en välttämättä itse kyseisestä ohjelmointikielestä kokonaisuudessaan pitäisikään.

Voin myös myöntää, että pidän kovasti myös .NET pohjaisista ohjelmointityökaluista vaikka en .NET ohjelmointia hirveämmin olekaan harrastellut. PL/I:stäkin olen kuullut olevan .NET pohjainen versio, ei tosin vielä varmaan yleisesti saatavilla.

Pekka Karjalainen [17.06.2012 18:48:52]

#

Minusta alkaa tuntua, että ainoa tapa miten jalski saa ihmiset lukemaan PL/I-kielestä on esittää ohessa virheellisiä väitteitä muista kielistä, joita sitten hyväuskoiset korjaavat, kuten minä tässä teen.

jalski kirjoitti:

Mainitsin aiemmin myös kompleksiluvut, joille löytyy tuki suoraan ohjelmointikielestä ilman mitään lisäkirjastoja. Näitä ei myöskään ole sidottu mihinkään tiettyyn tietotyyppiin, kuten C++:lla taitaa olla?

No voi kun ei ole. C++:ssa on malleja, joiden avulla on tehty millä tahansa lukutyypillä parametrisoituva tyyppi std::complex<T>. Tosin sen voi parametrisoida vaikkapa merkkijonolla, jos on dadaistinen ohjelmoija :)

Mutta nyt minä lopetan. Muahan trollataan armotta!

jalski [17.06.2012 19:40:39]

#

Pekka Karjalainen kirjoitti:

No voi kun ei ole. C++:ssa on malleja, joiden avulla on tehty millä tahansa lukutyypillä parametrisoituva tyyppi std::complex<T>. Tosin sen voi parametrisoida vaikkapa merkkijonolla, jos on dadaistinen ohjelmoija :)

Täytyy tunnustaa, että en hirveämmin ole noihin C++ templateihin tutustunut. Pidän niitä rumana ja päälle liimattuna tapana toteuttaa lisää toiminnallisuutta ohjelmointikieleen.

Eivät kompleksiluvut silti mitään ykkösluokan kansalaisia ole C++ ohjelmointikielessä. Vai onko tuo C++:n tapa oikeasti mielestäsi selkeämpi kuin PL/I:n tapa antaa muuttujan määrittelyssä sille complex attribuutti, jolloin sitä voidaan koodissa käsitellä matemaattisen notaation mukaisesti suoraan?

Lisäsin muuten tuohon yksinkertaiseen matriisitulon laskennan toteutukseen tuen rinnakkaiselle laskennalle. Tähänkin löytyivät työkalut suoraan ohjelmointikielestä...

Grez [17.06.2012 19:47:37]

#

jalski kirjoitti:

Vai onko tuo C++:n tapa oikeasti mielestäsi selkeämpi kuin PL/I:n tapa antaa muuttujan määrittelyssä sille complex attribuutti

No sanoisin että aika tarkkaan yhtä selkeä..

Milläs tavalla PL/I-kieleen saadaan liimattua lisää toiminnallisuutta ja onko se paljonkin kauniimpi? Eli jos haluaisit kielessä valmiiksi olevan "complex" attribuutin rinnalle tehdä vaikka uber -attribuutin, joka mahdollistaisi uber-numeroavaruuden lisäämisen?

jalski [17.06.2012 22:28:44]

#

Grez kirjoitti:

Milläs tavalla PL/I-kieleen saadaan liimattua lisää toiminnallisuutta ja onko se paljonkin kauniimpi? Eli jos haluaisit kielessä valmiiksi olevan "complex" attribuutin rinnalle tehdä vaikka uber -attribuutin, joka mahdollistaisi uber-numeroavaruuden lisäämisen?

Kiitos kysymästä... ;-)

Normaalit struktuurisen ohjelmointitavan keinot käytettävissä. Rutiinit voi paketoida ja kutsua niitä muissa ohjelmissa.

Mielenkiintoisempi tapa tietysti on käyttää erittäin monipuolista PL/I:n esikääntäjää apuna. Tällä tavalla on mahdollista lisätä uusia komentoja ja lausekkeita ohjelmointikieleen.

Metabolix [17.06.2012 22:38:39]

#

jalski, tulkitsisin Grezin kysymyksen näin: kun kerran C++:n tavalla complex<int> ja uber<int> näyttävät aivan samalta, onko PL/I:ssä jokin uber<int>-merkintää vastaava rakenne ja pysyykö koodi silloin muilta osin täysin yhdenmukaisena kompleksilukujen kanssa? Siis onko mahdollista tehdä ohjelma niin, että kun vain vaihtaa complex-sanat uber-sanoiksi, kaikki toimii oikein?

jalski [18.06.2012 00:27:58]

#

Metabolix kirjoitti:

Siis onko mahdollista tehdä ohjelma niin, että kun vain vaihtaa complex-sanat uber-sanoiksi, kaikki toimii oikein?

Ei nyt ihan suoraan... ;-) Esikääntäjän avulla voisi tietysti esimerkiksi korvata uber atribuutin koodissa kahden alkion kokoisen taulukon määrittävällä dimension attribuutilla. Ensimmäisessä alkiossa olisi siis esim. reaaliosa ja toisessa uber-osa. Helpoin lähestymistapa olisi varmaan esikääntäjän avulla määrittää uusi uber-lauseke ohjelmointikieleen.

esim.

/* määrittele uberluku */
uber dcl(a) type(float) init(2+7u);
/* aseta uberluvun reaaliosa ja uber-osa */
uberluku = uber r(5) u(2);
/* tai */
uberluku = uber set(5+2u);
/* laske yhteen uberlukuja */
uberluku = uber add(a,b,c,d);

Grez [18.06.2012 01:18:22]

#

Niin, kritisoit tuota että C++:ssa templatet on mielestäsi ruma ja päälleliimattu ominaisuus, mutta pidät esikääntäjällä määrittämistä sitten ilmeisesti kauniina ja vähemmän päälleliimattuna?

Itse kyllä pidän helppoa laajennettavuutta ilman että tarvitsee esikääntäjän kanssa pelleillä ihan positiivisena asiana.

jalski [18.06.2012 01:55:52]

#

Grez kirjoitti:

Niin, kritisoit tuota että C++:ssa templatet on mielestäsi ruma ja päälleliimattu ominaisuus, mutta pidät esikääntäjällä määrittämistä sitten ilmeisesti kauniina ja vähemmän päälleliimattuna?

Ainakin tuo esikääntäjä käyttää ymmärrettävää PL/I:n syntaksia...

Laitat muutenkin sanoja suuhuni. Sanoin aiemmin vain, että tuki kompleksiluvuille on ollut PL/I:ssä 40 vuotta, kuten on ollut tuki business tyyppiseen laskentaan desimaaliluvuilla ilman pyöristysvirheitä. C-standardissa nuo ovat huomattavasti tuoreempia lisäyksiä. PL/I osaa myös automaattisesti muuntaa tietotyypistä toiseen. Tämä toki tiettyjen sääntöjen puitteissa...

En ole missään välissä väittänyt, että joku ohjelmointikieli ei noihin asioihin ollenkaan kykenisi. Kyllähän kaikkiin on saatavilla valmiita kirjastoja, tai voihan sitä vaikka itse toteuttaa omiin tarkoituksiinsa riittävät rutiinit.

Grez [18.06.2012 02:30:13]

#

jalski kirjoitti:

Sanoin aiemmin vain, että tuki kompleksiluvuille on ollut PL/I:ssä 40 vuotta

Ja kun pohdittiin kannattaako sitä käyttää tänään, niin sillä ei ole mitään merkitystä. Kukaan ei ole sanonutkaan, etteikö se 40 vuotta sitten olisi ollut "aikaansa edellä".

jalski kirjoitti:

En ole missään välissä väittänyt, että joku ohjelmointikieli ei noihin asioihin ollenkaan kykenisi. Kyllähän kaikkiin on saatavilla valmiita kirjastoja, tai voihan sitä vaikka itse toteuttaa omiin tarkoituksiinsa riittävät rutiinit.

Niin, annoit ymmärtää että hyvä syy käyttää PL/I:tä olisi esimerkiksi sen natiivi tuki kompleksiluvuille. Koska standardikirjasto on melko kiinteä osa ohjelmointikieltä, niin voidaan kyllä sanoa että esimerkiksi C++:ssakin on natiivi tuki kompleksiluvuille.

Eli mielestäni mainitsemasi PL/I:n edut on lopulta aika mitättömiä, etkä ole ainakaan minua onnistunut vakuuttamaan, että PL/I:llä kannattaisi tehdä uusia projekteja.

Pekka Karjalainen [20.06.2012 11:02:05]

#

jalski kirjoitti:

Täytyy tunnustaa, että en hirveämmin ole noihin C++ templateihin tutustunut. Pidän niitä rumana ja päälle liimattuna tapana toteuttaa lisää toiminnallisuutta ohjelmointikieleen.

Tässä on nyt yleinen periaate, jonka haluan jakaa kaikkien putkalaisten kanssa.

Jos kielessä Y on ominaisuus X, niin sinun pitää ensin tutustua siihen konkreettisesti, ennen kuin voit sanoa sitä "rumaksi" tai "päälle liimatuksi". Tämä on ikään kuin se minimivaatimus, että kukaan jaksaa edes kuunnella sinun mielipiteitäsi asiasta. Tämän tutustumisesi jälkeenkin mielipiteesi voi olla huonosti harkittu tai väärä, mutta jos et edes vaivaudu ottamaan asioista selvää, olet jo ihan lähtökohtaisesti todennäköisesti väärässä, eikä mielipiteelläsi ole mitään painoa.

Tämä pätee kaikilla mahdollisilla muuttujan X ja Y arvoilla.

Totta kai C++:n templateissa (tai malleissa suomeksi) on puutteita. Onhan se huono puoli, että voi määritellä tyypin std::complex<std::string> ja saa virheen vasta, kun esimerkiksi yrittää kertoa kahta tämän tyypin muuttujaa keskenään. Parempi virheilmoitus olisi määrittelyn yhteydessä selvä teksti, joka sanoisi, että parametrin tulee olla lukutyyppi, ja std::string ei ole luku.

Valitettavasti kieliin ei voi lisätä rajattomasti ominaisuuksia ihan jokaisen toiveen mukaan. C++:ssakin on keskitytty siihen, mikä tekee sen käytettäväksi kielelle parhaiten sopiviin tehtäviin. Käytäntö on osoittanut, että näitä tehtäviä mihin C++ sopii hyvin on useita. Se riittää minulle perusteluksi sille, miksi harrastan C++:aa ja harrastan jatkossa. Template-rakenteiden kanssa voi elää. Minä ainakin voin. (Tiesin jo pienenä, että merkkijonot eivät ole lukuja. Kaikkea sitä lapsi oppii :-)

Jos löydän johonkin tarpeeseen paremman kielen kuin C++, käytän sitä mahdollisuuksien mukaan tähän tarpeeseen. Yleensä tämä arvio siitä, mikä on milloinkin parempi kieli kuin toinen, on hyvin vaikea tehdä kunnolla, koska siinä on hyvin monta muuttujaa. Valinnan vaikeuksien välttämiseksi on helpointa turvautua kieliin, joilla on vakiintunut asema, eli paljon käyttäjiä ja hyvin toimivat ohjelmointiympäristöt. Nyt en halua nimetä nimiä, mutta jotkut kielet kuuluvat tähän ryhmään, ja jotkut eivät. Että se siitä.

Tähän päättyy tiedoite yleisistä periaatteista. Kiitos.

jalski [20.06.2012 13:57:25]

#

Pekka Karjalainen kirjoitti:

Jos kielessä Y on ominaisuus X, niin sinun pitää ensin tutustua siihen konkreettisesti, ennen kuin voit sanoa sitä "rumaksi" tai "päälle liimatuksi". Tämä on ikään kuin se minimivaatimus, että kukaan jaksaa edes kuunnella sinun mielipiteitäsi asiasta.

Huomasitko muuten kysymysmerkin alkuperäisen argumenttini perässä, johon tartuit alunperin niin kärkkäästi? Tiedät kai kysymysmerkin käyttötarkoituksen kieliopissa?

C++ templateista en tiedä, koska olen joskus kauan sitten vanhalla kääntäjällä pelkästään perus C-ohjelmointia tehnyt. Silti omat tietosikaan PL/I-ohjelmoinnista tuskin ovat omia C++ tietojani paremmat.

Mitä tulee tuohon merkkijonojen lukuina käsittelemiseen niin tämänkin voi jättää PL/I:n huoleksi:

dcl n  fixed bin(31);
dcl nx char(10) varying;

on conversion
  begin;
    display( 'Syötteen pitää olla numeerinen' );
    goto recover;
  end;

recover:
  display( 'Syötä luku?') reply( nx );

n = nx;

Grez [20.06.2012 14:33:08]

#

jalski kirjoitti:

Tiedät kai kysymysmerkin käyttötarkoituksen kieliopissa?

Mikä muuten on kysymysmerkin tarkoitus kieliopissa? Se kuuluu toki laittaa kysymysvirkkeen perään, mutta silti toteamuksesta ei tule kysymysvirkettä pelkästään laittamalla pisteen tilalle kysymysmerkki.

Toki kysymysmerkin käytöstäsi ko. yhteydessä voi päätellä että virke on virheellinen, eikä sitä kannata ottaa kovin vakavasti.


Sivun alkuun

Vastaus

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

Tietoa sivustosta