Summor i APL och J
På den tiden när Matlab inte fanns skrev man normalt numerisk programvara i Fortran. Detta var (och är) mycket tidsödande och gjorde att villigheten att t.ex. utföra experiment minskade.
Ett språk som användes av somliga numeriker för att laborera med algoritmer var APL, ett kraftfullt språk vad gäller beräkningar och med en mycket kompakt notation. Carl-Erik Fröberg, en välkänd numeriker i Lund (nu professor emeritus) var t.ex. medförfattare till boken "Programmera i APL", Studentlitteratur, 1976. Språket är mycket generellt vad gäller t.ex. flerdimensionella fält; om något kan göras för vektorer och matriser så kan man raskt utföra det i godtyckligt många dimensioner, till exempel. Det finns loopar etc. men precis som i Matlab är de inte så nödvändiga, man tenderar att arbeta på fältnivå.
Man körde APL på speciella terminaler eftersom språket utnyttjade ASCII, grekiska bokstäver, diverse specialsymboler och överskrivningar av dessa. Detta var en viktig orsak till att språket inte blev mera spritt. En annan orsak var väl att APL-program kunde vara mycket svårlästa. Så kallade one-liners var inte ovanliga och kanske skrev man ett nytt program i stället för att ändra i det gamla, oläsliga ("write-only language")
Här är t.ex. en rad för att beräkna primtal. Uttrycket är i form av en bild (men det finns numera APL-typsnitt för webläsare). Om Du klickar på bilden så får Du en analys av uttrycket (och hamnar på sidan där jag tog bilden).
APL innehöll inslag av funktionell programmering, något som är konsekvent genomfört i språket J, en modern ASCII-variant av APL. Ett gratissystem finns att hämta hos www.jsoftware.com.
Här följer några J-exempel. För att beskriva språket används terminologi hämtad från engelsk grammatik. En funktion är ett verb, ett substantiv är ett argument (lista av tal) till ett verb etc. En operator (en funktion som verkar på en funktion) kallas adverb (adverb kan appliceras på verb).
Låt oss börja med att bild summan 1 / 1 + ... + 1 / 10. Så här kan det se ut, där jag har visat lämpliga delsteg i J-interpretatorn. Nämnas bör att J liksom APL utför operationer från höger till vänster. Så blir t.ex. 2 * 3 + 4 lika med 14 (dvs. 2 * (3 + 4)) men vi kan ändra på ordningen med hjälp av parenteser, så (2 * 3) + 4 blir 10.
i. 10 i.ger en lista (i APL användes den grekiska bokstaven jota i stället) 0 1 2 3 4 5 6 7 8 9 1 + i. 10 elementvis addition med ett 1 2 3 4 5 6 7 8 9 10 % 1 + i. 10 unär form av division ger "ett genom" 1 0.5 0.333333 0.25 0.2 0.166667 0.142857 0.125 0.111111 0.1 +/ % 1 + i. 10 applicera adverbet / på verbet + för att skapa ett listsummerande verb 2.92897 10 - i. 10 här adderar jag från andra hållet 10 9 8 7 6 5 4 3 2 1 +/ % 10 - i. 10 2.92897
Låt oss göra ett litet exempel till. Antag att vi kastar pil (slumpmässigt, likformig fördelning) på enhetskvadraten. Andelen träffar inom enhetscirkeln bör då ge en (dålig) approximation av pi eftersom arean ju är pi. Jag har gjort ett litet program i J för att simulera kastandet, där jag kastar enbart i första kvadranten. Programmet visar också att J har stöd för komplexa tal.
rand =: 3 : '(% 2147483647) * ? y. $ 2147483647'
rand 5
0.131538 0.755605 0.45865 0.532767 0.218959
pi_approx =: 3 : '(4 % y.) * +/ 1 > | (rand y.) j. rand y.'
pi_approx 10000
3.1312
pi_approx 100000
3.13872
pi_approx 1000000
3.14116
(10 ^ 1 + i.6)
10 100 1000 10000 100000 1e6
pi_approx " 0 (10 ^ 1 + i.6)
3.2 2.8 3.056 3.1484 3.1442 3.14244
Jag börjar med att skapa verbet rand
, i monadisk
form (dvs.
en funktion som tar ett argument till höger, t.ex func arg
).
Dyadisk form vore (vänsterargument funktion högerargument).
Trean
talar om vilken sorts definition vi gör (inte mycket
syntaktiskt
socker här inte) och strängen innehåller
funktionskroppen. I funktionen refererar jag till högerargumentet
med
y.
(ett vänsterargument har namnet x.
).
Slumptalsgeneratorn genererar tal i intervallet (0,
2147483647)
och y. $ 2147483647
ger en lista med y.
stycken
kopior av 2147483647
. Slumptal genereras med ? tal
,
som skapar slumptal i (0, tal)
. Vi applicerar ?
på varje element i listan (vi får en lista av slumptal).
Jag
multiplicerar sedan listan elementvis med (% 2147483647)
för
att skapa slumptal i (0, 1). rand 5
på nästa
ger
oss alltså fem slumptal i (0, 1).
Fullt så här långrandiga behöver vi inte vara i J. En kortare lösning ges av:
rand =: 3 : '(% max) * ? y. $ max =. 2147483647'
max =. 2147483647
är en tilldelning till den
lokala variabeln
max
(som skapas) och sedan används två
gånger.
Jag har sedan skapat verbet pi_approx
som genererar
två
listor av slumptal och sedan sätter samman dessa till en lista av
komplexa
tal. (rand y.) j. rand y.
Realdelen utgörs av (rand
y.)
och rand y.
imaginärdel. j.
utgör
(ungefär) imaginära enheten. Jag applicerar sedan
beloppsverbet,
|, elementvis på den komplexa listan, och kontrollerar sedan
vilka
som är mindre än 1. Detta ger en lista av ettor (sant) och
nollor
(falskt) Jag adderar ihop ettorna (det komplexa talet ligger
innanför
enhetscirkeln i första kvadranten), multiplicerar med fyra (en
kvadrant
bara) och dividerar med antalet kast. Sen har jag testat
pi_approx
på några fall och sista har jag
applicerat
pi_approx
på elementen i en lista. " 0
talar
om att mitt verb skall appliceras elementvis på listan och inte
en
gång på hela listan som en enhet.
Låt oss avsluta med några trigonometriska funktioner.
pi =: 4 * _3&o. 1 3&o. står för tan och _ betecknar minus (som tecken) så pi _3&o. är givetvis arctan (inversen) 3.14159 1&o. pi 1&o. är sinus 0 sin =: 1&o. om vi inte kommer ihåg detta kan vi definiera sin sin pi 0 sin pi % 2 sin pi / 2 1