start -> id (k (f c s) (g s c)) -> k (f c s) (g s c) -> f c s -> s c



Vergelijkbare documenten
1 Rekenen in eindige precisie

College Notatie, Recursie, Lijsten

1 Inleiding in Functioneel Programmeren

Vorig college. IN2505-II Berekenbaarheidstheorie College 4. Opsommers versus herkenners (Th. 3.21) Opsommers

Inleiding Programmeren 2

Elfde college complexiteit. 23 april NP-volledigheid III

Als een PSD selecties bevat, deelt de lijn van het programma zich op met de verschillende antwoorden op het vraagstuk.

Gaap, ja, nog een keer. In één variabele hebben we deze formule nu al een paar keer gezien:

HOOFDSTUK 3. Imperatief programmeren. 3.1 Stapsgewijs programmeren. 3.2 If Then Else. Module 4 Programmeren

Wat is FP? The Haskell School of Expression. Functies. Types 1+1=2. Iedere expressie (en waarde) heeft een type.

We beginnen met de eigenschappen van de gehele getallen.

Variabelen en statements in ActionScript

Getallensystemen, verzamelingen en relaties

1.3 Rekenen met pijlen

8.1 Herleiden [1] Herleiden bij vermenigvuldigen: -5 3a 6b 8c = -720abc 1) Vermenigvuldigen cijfers (let op teken) 2) Letters op alfabetische volgorde

Practicum Programmeerprincipes

3 De stelling van Kleene

10 Meer over functies

Nu een leuk stukje wiskunde ter vermaak (hoop ik dan maar). Optellen van oneindig veel getallen

V.2 Limieten van functies

Inleiding Programmeren 2

Modelleren en Programmeren

Constanten. Variabelen. Expressies. Variabelen. Constanten. Voorbeeld : varid.py. een symbolische naam voor een object.

8.1 Herleiden [1] Herleiden bij vermenigvuldigen: -5 3a 6b 8c = -720abc 1) Vermenigvuldigen cijfers (let op teken) 2) Letters op alfabetische volgorde

Recursion. Introductie 37. Leerkern 37. Terugkoppeling 40. Uitwerking van de opgaven 40

Ontwerp van Algoritmen: opgaven weken 3 en 4

Algoritmen abstract bezien

Practicumopgave 3: SAT-solver

Uitleg van de Hough transformatie

Aanvulling bij de cursus Calculus 1. Complexe getallen

1. Vectoren in R n. y-as

Programmeren in Java les 3

extra oefening algoritmiek - antwoorden

Faculteit Elektrotechniek - Capaciteitsgroep ICS Tentamen Schakeltechniek. Vakcodes 5A010/5A050, 19 januari 2004, 9:00u-12:00u

Talen & Automaten. Wim Hesselink Piter Dykstra Opleidingsinstituut Informatica en Cognitie 9 mei 2008

Logisch en Functioneel Programmeren voor Wiskunde D

III.2 De ordening op R en ongelijkheden

Machten, exponenten en logaritmen

Uitgebreide uitwerking Tentamen Complexiteit, mei 2007

Verzamelingen, Lijsten, Functioneel Programmeren

Paradox van zelfreproductie. IN2505-II Berekenbaarheidstheorie. Zelfreproductie? Programma s en zelfreproductie. College 11.

TENTAMEN Basismodellen in de Informatica VOORBEELDUITWERKING

Zoemzinnen. Algemene info. Functies met een variabel aantal argumenten

2. Optellen en aftrekken van gelijknamige breuken

Onafhankelijke verzamelingen en Gewogen Oplossingen, door Donald E. Knuth, The Art of Computer Programming, Volume 4, Combinatorial Algorithms

Automaten. Informatica, UvA. Yde Venema

Bijzondere getallen. Oneindig (als getal) TomVerhoeff. Technische Universiteit Eindhoven Faculteit Wiskunde en Informatica

OEFENINGEN PYTHON REEKS 1

Introductie tot de cursus

Semantiek (2IT40) Bas Luttik. HG 7.14 tel.: Hoorcollege 8 (7 juni 2007)

Vakgroep CW KAHO Sint-Lieven

Combinatorische Algoritmen: Binary Decision Diagrams, Deel III

