Seite 1 von 2

putchar (B sprache)

Verfasst: Donnerstag 7. Mai 2009, 01:58
von stuhlbein
Hallo :)

Ich versuche grad die "putchar" funktion aus der B Sprache nachzubauen.

Normalerweise werden Ascii escapes in B durch ein vorangehendes '*' deklariert.

Meine frage ist nun, wie man das am elegantesten in Python umsetzt.

Das Original aus B würde so aussehen:

Code: Alles auswählen

 main( ) {
 	putchar('hell');
 	putchar('o, w');
 	putchar('orld');
 	putchar('*n' );
 }
Die Ausgabe wäre "hello, world\012".

Mein bisheriges Ergebnis:

Code: Alles auswählen

import sys

def putchar(chars, out=sys.stdout, enc='utf-8'):
    special_chars = {'*n': '\012',
                     '*t': '\011',
                     '*r': '\015',
                     '*b': '\010',
                     '*a': '\007',
                     '*f': '\014',
                     '*0': '\000'}
    nchars = ''
    for char in chars: # kann natuerlich niemals funktionieren
        spc = special_chars.get(str(char))
        if spc:
            char.replace(char, spc)
        nchars += char
    out.write(unicode(nchars, enc))

# beispiel aufruf:

putchar('hell')
putchar('o, w')
putchar('orld')
putchar('*n' ) # das '*n' sollte eigentlich durch \012 ersetzt werden
Natürlich kann das in der form nie funktionieren, da 'for char in chars: [...]' ja jedes zeichen *einzeln* durchläuft.
Wie splitte ich den string nun also in zweier gruppen? Oder gibt es bessere methoden das zu lösen?


PS: falls es jemand interessiert warum ich mir das antue: just 4 fun! (kein scherz, ich mach das wirklich nur zum spass. Und um nebenbei ein wenig Python zu lernen. :))

Verfasst: Donnerstag 7. Mai 2009, 06:41
von martin101986
Hallo,

du könntest schauen ob ein Sonderzeichen in deinem String ist und dieses dann ersetzen.

Code: Alles auswählen

text = 'Test*n'

special_chars = {'*n': '\012',
                     '*t': '\011',
                     '*r': '\015',
                     '*b': '\010',
                     '*a': '\007',
                     '*f': '\014',
                     '*0': '\000'}

for key in special_chars.keys():
    if key in text:
        text = text.replace(key, special_chars[key])

Grüße Martin

Verfasst: Donnerstag 7. Mai 2009, 07:00
von BlackJack
@Bitfish: Wieso an Zweigergruppen? Oder müssen die Sternchen immer an geraden Indexen beginnen?

Ich würde das mit einem regulären Ausdruck machen. Zu `special_chars` kommt noch die Übersetzung '**' -> '*' dazu, oder kann man den '*' in B gar nicht ausgeben, oder nur, wenn danach keines der bekannten Zeichen folgt?

Code: Alles auswählen

In [14]: special_chars
Out[14]:
{'**': '*',
 '*0': '\x00',
 '*a': '\x07',
 '*b': '\x08',
 '*f': '\x0c',
 '*n': '\n',
 '*r': '\r',
 '*t': '\t'}

In [15]: s = 'Hallo*n' + 'Keine Ersetzung: *x*n' + 'Einfacher Asterisk: **'

In [16]: print re.sub(r'\*[*ntrbaf0]', lambda m: special_chars[m.group(0)], s)
Hallo
Keine Ersetzung: *x
Einfacher Asterisk: *
Eine Frage bleibt noch: Werden bei B die Zeichen beim Übersetzen ausgetauscht, wie C und Python das machen, oder ist es wirklich die Funktion `putchar()`, die das tut?

@martin101986: Ziemlich ineffizient.

Verfasst: Donnerstag 7. Mai 2009, 07:08
von stuhlbein
martin101986 hat geschrieben:Hallo,

du könntest schauen ob ein Sonderzeichen in deinem String ist und dieses dann ersetzen.
Klappt tatsächlich... Da war ich mit meiner lösung ziemlich auf dem Holzweg ;)

Wär aber dennoch interessant zu erfahren, wie man Strings in zweier (oder dreier, vierer, etc..) gruppen aufsplittet, sofern sowas überhaupt geht ;)

Verfasst: Donnerstag 7. Mai 2009, 07:24
von BlackJack
@Bitfish: Wenn es '**' -> '*' wirklich gibt, geht das mit dem suchen und ersetzen nicht, wenn man nicht auf die Reihenfolge achtet. '**n' kann dann nämlich über '*n' zu '\n' werden, wenn man die '**' nicht erst ganz zum Schluss ersetzt.

