Forth

Code-Stücke können hier veröffentlicht werden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jan.b hat geschrieben:die klammern schaden aber auch nicht. und wie beim anderen auch war das nur ein auszug vom Code und die for schleife wird noch wichtig, und das ir die variabel für den Input ist, ist nicht zu verkennen.
Ich würde mal sagen, befasse Dich einmal mit einer Programmiersprache, die auf den Grundlagen aufsetzt, dann verstehst Du python auch: https://sites.google.com/site/win324th/about-win32forth

Und lernst es zu schätzen!

Bei FORTH muss man sich etwas umgewöhnen:

statt: a = 5 + 8 * 3
schreibt man:
5 8 3 * + a !

Und man kann auch schöne Funktionen definieren, etwa:

: + - ;

Das heißt dass bei + ein - ausgeführt werden soll.
Benutzeravatar
pillmuncher
User
Beiträge: 1482
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Alfons Mittelmeyer hat geschrieben:[...] einer Programmiersprache, die auf den Grundlagen aufsetzt, [...]
So eine Programmiersprache gibt es nicht. Jede Programmiersprache, die diesen Titel verdient, ist Turing-vollständig und man kann mit ihr jede andere Programmiersprache implementieren (denn genau das bedeutet "Turing-vollständig"). Es gibt auch kein grundlegendes Programmiermodell, nicht mal eine grundlegende Computerarchitektur, ebenfalls wegen der o.g. Turing-Vollständigkeit. Die Grundlage der Programmierung ist die Programmierung. Und dank des Curry-Howard-Lambek-Isomorphismus wissen wir, dass es keinen grundlegenden Unterschied zwischen Mathematik, Logik und Programmierung gibt. Nur einen perspektivischen.
In specifications, Murphy's Law supersedes Ohm's.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Also Forth hat viel mit einer Liste zu tun, nämlich einem Stack.

Was bedeutet das?

5 8 3 + a !

Das wäre etwa in Python, dabei hatte ich die Variable unterschlagen:

Code: Alles auswählen

a = variable()
lit(5)
lit(3)
lit(8)
mul()
add()
lit(a)
store()
Wobei wir zuvor definiert hatten:

Code: Alles auswählen

stack = []

class variable:
    value = None
    def set(self,value):
        self.value = value
    def get(self):
        return self.value

def lit(value):
    stack.append(value)

def add():
    stack[-1] = stack[-2] + stack.pop()

def mul():
    stack[-1] = stack[-2] * stack.pop()

def store():
    variable = stack.pop()
    variable.set(stack.pop())
Wenn wir dann die Variable ausgeben wollen, schreiben wir:

a @ .

Das ist dann in Python:

Code: Alles auswählen

lit(a)
get()
output()
Wobei wir dann noch definiert haben:

Code: Alles auswählen

def get():
    stack[-1] = stack[-1].get()

def output():
    print(stack.pop())
Wer sich also in FORTH auskennt, kennt sich auch mit Listen aus.

Interessant wären da noch solche FORTH Befehle, wie DUP, SWAP, OVER, ROT, PICK

Vielleicht mal versuchen, diese zu implementieren.

PS: es ist überhaupt kein Problem FORTH nach Python zu übersetzen. Braucht man natürlich nicht, weil es glücklicherweise dort compile und exec bzw. eval gibt

ich will jetzt nicht auf die Definition von FORTH Funktionen in Python eingehen, aber eine Funktion, die ein Quadrat berechnet, ist auch ganz einfach:

: quadrat DUP * ;

Wobei dann DUP ist:

Code: Alles auswählen

def dup():
    stack.append(stack[-1])
Wenn Du jetzt noch SWAP, OVER, ROT und PICK probierst, hast Du es vielleicht kapiert.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Da habe ich noch etwas Wichtiges vergessen: ROLL gibt es auch noch. Und hier sieht man. wie man dup, over und swap mit pick und roll definieren kann:

Code: Alles auswählen

pick ( a0 .. an n -- a0 .. an a0 )
roll ( a0 .. an n -- a1 .. an a0 )
// examples: reimplement primitives
: dup  0 pick ;
: over 1 pick ;
: swap 1 roll ;
: rot  2 roll
Hier sieht man die Definition der Stack Operatoren: http://wiki.laptop.org/go/Forth_stack_operators
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