College Introductie

HOE TEKEN IK EEN OMGEVINGSMODEL

Praktisch bestaan er enkele eenvoudige methoden om een decimaal getal om te zetten naar een binair getal. We bespreken hier de twee technieken.

Haskell: programmeren in een luie, puur functionele taal

Stelling. SAT is NP-compleet.

Het omzetten van reguliere expressies naar eindige automaten, zie de vakken Fundamentele Informatica 1 en 2.

recursie Hoofdstuk 5 Studeeraanwijzingen De studielast van deze leereenheid bedraagt circa 6 uur. Terminologie

opgaven formele structuren tellen Opgave 1. Zij A een oneindige verzameling en B een eindige. Dat wil zeggen (zie pagina 6 van het dictaat): 2 a 2.

Helden van de wiskunde: L.E.J. Brouwer Brouwers visie vanuit een logica-informatica perspectief

[8] De ene 1 is de andere niet

Verzamelingen, Lijsten, Functioneel Programmeren

equationeel programmeren college 1

Divide & Conquer: Verdeel en Heers vervolg. Algoritmiek

Hoofdstuk 1. Inleiding. Lichamen

Verzamelingen, Lijsten, Functioneel Programmeren

Bijzondere kettingbreuken

rh276a 0 We breiden nu bovenstaand programmafragment uit door assignments toe te voegen aan een nieuwe variabele m, aldus:

Verzamelingen deel 3. Derde college

Een eenvoudig algoritme om permutaties te genereren

Zo gaat jouw kunstwerk er straks uitzien. Of misschien wel heel anders.

Hoofdstuk 3. Equivalentierelaties. 3.1 Modulo Rekenen

Tentamen Imperatief en Object-georiënteerd programmeren in Java voor CKI

Fundamenten van de Informatica

Definitie 1.1. Een partitie van een natuurlijk getal n is een niet stijgende rij positieve natuurlijke getallen met som n

Min maal min is plus

Uitwerking tentamen Analyse van Algoritmen, 29 januari

Uitwerking Puzzel 93-1, Doelloos

Tentamen Discrete Wiskunde 1 10 april 2012, 14:00 17:00 uur

Ter Leering ende Vermaeck

TENTAMEN WISKUNDIGE BEELDVERWERKINGSTECHNIEKEN

Termherschrijven. Jan van Eijck CWI. Achtergrondcollege Software Evolution, 22 september 2005

Gödels theorem An Incomplete Guide to Its Use and Abuse, Hoofdstuk 3

Programmeermethoden NA. Week 5: Functies (vervolg)

Een topprogrammeur in het OO programmeren is Graig Larman. Hij bedacht de volgende zin:

Vierde college complexiteit. 26 februari Beslissingsbomen en selectie Toernooimethode Adversary argument

Netwerkstroming. Algoritmiek

Java Les 3 Theorie Herhaal structuren

II.3 Equivalentierelaties en quotiënten

Volledige inductie. Hoofdstuk 7. Van een deelverzameling V van de verzameling N van alle natuurlijke getallen veronderstellen.

IN2505 II Berekenbaarheidstheorie. IN2505-II Berekenbaarheidstheorie. Practicum: Inschrijven. Practicum

EXCEL BASIS 2013

RSA. F.A. Grootjen. 8 maart 2002

Logische Complexiteit

Breuken met letters WISNET-HBO. update juli 2013

Modelleren en Programmeren

PYTHON REEKS 1: BASICS. Mathias Polfliet

Bouwstenen voor PSE. Datatypes en Datastructuren

Oneindig in Wiskunde & Informatica. Lezing in de reeks Oneindig 3 oktober 2007 / Studium Generale TU Delft. Tom Verhoeff

Transcriptie:

Een Minimaal Formalisme om te Programmeren We hebben gezien dat Turing machines beschouwd kunnen worden als universele computers. D.w.z. dat iedere berekening met natuurlijke getallen die met een computer gedaan kan worden, ook met een Turing machine uitgevoerd kan worden. Het omzetten van het algoritme naar een Turing machine is hierbij meestal geen eenvoudige zaak. In het algemeen is het oorspronkelijke algoritme ook niet meer te herkennen na de transformatie. Een Turing machine is weliswaar een abstracte computer maar kan in principe ook gebouwd worden en is daarom ook als een implementatie van het algoritme te beschouwen. Bedenk hierbij wel dat je voor ieder algoritme een aparte Turing machine moet bouwen. In dit hoofdstuk zullen we een heel andere benadering hanteren we het programmeerformalisme als uitgangspunt ipv een (abstracte) machine die het algoritme uitvoert. Het streven is om met een minimum aan formalisme een maximum aan expressie kracht te krijgen. Het verrassende is dat er een formalisme mogelijk is dat maar op 1 regel is gebaseerd. Het formalisme dat we hanteren is gebaseerd op de Lambda Caluclus. De Lambda Calculus werd in de jaren 30 van de vorige eeuw door Alonzo Church ontwikkeld als een alternatief fundament voor de wiskunde (i.p.v. logica). Al snel bleek dat de Lambda Calculus ook zeer geschikt is om algoritmen mee te beschrijven. Alan Turing bewees dat de Lambda Calculus dezelfde klasse van algoritmen kan beschrijven als zijn Turing machines. De Lambda Calculus is nogal abstract en lastig te leren. Daarom gebruiken wij hier een wat meer toegankelijke versie die meer op een gewone programmeertaal lijkt. Definitie van een minimale programmeertaal Een programma in onze programmeertaal bestaat uit een aantal functies met een start functie. Een functie is een voorschrift waarmee het ene patroon door een ander kan worden vervangen. Hier een aantal voorbeelden: f a b = b a g x y = f x y id x = x k x y = x c x y z = x (y z) s f g x = f x (g x) app f x = f x start = id (k (f c s) (g s c)) Een functie bestaat uit een naam gevolgd door een aantal variabele namen (0 of meer), gevolgd door = gevolgd door een functie body. In de functie body mogen alleen namen van variabelen en namen van andere functies uit ons programma en haakjes om te groeperen verschijnen. Een functie body is altijd een functie applicatie (het toepassen van een functie op een aantal argumenten). Hierbij mag de functie die wordt toegepast ook een variabele zijn. De argumenten mogen functies, variabelen of andere functie toepassingen zijn. Bv. in de functie f wordt in de body a toegepast op b. In de functie c wordt x toegepast op de toepassing van y op z. Het runnen van een programma bestaat nu uit, beginnende met de start functie, alle functie applicaties uit te voeren tot dit niet meer verder kan. Het uitvoeren van een functie applicatie bestaat uit het vervangen van een functie aanroep door de rechterkant van de bijbehorende functie waarbij op de plaats van de variabelen de actuele argumenten worden ingevuld. In ons voorbeeld wordt dit: start -> id (k (f c s) (g s c)) -> k (f c s) (g s c) -> f c s -> s c De laatste functie applicatie kan niet meer herschreven worden omdat s 3 argumenten verwacht en er maar 1 krijgt. De afspraak is dan dat we niet verder herschrijven. Er zijn verschillende vragen te stellen. 1. Maakt het uit in welke volgorde je functie herschrijvingen uitvoert? 2. Eindigt het herschrijfproces altijd? 1

