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).

primtal

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


Back