Ansonsten ist das wie gesagt ziemlich ineffizient, weil die Zeichenkette für jeden `special_char`-Eintrag komplett durchlaufen wird. Wenn man überflüssigerweise vorher prüft ob die Zeichen überhaupt enthalten sind, wird's noch ineffizienter.

Ein Ansatz da Paarweise durchzugehen:

Code: Alles auswählen

In [18]: s = 'hallo'

In [19]: zip(s, s[1:])
Out[19]: [('h', 'a'), ('a', 'l'), ('l', 'l'), ('l', 'o')]
Oder man geht einzeln durch und merkt sich immer das letzte Zeichen.

Aufpassen sollte man auf jeden Fall bei den Sonderfällen, also zum Beispiel die leere Zeichenkette, oder eine in der nur ein einzelnes '*' steht.

Verfasst: Donnerstag 7. Mai 2009, 07:33
von stuhlbein
BlackJack hat geschrieben:@Bitfish: Wieso an Zweigergruppen? Oder müssen die Sternchen immer an geraden Indexen beginnen?
Naja, Ich dachte es wäre einfacher, den string in zweiergruppen zu zerlegen, und in einer for-schleife gegen das special_chars dict zu prüfen.
Ich würde das mit einem regulären Ausdruck machen.
Warum?

Mit der von User:martin101986 genannten lösung kann ich vollkommen problemlos folgenden Code korrekt Interpretieren:

>>> putchar("*tFoo*0bar!*r*n*r*t*tNaechste zeile! *nZwei Sterne: ***n!")
Zu `special_chars` kommt noch die Übersetzung '**' -> '*' dazu, oder kann man den '*' in B gar nicht ausgeben, oder nur, wenn danach keines der bekannten Zeichen folgt?
Doch, in B's putchar() konnte man auch ganz normal ein einzelnes '*' wiedergeben, wenn darauf kein "interpretiertes" zeichen kam.
Das kann man relativ gut mit dem heute gebräuchlichen Backslash vergleichen:

"Foo\bar!" => das "\b" wird als backspace interpretiert
"Foo\\bar" => das "" und "\b" werden ausgegeben, der backspace wird nicht interpretiert

.. Demnach brauch man auch kein '**' in die tabelle einfügen, da das "* + zeichen" nur dann interpretiert wird, wenn auf das '*' auch ein zugeordnetes zeichen folgt! (ich hoffe es war klar worauf ich hinaus wollte ... :D)

Code: Alles auswählen

In [14]: special_chars
Out[14]:
{'**': '*',
 '*0': '\x00',
 '*a': '\x07',
 '*b': '\x08',
 '*f': '\x0c',
 '*n': '\n',
 '*r': '\r',
 '*t': '\t'}

In [15]: s = 'Hallo*n' + 'Keine Ersetzung: *x*n' + 'Einfacher Asterisk: **'

In [16]: print re.sub(r'\*[*ntrbaf0]', lambda m: special_chars[m.group(0)], s)
Hallo
Keine Ersetzung: *x
Einfacher Asterisk: *
Das ginge natürlich auch, das problem hierbei ist, wie bei allen arbeiten mit regulären ausdrücken, dass diese bei grossen dateien sehr lange dauern können (auch wenn das für die gezeigten beispiele irrelevant ist.)

Zugegebenermassen sieht deine lösung sauberer aus. Aber ich denke nicht, dass sie auch zwingend besser sein muss ;)
Eine Frage bleibt noch: Werden bei B die Zeichen beim Übersetzen ausgetauscht, wie C und Python das machen, oder ist es wirklich die Funktion `putchar()`, die das tut?
Kann ich dir leider nicht beantworten, da B eine sehr alte programmiersprache ist, und es kaum verfügbare quelltexte gibt.
Ich vermute, dass `putchar()` diese zeichen ersetzt, da B nur sehr wenige eingebaute funktionen besitzt.
Aber das wäre echt interessant zu erfahren ... ;)
@martin101986: Ziemlich ineffizient.
Kann ich nicht zustimmen. Folgender code funktioniert hier prima:

Code: Alles auswählen

import sys

def putchar(chars, out=sys.stdout, enc='utf-8'):
    special_chars = {'*n': '\012',
                     '*t': '\011',
                     '*r': '\015',
                     '*b': '\010',
                     '*a': '\007',
                     '*f': '\014',
                     '*0': '\000'}
    nchars = ''
    for key in special_chars.keys():
        if key in chars:
            chars = chars.replace(key, special_chars[key])
    out.write(unicode(chars, enc))