Bv. we hadden bij de tweede pijl ook kunnen kiezen om eerst het argument van id te herschrijven en daarna pas de id functie uit te voeren. Hetzelfde geldt voor de derde pijl. Hier hadden we eerst de argumenten van k kunnen herschrijven en daarna k toe te passen. In dit geval leidt dit tot extra werk omdat het tweede argument van k wordt weggegooid. Probeer zelf in het bovenstaande voorbeeld verschillende volgorden uit. Het zal blijken dat dit voor het eindresultaat niets uit maakt. Het is gemakkelijk voorbeelden te geven programma s die niet eindigen. f x = f (f x) id x = x start = f id Het herschrijf proces wordt nu: start = f id -> f (f id) -> f (f (f id)) ->... In dit geval eindigt de herschrijving niet. Voor het herschrijven zullen we de strategie hanteren dat we steeds de buitenste functie aanroep proberen te herschrijven tot dit niet meer verder gaat en dat we daarna de argumenten van de overgebleven functie van links naar rechts gaan herschrijven (waarbij we weer dezelfde regel hanteren). Deze herschrijfstrategie heet left-outer-most reductie. Er geldt nu de volgende stelling: Stelling (Church-Rosser) Als er een voor een programma een eindigende herschrijfstrategie bestaat, dan zal left-outer-most reductie ook eindigen en bovendien zal het resultaat hetzelfde zijn. Het bewijs van deze stelling is zeer technisch en zullen we daarom achterwege laten. Intuïtief is het resultaat wel te snappen omdat de buitenste functie applicatie nooit kan wegvallen en daarom op zeker moment altijd uitgevoerd moet worden. Het moment van herschrijven van de buitenste functie applicatie kan daarom het al of niet eindigen van het programma niet veranderen. De antwoorden op bovenstaande vragen zijn dus: 1. De volgorde maakt alleen uit als de ene volgorde eindigt en de andere niet. Alle eindigende herschrijf strategieën leiden tot hetzelfde resultaat. 2. Nee. Hiermee hebben we het basis formalisme gedefinieerd. Het lijkt op het eerste gezicht een weinig bruikbaar formalisme. Er zitten bv geen getallen en bewerkingen op getallen in het formalisme. Deze zullen we zelf moeten definiëren. De enige regel die we hebben is het vervangen van linkerkanten door rechterkanten. In deze zin is het formalisme erg minimaal. Alvorens verder te gaan maken een kleine uitbreiding op het formalisme, n.m.l. anonieme functies. Deze vereenvoudigen de notatie in veel gevallen maar veranderen de kracht van het formalisme niet. Een anonieme functie heeft de vorm \v1 v2 -> body en mag aan de rechter kant van functie definities verschijnen op iedere plek waar ook een functie aanroep mag staan. We geven een paar voorbeelden. h = (\x y -> x) c s f a b = app (\x -> c x a) b We kunnen een definitie met anonieme functies altijd vervangen door een aantal definities zonder anonieme functies. In het eerste voorbeeld kunnen we de anonieme functie vervangen door k. Het tweede voorbeeld kunnen we vervangen door twee functies: f a b = app (f a) b f a x = c x a 2