FORTH ist sehr kurz und bündig und braucht keine lokalen Varablen, wenn man mal das vergleicht:

Code: Alles auswählen

def quadrat(value):
    return value * value


: quadrat dup * ;
Der Wert auf dem Stack wird dupliziert und multipliziert und das war es. Wenn man sich allerdings mit den Werten auf dem Stack verzählt hat, dann hat man Pech gehabt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

pillmuncher hat geschrieben:So eine Programmiersprache gibt es nicht. Jede Programmiersprache, die diesen Titel verdient, ist Turing-vollständig und man kann mit ihr jede andere Programmiersprache implementieren (denn genau das bedeutet "Turing-vollständig").
Es gibt Grundlagen einer Computerarchitektur. Und eine Grundlage ist Speicher. Und dabei auch physikalischer Speicher. Das sind etwa Bytes. Den Speicher eines Computers kann man sich vorstellen wie die Kästchen in einem Rechenheft. Jedes Kästchen ist ein Byte. Natürlich kann man darauf fußend sich nicht nur Bytearrays vorstellen, sondern auch verkette Listen, die dann zu Pythonlisten führen. Aber zuerst sollte man einmal verstanden haben, was man mit solchem physikalischem Speicher tun kann. Nämlich so ziemlich alles. Der Vorstellung sind da keine Grenzen gesetzt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Alfons Mittelmeyer hat geschrieben:
pillmuncher hat geschrieben:So eine Programmiersprache gibt es nicht. Jede Programmiersprache, die diesen Titel verdient, ist Turing-vollständig und man kann mit ihr jede andere Programmiersprache implementieren (denn genau das bedeutet "Turing-vollständig").
Es gibt Grundlagen einer Computerarchitektur. Und eine Grundlage ist Speicher. Und dabei auch physikalischer Speicher. Das sind etwa Bytes. Den Speicher eines Computers kann man sich vorstellen wie die Kästchen in einem Rechenheft. Jedes Kästchen ist ein Byte. Natürlich kann man darauf fußend sich nicht nur Bytearrays vorstellen, sondern auch verkette Listen, die dann zu Pythonlisten führen. Aber zuerst sollte man einmal verstanden haben, was man mit solchem physikalischem Speicher tun kann. Nämlich so ziemlich alles. Der Vorstellung sind da keine Grenzen gesetzt.

Programmiersprachen wie ASSEMBLER und FORTH sowie auch C orientieren sich an solchem Speicher, wobei leider viele Programmierer etwa bei C den Bezug zu diesem Speicher nicht mehr richtig kennen. Genau genommen orientiert sich jede Programmiersprache am vorhandenen Speicher und am Umgang mit demselbigen, wobei der Benutzer aber den Bezug nicht mehr herzustellen weiß. Vielfach wissen viele ja gar nicht mehr, was die Buchstaben des Aphabets für den Computer sind, nämlich ein ASCII Code, also ein Byte (oder auch mehr bei Unicode) im Speicher.
Benutzeravatar
pixewakb
User
Beiträge: 1405
Registriert: Sonntag 24. April 2011, 19:43

Ich habe Respekt vor Leuten, die etwas mehr über Computer wissen, als zum Lösen einfacher Aufgaben mit Python erforderlich sind. Die Sache mit den Bytes, dem Speicher usw. finde ich spannend. Ich merke aber auch an, dass ich bei den Beiträgen etwas den Überblick verloren habe und mir bei den Forth-Beispielen nur schummrig geworden ist.

Wir sollten das eigentliche Ziel des Threads nicht aus dem Auge verlieren: Der Threadstarter hat eine Frage zu Python, hier sollten wir helfen. Eine andere Programmiersprache zu empfehlen, halte ich nicht für sinnvoll, das verwirrt ihn nur. Ich habe vor Python auch kein Forth oder C gelernt. Der Vorteil von Python ist doch eigentlich auch, dass Python eine Menge für den Anwender erledigt...