putchar("*tFoo*0bar!*r*n*r*t*tNaechste zeile! *nZwei Sterne: ***n!")

Verfasst: Donnerstag 7. Mai 2009, 08:29
von birkenfeld
Bitfish hat geschrieben: Das ginge natürlich auch, das problem hierbei ist, wie bei allen arbeiten mit regulären ausdrücken, dass diese bei grossen dateien sehr lange dauern können (auch wenn das für die gezeigten beispiele irrelevant ist.)
Soso, reguläre Ausdrücke "dauern lange bei großen Dateien". Wo hast du denn das so allgemein gelesen? Und du meinst, die Lösung mit str.replace() ist besser? Mehr dazu siehe unten.
Zugegebenermassen sieht deine lösung sauberer aus. Aber ich denke nicht, dass sie auch zwingend besser sein muss ;)
Ist sie aber. Und bei BlackJack kann man sich da eigentlich sicher sein.
@martin101986: Ziemlich ineffizient.
Kann ich nicht zustimmen. Folgender code funktioniert hier prima:
Seit wann heißt "ineffizient" "funktioniert nicht"?

Martins Code ist deshalb ineffizient, weil der String für jedes Escape einmal durchlaufen wird, um ein Vorkommen zu finden (das ist ok), aber dann *noch ein zweites mal*, um dieses auch zu ersetzen. Sprich, das "find" vor dem "replace" ist unnütz.

Dann bleibt noch die Tatsache, dass der String für jedes Escape durchlaufen wird. Genau das spart man sich mit der Regex.

Verfasst: Donnerstag 7. Mai 2009, 08:34
von birkenfeld
Bitfish hat geschrieben:
Zu `special_chars` kommt noch die Übersetzung '**' -> '*' dazu, oder kann man den '*' in B gar nicht ausgeben, oder nur, wenn danach keines der bekannten Zeichen folgt?
Doch, in B's putchar() konnte man auch ganz normal ein einzelnes '*' wiedergeben, wenn darauf kein "interpretiertes" zeichen kam.
Das kann man relativ gut mit dem heute gebräuchlichen Backslash vergleichen:

"Foo\bar!" => das "\b" wird als backspace interpretiert
"Foo\\bar" => das "" und "\b" werden ausgegeben, der backspace wird nicht interpretiert
Nein, gerade dieses Verhalten ist eben nicht vergleichbar mit dem Verhalten der Sternchen, du hättest als Beispiel z.B. "Foo\kbar" anbringen müssen.
Eine Frage bleibt noch: Werden bei B die Zeichen beim Übersetzen ausgetauscht, wie C und Python das machen, oder ist es wirklich die Funktion `putchar()`, die das tut?
Kann ich dir leider nicht beantworten, da B eine sehr alte programmiersprache ist, und es kaum verfügbare quelltexte gibt.
Ich vermute, dass `putchar()` diese zeichen ersetzt, da B nur sehr wenige eingebaute funktionen besitzt.
Aber das wäre echt interessant zu erfahren ... ;)
Trotz des Alters findet man massenweise Material bei Google. Zum Beispiel ein Tutorial (http://cm.bell-labs.com/cm/cs/who/dmr/btut.html), woraus klar wird, dass das Sternchen ein Feature der Literalsyntax ist (da es z.B. in ``if (c == '*n')`` verwendet wird).

Verfasst: Donnerstag 7. Mai 2009, 09:01
von stuhlbein
birkenfeld hat geschrieben:
Martins Code ist deshalb ineffizient, weil der String für jedes Escape einmal durchlaufen wird, um ein Vorkommen zu finden (das ist ok), aber dann *noch ein zweites mal*, um dieses auch zu ersetzen. Sprich, das "find" vor dem "replace" ist unnütz.

Dann bleibt noch die Tatsache, dass der String für jedes Escape durchlaufen wird. Genau das spart man sich mit der Regex.
Verstehe :) In dem fall ist ein regulärer ausdruck natürlich besser. :D
Trotz des Alters findet man massenweise Material bei Google. Zum Beispiel ein Tutorial (http://cm.bell-labs.com/cm/cs/who/dmr/btut.html), woraus klar wird, dass das Sternchen ein Feature der Literalsyntax ist (da es z.B. in ``if (c == '*n')`` verwendet wird).
http://cm.bell-labs.com/cm/cs/who/dmr/btut.html hat geschrieben: The sequence '*n' is B Jargon for "newline character", which, when printed, skips the terminal to the beginning of the next line.
Steht auf deiner verlinkten seite ... :roll:

PS: Ich hab die funktion num um Blackjack's lösung umgeändert, da mir diese auch logischer erscheint. Danke!

Verfasst: Donnerstag 7. Mai 2009, 09:03
von lunar
Bitfish hat geschrieben:
Trotz des Alters findet man massenweise Material bei Google. Zum Beispiel ein Tutorial (http://cm.bell-labs.com/cm/cs/who/dmr/btut.html), woraus klar wird, dass das Sternchen ein Feature der Literalsyntax ist (da es z.B. in ``if (c == '*n')`` verwendet wird).
http://cm.bell-labs.com/cm/cs/who/dmr/btut.html hat geschrieben: The sequence '*n' is B Jargon for "newline character", which, when printed, skips the terminal to the beginning of the next line.
Steht auf deiner verlinkten seite ... :roll:
Ja und? Das widerspricht birkenfelds Aussage nicht.

Verfasst: Donnerstag 7. Mai 2009, 09:20
von birkenfeld
http://cm.bell-labs.com/cm/cs/who/dmr/btut.html hat geschrieben: The sequence '*n' is B Jargon for "newline character", which, when printed, skips the terminal to the beginning of the next line.
Steht auf deiner verlinkten seite ... :roll:
Naja, das "when printed" bezieht sich wohl auf das physikalische Terminal.
Im "Appendix B" findet sich jedenfalls Klarheit:
"""
Escape sequences are used in character constants and strings to obtain characters which for one reason or another are hard to represent directly.
"""

Verfasst: Donnerstag 7. Mai 2009, 09:51
von sma
Ich würde das ja so machen:

Code: Alles auswählen

putchar_esc = False
putchar_specials = {'n': '\n'}

def putchar(s, out=sys.stdout):
    for c in s:
        global putchar_esc
        if putchar_esc:
            out.write(putchar_specials.get(c, c))
            putchar_esc = False
        elif c == '*':
            putchar_esc = True
        else:
            out.write(c)
Damit erwische ich auch den Fall `putchar("*"); putchar("n")` falls das ebenfalls einen Zeilenumbruch ergeben soll. Falls das unnötig ist, kann man `putchar_esc` zu einer lokalen Variable machen.

Stefan

Verfasst: Donnerstag 7. Mai 2009, 09:58
von birkenfeld
Das hat zwei Probleme: Erstens, '*n' ist wirklich ein Escape, also funktioniert es nicht in zwei Aufrufen, und zweitens, '*k' (unbekanntes Escape) soll wieder '*k' ergeben.

Verfasst: Donnerstag 7. Mai 2009, 10:11
von sma
Hätte vielleicht vorher in die Sprachspezifikation schauen sollen. Ich fand nur komisch, dass niemand den IMHO einfachsten Weg vorschlug und stattdessen jeder mit Suchen-und-Ersetzen hantierte. Aus der ein bisschen zu knappen Sprachspezifikation rate ich, dass "*n" bereits vom Scanner in ein LF umgewandelt wird und nie in putchar ankommt. Ob ein *Q zu *Q oder Q wird (was ja bei unbekannten \-Escapes in C passieren würde) kann ich der Sprachspezifikation nicht entnehmen.

Entscheidend ist aber - egal ob's nun im Scanner oder der putchar-Funktion passiert - das eine primitive "state machine" einfacher ist als den kompletten Text jeweils mit verschiedenenen replace-Aufrufen zu durchsuchen.

Code: Alles auswählen

def putchar(s, out=sys.stdout):
    esc = False
    for c in s:
        if esc:
            if c in specials: out.write(specials[c])
            else: out.write("*"); out.write(c)
            esc = False
        elif c == '*': esc = True
        else: out.write(c)
Stefan

Verfasst: Donnerstag 7. Mai 2009, 10:40
von stuhlbein
sma hat geschrieben: Aus der ein bisschen zu knappen Sprachspezifikation rate ich, dass "*n" bereits vom Scanner in ein LF umgewandelt wird und nie in putchar ankommt. Ob ein *Q zu *Q oder Q wird (was ja bei unbekannten \-Escapes in C passieren würde) kann ich der Sprachspezifikation nicht entnehmen.
Vermutlich. Ohne quelltext eines B compilers könnte man das eh nur erraten.
Entscheidend ist aber - egal ob's nun im Scanner oder der putchar-Funktion passiert - das eine primitive "state machine" einfacher ist als den kompletten Text jeweils mit verschiedenenen replace-Aufrufen zu durchsuchen.

Code: Alles auswählen

def putchar(s, out=sys.stdout):
    esc = False
    for c in s:
        if esc:
            if c in specials: out.write(specials[c])
            else: out.write("*"); out.write(c)
            esc = False
        elif c == '*': esc = True
        else: out.write(c)
Stefan
Ich versteh nicht, inwiefern deine Funktion irgendwie besser sein soll - Bei mir funktioniert sie nichtmal... :K

*edit*: Ich hab einen quellofenen BCPL Compiler finden können. Allerdings ist BCPL der vorgänger von B, und auch entsprechend ausgerüstet ... ;) Für interessierte aber dennoch sicher einen blick wert: http://www.cl.cam.ac.uk/~mr10/BCPL.html

ich glaube ich spar mir die frage, warum manche beiträge mit einer gradezu flammenden verbittertheit (bzw, unfreundlichkeit) beantwortet werden... ich persönlich fühl mich hier manchmal, als müsste ich mich von anfang an für meine unwürdigen programmier ergüsse entschuldigen ... es kann ja nunmal nicht jeder als perfekter, studierter programmierer geboren worden sein *augenroll*

Verfasst: Donnerstag 7. Mai 2009, 13:49
von birkenfeld
Bitfish hat geschrieben:ich glaube ich spar mir die frage, warum manche beiträge mit einer gradezu flammenden verbittertheit (bzw, unfreundlichkeit) beantwortet werden... ich persönlich fühl mich hier manchmal, als müsste ich mich von anfang an für meine unwürdigen programmier ergüsse entschuldigen ... es kann ja nunmal nicht jeder als perfekter, studierter programmierer geboren worden sein *augenroll*
Das verlangt ja auch keiner... Wenn du Fragen hast, werden sie sicher nicht unfreundlich beantwortet. Wenn du allerdings die Aussagen der Experten nicht glaubst, musst du dich darauf einstellen dass sie sie verteidigen...

Übrigens, viele Posts klingen nur deshalb "freundlich", weil jeder Satz mit einem Smiley abgeschlossen wird. Das halte ich aber auch nicht für eine sinnvolle Angewohnheit.

Verfasst: Donnerstag 7. Mai 2009, 14:48
von Leonidas
birkenfeld hat geschrieben:Übrigens, viele Posts klingen nur deshalb "freundlich", weil jeder Satz mit einem Smiley abgeschlossen wird. Das halte ich aber auch nicht für eine sinnvolle Angewohnheit.
Ich finde die Smileyanhängerei bei unfreundlichen Posts um sie 'freundlich' zu machen auch für Bedenklich, jedoch ist es tatsache das gerade im Dialog und als solcher ist so eine Forendiskussion am ehesten einzuschätzen, einige weitere Informationen wie Ton, Mimik und Gestik übertragen werden, so dass man besser weiß wie die Aussage einzuschätzen ist. Das hat man in der reinen Textform eher nicht, daher benutzt man solche Krücken.

Verfasst: Donnerstag 7. Mai 2009, 15:07
von numerix
Bitfish hat geschrieben:ich glaube ich spar mir die frage, warum manche beiträge mit einer gradezu flammenden verbittertheit (bzw, unfreundlichkeit) beantwortet werden... ich persönlich fühl mich hier manchmal, als müsste ich mich von anfang an für meine unwürdigen programmier ergüsse entschuldigen ... es kann ja nunmal nicht jeder als perfekter, studierter programmierer geboren worden sein
... und manchmal wird man auch Jahre nach der Geburt und überhaupt zu Lebzeiten überhaupt keiner.

Eine nicht zu unterschätzende Eigenschaft ist aber sicherlich, wenn man seine eigenen Programmierfähigkeiten und Kenntnisse einzuschätzen und einzuordnen weiß; weiß, was man kann und was man nicht kann und sich dann von denen, die das können, was man (noch) nicht (oder vielleicht auch nie) kann, sagen zu lassen, wie es richtig oder besser geht.

Verfasst: Donnerstag 7. Mai 2009, 15:59
von audax
Ihr seid alle doof und könnt nichts! :)

Verfasst: Donnerstag 7. Mai 2009, 16:30
von numerix
audax hat geschrieben:Ihr seid alle doof und könnt nichts! :)
Ja, genauso hatte ich das gemeint. Eine gesunde Selbsteinschätzung gepaart mit einer annähernd objektiven Einschätzung der anderen ist Gold wert!