4.2 Primitive Rekursion: Konstruktionen, Tricks
Ich beginne dieses Teilkapitel, manche der primitiv-rekursive Implementierungen in stockpile.py zu erklären und werde dann weitere, weniger offensichtliche Konstruktionen diskutieren.
Arithmetische Operationen
Wir haben bereits gesehen, dass wir die Addition permult
in der Schleifenform, wie sie die primitive Rekursion zulässt, und schreiben in Orange
auch gleich die Funktionen def mult(t,x): temp = 0 = zero(x) for i in range(t): temp = add(temp,x) = Comp(add, p_0, p2) (temp,i,x) return temperinnern Sie sich daran:
def factorial(t): temp = 1 = one() for i in range(t): temp = mult(temp,i+1) = Comp(mult, p_0, Comp(succ, p_1)) (temp,i) return tempWir können nicht einfach
Operationen wie
def pred(t): temp = 0 = zero() for i in range(t): temp = i = p1 (temp,i) return tempIn Worten: wenn
Für minus schreiben wir uns erst mal
Boolesche Werte
Die primitive Rekursion stellt uns als "Datentyp" nur die natürlichen Zahlen zur
Verfügung. Alles andere müssen wir als natürliche Zahlen nach einem von uns
selbst gewählten Schema codieren. Für Boolesche Werte ist das recht naheliegend.
Wir codieren True
als 1 und False
als 0.
Unser erstes Prädikat, also Funktion mit Booleschem Ausgabewert, ist
isPositive
:
def pred(t): temp = 0 = zero() for i in range(t): temp = 1 = one return tempund somit
Auf ähnliche Weise können wir uns nun lessThan, lessEqual, and, or, not, ifThenElse konstruieren.
Wurzel und Suchen im Allgemeinen
Eine größere Herausforderung stellt die ganzzahlige WurzelWir können auch den Suchraum begrenzen: da
Das Prädikat
def largestIbelowTwithISquareLessEqualX(t,x): temp = 0 = zero(x) for i in range(t): temp = ifThenElse(iSquareLessEqualX(i,x), i, temp) = Comp(ifThenElse, Comp(iSquareLessEqual, p_1, p_2), p_1, p_0) (temp,i,x) return temp
Was geht hier vor? Wenn
def LargestLessThan(upperBound, predicate): def new_function(*x): temp = 0 for i in range(upperBound(*x)): if (predicate(i,*x)): temp = i return temp return new_function
und somit können wir sqrt schreiben als
Paare und Listen
Eine recht stark anmutende Beschränkung primitiv rekursiver Funktionen ist die Tatsache, dass wir in der Schleife nur eine lokale Variable führen dürfen, hier meistenstemp
genannt.
Manche Funktionen scheinen inhärent mindestens zwei zu benötigen.
Betrachten wir den Fall der Fibonacci-Zahlen:
def fibIterative(t): a = 0 b = 1 for i in range(t): c = a+b a = b b = c return aWir führen hier zwei lokale Variable,
b = a+b a = b-aDoch selbst dann hätten wir immer noch zwei lokale Variable. Die Fibonacci-Zahlen rekursiv per
return F(n-1)+F(n-2)
scheint noch weiter weg zu sein
vom Paradigma der primitiven Rekursion; primitive Rekursion verzweigt sich nie.
Dennoch ist es möglich, fib
primitiv rekursiv zu implementieren.
Hauptzutat hierbei ist es, dass wir Paare als neue Datenstruktur
verwenden.
Erinnern Sie sich: die primitive Rekursion stellt uns als Datentyp von Haus aus nur die natürlichen Zahlen zur Verfügung. Alles andere müssen wir nach einem selbst gewählten Schema codieren. Bei Booleschen Werten war es einfach. Wie steht es mit Paaren von natürlichen Zahlen? In Kapitel 2: Unendliche Mengen haben wir die bijektive Funktion
def choose2(t): temp = 0 = zero() for i in range(t): temp = add(temp,i) = Comp(add, p0, p1)und somit
Um die Umkehrfunktionen
Listen können wir implementieren, indem wir Paare verschachtelt zusammenhängen,
z.B.
pair(5,pair(7,9))
11031
pair(7,9)
143
pair(5,143)
11031
Das Problem ist, dass wir das Ende der Liste nicht kennen. Wir lösen das Problem, indem
wir die leere Liste mit
Jetzt klappt alles wunderbar:
push(11, push(7, push(5, 0)))
90537
head(90537)
11
head(tail(90537))
7
head(tail(tail(90537)))
5
Native Implementierung. Die primitive Rekursion ist
ein theoretischer Berechenbarkeitsbegriff für Funktionen
auf natürlichen Zahlen. Es ist definitiv keine ernstzunehmende Programmiersprache.
Ich finde allerdings, dass es hilfreich ist, sie als Programmiersprache zu begreifen
und zu verwenden, rein experimentell. Leider ist sie extrem ineffizient: selbst
Subtraktion hat quadratische Komplexität. Längere Listen wie die gerade werden Sie in
vertretbarer
Zeit nicht behandeln können. Bei meinen eigenen Experimenten mit meinem
Python-Framework bin ich daher dazu übergegangen, dass ich, sobald
ich gezeigt habe, dass eine Funktion stockpile.py
:
def pair(x,y):code>
return int(((x+y+1) * (x+y)) / 2 + x )