Im Kern: Grundsatzdiskussionen über die richtige Didaktik sind hier ziemlich Offtopic und können den Threadstarter eher verunsichern und vertreiben, befürchte ich. Python ist einfach, aber er muss in ein Tutorial reinschauen...
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@pixewakb: naja es ging darum, dass sich der User mehr mit der Liste beschäftigen sollte, etwa anfügen, vertauschen, rausholen sowohl normal, wie auch nachrutschend, ersetzen und einfügen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Noch ein Beispiel, was man mit Listen oder Tupeln noch anstellen kann:

Code: Alles auswählen

# stacks =============
stack = []
returnstack = []

# instruction pointer ======

class Ip:
    liste = None
    index = None
    def set(self,liste,index):
        self.liste = liste
        self.index = index

ip = Ip()

# interpreter =============

class Interpreter:
    run = False
    def start(self):
        self.run = True
        while self.run:
            command = ip.liste[ip.index]
            ip.index += 1
            command.execute()

interpreter = Interpreter()

class Abort:
    def execute(self):
        interpreter.run = False

abort = Abort()


# routines =====================

class Secondary:
    def __init__(self,liste):
        self.liste = liste
    def execute(self):
        returnstack.append((ip.liste,ip.index))
        ip.set(self.liste,0)


class Semis:
    def execute(self):
        pointer = returnstack.pop()
        ip.liste = pointer[0]
        ip.index = pointer[1]

semis = Semis()


# primary commands ============

class Lit:
    def execute(self):
        stack.append(ip.liste[ip.index])
        ip.index += 1

lit = Lit()

class Mul:
    def execute(self):
        stack[-1] = stack[-2] * stack.pop()

mul = Mul()    

class Add:
    def execute(self):
        stack[-1] = stack[-2] + stack.pop()

add = Add()

class Output:
    def execute(self):
        print(stack.pop())

output = Output()

class Dup:
    def execute(self):
        stack.append(stack[-1])

dup = Dup()

# secondary definition (routine) ============

quadrat = Secondary((dup,mul,semis))
    

# run code ======================

code = (lit,3,quadrat,lit,5,lit,8,mul,add,output,abort)

ip.set(code,0)
interpreter.start()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sorry, man schreibt das nicht als Klassen sondern als Funktionen:

Code: Alles auswählen

import sys

# stacks =============
stack = []
returnstack = []

# instruction pointer ======

class Ip:
    liste = None
    index = None
    def set(self,liste,index):
        self.liste = liste
        self.index = index

ip = Ip()

# interpreter =============

class Interpreter:
    run = False
    def start(self):
        self.run = True
        while self.run:
            command = ip.liste[ip.index]
            ip.index += 1
            command()

interpreter = Interpreter()

def abort():
    interpreter.run = False

# routines =====================

class Secondary:
    def __init__(self,liste):
        self.liste = liste
    def execute(self):
        returnstack.append((ip.liste,ip.index))
        ip.set(self.liste,0)

def semis():
    pointer = returnstack.pop()
    ip.liste = pointer[0]
    ip.index = pointer[1]


# primary commands ============

def lit():
    stack.append(ip.liste[ip.index])
    ip.index += 1

def mul():
    stack[-1] = stack[-2] * stack.pop()

def add():
    stack[-1] = stack[-2] + stack.pop()

def output():
    sys.stdout.write('{}'.format(stack.pop()))

def emit():
    sys.stdout.write(chr(stack.pop()))

def dup():
    stack.append(stack[-1])

# secondary definition (routine) ============

quadrat = Secondary((dup,mul,semis)).execute
cr = Secondary((lit,'\n',output,semis)).execute

# run code ======================

code = (lit,3,dup,output,lit,' quadrat = ', output, quadrat,output,cr,
        lit,5,dup,output, lit, ' * ', output, lit,8,dup,output,lit, ' = ', output, mul,output,cr,abort)

ip.set(code,0)
interpreter.start()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Damit wir auch noch eine Schleife haben, wie: for i in range(65,96):

Code: Alles auswählen

import sys

# stacks =============
stack = []
returnstack = []
loopstack = []

# instruction pointer ======

class Ip:
    liste = None
    index = None
    def set(self,liste,index):
        self.liste = liste
        self.index = index

ip = Ip()

# interpreter =============

class Interpreter:
    run = False
    def start(self):
        self.run = True
        while self.run:
            command = ip.liste[ip.index]
            ip.index += 1
            command()