Men kan eenvoudig nagaan dat de nieuwe functies samen hetzelfde resultaat opleveren als de oorspronkelijke definitie met anonieme functie. Let er op dat in de aanroep van de nieuwe functie f a als extra argument zit. Getallen als functies Om te kunnen rekenen hebben we in ieder geval getallen en hun operaties (optellen, aftrekken, vermenigvuldigen, delen, etc.) nodig. Voor de definitie van getallen vallen we terug op de Peano axioma s. Peano was de eerste die een formele definitie van natuurlijke getallen gaf. Voor die tijd beschouwde men natuurlijke getallen als een (goddelijk) gegeven. Het principe van volledige inductie zoals dit in het dictaat discrete wiskunde wordt behandeld is gebaseerd op de Peano Axioma s. Het idee van Peano was de natuurlijke getallen onder te verdelen in twee klassen. De ene klasse bevat alleen het getal 0. Deze andere klasse bevat alle getallen die een opvolger van een natuurlijk zijn (alle andere getallen). We zullen 0 in het Peano formalisme zero noemen en de opvolger van het getal n aanduiden met suc n (successor n). Op deze wijze is 1: suc zero, 2: suc (suc zero), etc. We beginnen al functies in het bovenstaande formalisme te herkennen. Maar we zijn er nog niet. Laten we eerst naar de optel operatie van natuurlijke getallen kijken. We kunnen deze als volgt definiëren: add n zero = n add n (suc pm) = suc (add n pm) In woorden: Als we 0 bij n optellen is het resultaat n. Als we de opvolger van pm bij n willen optellen is het resultaat de opvolger van het resultaat van de som van n en pm. Omdat de laatste optelling eenvoudiger is dan de oorspronkelijke zal de recursie uiteindelijk eindigen als pm 0 is geworden. De definitie boven lijkt een aardige poging getallen en optelling in ons formalisme te definiëren, maar de definities hebben ook aan de linker kant een patroon (zero en suc m) en dit is niet toegestaan. Bovendien hebben we voor zero en suc nog geen definitie. We geven nu een definitie van zero, suc en add in bovenstaand formalisme en zullen m.b.v een aantal voorbeelden laten zien dat deze definities inderdaad tot de gewenste resultaten leiden. zero = \f g -> f suc n = \f g -> g n add n m = m n (\pm -> suc (add n pm)) We zien dat in de definities volop gebruik wordt gemaakt van anonieme functies. In zero en suc is dit niet echt nodig (we kunnen de definities vervangen door: zero f g = f en suc n f g = g n). add zouden we zonder anonieme functies naar 2 minder goed leesbare functies moeten transformeren. In add zien we dat we het argument m echt als functie gebruiken en toepassen op 2 argumenten. Bekijk nu het herschrijven van de expressie add (suc zero) (suc zero) add (suc zero) (suc zero) -> def add (suc zero)(suc zero)(\pm -> suc (add (suc zero) pm)) -> (f a) b == f a b suc zero(suc zero)(\pm -> suc (add (suc zero) pm)) -> def. suc (\pm -> suc (add (suc zero) pm)) zero -> toepassing suc (add (suc zero) zero)) -> def add suc (zero (suc zero) ((\pm -> suc(add (suc zero) pm))) -> def zero suc (suc zero) Dit lijkt misschien allemaal heel ingewikkeld, maar het komt precies op hetzelfde neer als de definitie van optellen die we boven hebben gegeven. We maken handig gebruik van het feit dat een getal nu een functie is die je kunt toepassen op twee argumenten. Als het getal 0 is selecteert dit het eerste argument. Als het getal de opvolger is van een ander, dan selecteert dit het tweede argument en past dit toe op de ander. Daarom moet het tweede argument van m in add ook een functie zijn. Je kunt de definitie ook lezen als: 3

add n m = n, if m == 0 en add n m = suc (add n pm), if m == suc pm. Opgave Definieer zelf vermenigvuldiging en uitgaande van optelling. Doe hetzelfde met machtsverheffen. Opgave Definieer de functie pred, die de voorganger van een getal geeft. Definieer hierbij de voorganger van 0 als 0. Gebruik deze definitie om aftrekken (sub) te definiëren. Probeer ook een rechtstreekse definitie van sub te geven. Opgave Definieer de faculteit functie gebruikmakend van bovenstaande definitie van getallen. We zien dat het formalisme krachtig genoeg is om natuurlijke getallen en de operaties daarop te definiëren. In het voorafgaande hebben we de functionele programmeertaal Amanda gebruikt. Programma s in Amanda kunnen op een recht-toe-recht-aan manier naar dit formalisme geconverteerd worden. We hebben al gezien hoe we natuurlijke getallen representeren. We kijken nu naar de definitie van lijsten. In Amanda heeft een lijst de vorm: [] of (x:xs). We definiëren nu voor [] de functie Nil en voor (x:xs) de functie Cons. Nil = \f g -> f Cons x xs = \f g -> g x xs We kunnen nu b.v de functie length als volgt definieren: length xs = xs zero (\x xs -> suc (length xs)) Andere data typen kunnen op analoge wijze geconverteerd worden. Opgave Geef zelf een implementatie van binaire bomen en de functie depth. Opgave Bedenk een representatie voor True, False en de if constructie. Amanda beschikt over een aantal syntactische constructies zoals lijstcomprehensies en pattern matching. Ook deze constructies zijn om te werken naar constructies binnen het minimale formalisme. Turing Machines en het minimale formalisme We hebben al gezien dat het eenvoudig is om een Turing machine in Amanda (en daarom ook in het minimale formalisme) te implementeren. Het omgekeerde is veel lastiger. Het minimale formalisme is weliswaar eenvoudig maar lastig m.b.v een Turing machine uit te drukken omdat we in feite de herschrijfregel (links door rechts vervangen) moeten uitdrukken. Omdat de herschrijfregels willekeurig kunnen zijn is dit lastig. We zullen daarom een programma niet rechtstreeks op een Turing machine afbeelden, maar via een tussenstap. De tussenstap is een vertaling van een programma naar een equivalent programma waarin maar 3 verschillende functies in voorkomen. We geven eerst de definities van deze 3 functies. I x = x K x y = x S f g x = f x (g x) I is de identiteitsfunctie, K gooit zijn tweede argument weg en S past zijn eerste argument toe op het derde argument en op de toepassing van het tweede op het derde argument. Het blijkt nu dat we iedere functie kunnen om schrijven in termen van I, K en S. We beginnen met een aantal voorbeelden. De truc is dat we de rechterkant van de functie f x1.. xn = rechts-expr net zolang herschrijven tot dat deze van de vorm SKI-expr x1.. xn is, waarbij SKI-expr een uitdrukking is die alleen maar S,K, I en andere functie namen bevat en geen 4

variabelen meer. Bij het omschrijven werken we een voor een de variabelen weg te beginnen bij de laatste. h x y = y => I y => K I x y f x y z = x (y z) => S (K x) y z => S (K S) K x y z t x y z = y z (x z) => S y x z => S S (K x) y z => S (K (S S)) K x y z Er geldt dus: h = K I f = S (K S) K t = S (K (S S)) K Als een functie nog andere functies bevat kunnen deze op het eind vervangen worden door hun SKI representaties. Hoewel het omzetten van een functie naar het SKI formaat ingewikkeld lijkt is het algoritme wat deze omzetting uitvoert vrij kort. Een Turing machine die SKI expressie kan evalueren We schetsen nu een Turing machine die SKI expressies kan evalueren. We gebruiken een Turing machine met 6 tapes. Tape 0 bevat de invoer en uiteindelijk het eindresultaat. Er zijn 3 tapes (Arg0, Arg1 en Arg2) die de argumenten van een expressie bevatten (maximaal 3 voor een S expressie). Tape 4 is de to do expressie stack. Hierop kunnen meerdere expressies gescheiden door een separator geplaatst worden. De expressies op deze tape staan in de volgorde waarin ze geëvalueerd moeten worden. Tape 5 is de huidige expressie stack. Deze bevat de expressie die op dit moment geëvalueerd wordt. Het algoritme start met het kopiëren van de input naar de to do stack. Op de oorspronkelijke invoertape wordt het markeringssymbool # gezet. Op deze plaats moet straks het resultaat neergezet worden. 1. Het algoritme begint met een check of er nog iets op de to do tape staat. Zo nee, dan is het resultaat dat wat op de invoer tape staat. Staat er wel wat op de to do stack dan wordt de eerste expressie naar tape 5 gekopieerd. We gaan nu verder met wat op tape 5 staat. 2. Als dit begint met een I dan checken we eerst of er wat na de I staat. Als dit zo is dan kan I verwijderd worden waarna het algoritme opnieuw begint met de rest van de invoer (I is immers de identiteitsfunctie). Als er na de I niets meer staat dan is het resultaat I en wordt teruggeschreven naar de positie van de eerste # op de invoer tape. 3. Als deze begint met een K dan checken we eerst hoeveel argumenten er na de K op de invoer staan. Dit is een kwestie van tellen hoeveel geneste haakjes expressies er op de invoer na de K staan. a. Als dit er twee of meer zijn dan moeten de K en het tweede argument verwijderd worden. Waarna het algoritme opnieuw begint op de plaats waar K stond in de invoerstring. b. Als er 0 argumenten zijn de is het resultaat K en zijn we klaar. c. Als er 1 argument is dan moet de K blijven staan en herhalen we het algoritme voor het ene argument. 4. Als de expressie met een S begint dan checken we eerst het aantal argumenten na de S. a. Als dit er 3 of meer zijn dan kopiëren we de eerste 3 argumenten naar resp Arg0, Arg1 en Arg2, waarbij we ze samen met de S verwijderen van de invoer. Daarna kopiëren we de string Arg0 Arg2 (Arg1 Arg2) terug naar de positie waar S stond en herhalen het algoritme vanaf de positie waar S stond. b. Als er 0 argumenten zijn dan is het resultaat S. c. Als er 1 argument is Opmerking Als we ons echter beperken tot een vast programma dan is het wel mogelijk een Turing machine te maken die het programma kan uitvoeren zonder de tussenstap via SKI expressies te maken. Je hoeft 5

dan immers maar een vast aantal functies te kunnen executeren (net zoals voor de SKI machine). De constructie gaat op vergelijkbare wijze. 6