interpreter = Interpreter()

def abort():
    interpreter.run = False

# routines =====================

class Secondary:
    def __init__(self,liste):
        self.liste = liste
    def execute(self):
        returnstack.append((ip.liste,ip.index))
        ip.set(self.liste,0)

def semis():
    pointer = returnstack.pop()
    ip.liste = pointer[0]
    ip.index = pointer[1]

# primary commands ============

def lit():
    stack.append(ip.liste[ip.index])
    ip.index += 1

def output():
    sys.stdout.write('{}'.format(stack.pop()))

def emit():
    sys.stdout.write(chr(stack.pop()))

def do():
    loopstack.append(stack.pop(-2))
    loopstack.append(stack.pop())

def i():
    stack.append(loopstack[-1])

def loop():
    loopstack[-1] += 1
    if loopstack[-1] < loopstack[-2]:
        ip.index += ip.liste[ip.index]
    else:
        loopstack.pop()
        loopstack.pop()
        ip.index +=1

# secondary definition (routine) ============

cr = Secondary((lit,'\n',output,semis)).execute

# run code ======================

code = (lit,96,lit,65,do,i,output,lit,' ',output,i,emit,cr,loop,-9,abort)

ip.set(code,0)
interpreter.start()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Und man kann dann auch schönen fein strukturierten Code schreiben:

Code: Alles auswählen

# secondary definition (routine) ============

cr = Secondary((lit,10,emit,semis)).execute
space = Secondary((lit,32,emit,semis)).execute
spaces = Secondary((lit,0,do,space,loop,-2,semis)).execute
star = Secondary((lit,42,emit,semis)).execute
stars = Secondary((lit,0,do,star,loop,-2,semis)).execute
margin = Secondary((cr,lit,30,spaces,semis)).execute
blip = Secondary((margin,star,semis)).execute
bar = Secondary((margin,lit,5,stars,semis)).execute
# run code ======================

code = (bar,blip,bar,blip,blip,cr,abort)
Das wäre allerdings nur er innere Interpreter, der bereits compilierten Code ausführt. Der äußere Interpreter liest aus Source Text und führt diesen aus und kompiliert auch, alles schön hintereinander, ein Wort nach dem anderen, durch Leerzeichen getrennt, es sei denn, es wäre ein String oder ein Kommentar.
BlackJack

@Alfons Mittelmeyer: Verkettete Listen haben jetzt nicht sooo viel mit dem `list`-Datentyp in Python zu tun. Klar könnte man eine Python-Implementierung schreiben die dafür intern eine verkettete Liste verwendet, aber die würde wohl kaum jemand benutzen wollen, denn das Laufzeitverhalten bezüglich `list` wäre da dann deutlich schlechter als das bei den vorhandenen Python-Implementierungen.

Es orientiert sich auch nicht jede Programmiersprache an dem Speichermodell. Implementierungen müssen die Sprache natürlich auf die vorhandene Hardware abbilden, aber die Programmiersprache selbst kann da nahezu beliebig weit von abstrahieren. Und Hochsprachen tun das auch, und auch absichtlich, damit der Programmierer seine Problemlösungen eher in der Problemdomäne formulieren kann, und nicht auf die Ebene der Hardware herunter gehen muss.

Bei modernen Hochsprachen wie Python, Ruby, Java, C#, … funktioniert das Gedankenmodell mit den Kästchen auf denen Namen stehen und in die man Werte hinein tut schon nicht mehr oder nicht mehr uneingeschränkt, das man bei Sprachen wie BASIC, C, Pascal, Forth, … noch lernt, und bei Sprachen wie Haskell wird es dann schon richtig schwer am Quelltext abzulesen wie der Code mit dem Speicher in Verbindung zu bringen ist.

Das C-Programmierer keinen Bezug mehr zum Speichermodell haben, halte ich für unwahrscheinlich, denn da kommt man dann nicht weit, weil kaum ein Programm ohne Zeiger und manuelle Speicherverwaltung auskommt. Zumal man C heute meistens lernt weil man hardwarenah programmieren will/muss, also beispielsweise für Programme wo das mit dem Speicher wegen Hardwareregistern die in den Adressraum eingeblendet werden wichtig ist.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

pixewakb hat geschrieben:Der Threadstarter hat eine Frage zu Python, hier sollten wir helfen.
Der Threadstarter hat meiner bescheidenen Meinung nach einige typischen Eigenschaften eines Forentrolls, verfügt möglicherweise über genügend Fachwissen, um alle seine Fragen selbst beantworten zu können und stellt diese nur dümmlich, um damit provozieren zu können. Kommt eine Reaktion, wird er oft mit einer knappen Antwort und einem Vollzitat darauf eingehen, um so effizient wie möglich weiter provozieren zu können und andere möglichst auf die Palme zu treiben. Jeder Versuch belehrend zu wirken, wird fehlschlagen, da es Forentrolle nicht um Einsicht geht, sondern ums Provozieren. Ist zumindest meine Einschätzung, mehr nicht, ob es so ist, wird sich mit den nächsten 3 Vollzitaten und den nächsten 3 bis 5 neu eröffneten Threads herausstellen.

https://de.wikipedia.org/wiki/Troll_(Netzkultur)

Womit er vermutlich nicht gerechnet hat, dass sich etwa aus jeder dritten Fragestellung noch ein ernsthafter Diskussionsfaden entwickelt, was somit nicht schädlich fürs Forum ist, so lange er sich nicht in von anderen gestarteten Threads trollt, um diese zu stören.
BlackJack

Dann hätte ich die beiden vielleicht doch in einem Thema belassen sollen. :twisted:
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Entweder Troll oder er ist noch sehr jung, dass er so komisch schreibt. Vielleicht ist er auch das Kind von Alfons Mittelmeyer. :o
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Anmerkungen zum Code:

Wenn ich ein Interpreter-Objekt habe, würde ich nicht davon ausgehen, dass daneben noch eine Menge globaler Variablen existieren, und es nur einen Interpreter geben kann. Da ist nämlich die Interpreter-Klasse gar keine, sondern eine einfache Funktion. Klassenattribute sind kein Ersatz für Instanzattribute, die Klassenattribute bei IP und Interpreter sollten eigentlich Instanzattribute sein und in __init__ gesetzt werden.

Das ganze mal objektorientiert:

Code: Alles auswählen

import sys
 
class Interpreter:
    def __init__(self, code, functions=None):
        self.code = code
        self.functions = functions
        self.ip = 0
        self.running = False
        self.stack = []
        self.returnstack = []
        self.loopstack = []
    
    def next_code(self):
        self.ip += 1
        return self.code[self.ip - 1]
            
    def run(self):
        self.running = True
        while self.running:
            try:
                command = self.next_code()
            except IndexError:
                if not self.returnstack:
                    break
                self.code, self.ip = self.returnstack.pop()
            else:
                getattr(self, 'cmd_' + command)()
 
    def cmd_abort(self):
        self.running = False
 
    def cmd_semis(self):
        self.code, self.ip = self.returnstack.pop()
 
    def cmd_lit(self):
        self.stack.append(self.next_code())
 
    def cmd_output(self):
        sys.stdout.write('{}'.format(self.stack.pop()))
 
    def cmd_emit(self):
        sys.stdout.write(chr(self.stack.pop()))
 
    def cmd_do(self):
        self.loopstack.append([self.stack.pop(), self.stack.pop(), self.ip])
 
    def cmd_i(self):
        self.stack.append(self.loopstack[-1][0])
 
    def cmd_loop(self):
        self.loopstack[-1][0] += 1
        cur, end, ip = self.loopstack[-1]
        if cur < end:
            self.ip = ip
        else:
            self.loopstack.pop()

    def cmd_call(self):
        function = self.functions[self.next_code()]
        self.returnstack.append((self.code, self.ip))
        self.code = function
        self.ip = 0


code_nl = ('lit','\n','output','semis')
code = ('lit',96,'lit',65,'do','i','output','lit',' ','output','i','emit','call', 'nl', 'loop','abort')
interpreter = Interpreter(code, {'nl': code_nl})
interpreter.run()
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

    def cmd_output(self):
        sys.stdout.write('{}'.format(self.stack.pop()))
Warum nicht einfach print(self.stack.pop(), end='')?
BlackJack

@snafu: Wegen dem Zeilenumbruch würde ich sagen.
Antworten